Merge remote-tracking branch 'origin/master' into sdl_platform
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 11,
|
||||
"Revision": 0,
|
||||
"Build": 6804
|
||||
"Build": 6805
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -281,6 +281,13 @@ namespace FlaxEditor.Content
|
||||
|
||||
private void CacheData()
|
||||
{
|
||||
if (!_asset)
|
||||
{
|
||||
_parameters = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_methods = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_attributes = Utils.GetEmptyArray<Attribute>();
|
||||
return;
|
||||
}
|
||||
if (_parameters != null)
|
||||
return;
|
||||
if (_asset.WaitForLoaded())
|
||||
@@ -344,13 +351,13 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Path.GetFileNameWithoutExtension(_asset.Path);
|
||||
public string Name => _asset ? Path.GetFileNameWithoutExtension(_asset.Path) : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Namespace => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TypeName => JsonSerializer.GetStringID(_asset.ID);
|
||||
public string TypeName => _asset ? JsonSerializer.GetStringID(_asset.ID) : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic => true;
|
||||
|
||||
@@ -1368,6 +1368,9 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
{
|
||||
typeName = e.TypeName;
|
||||
}
|
||||
if (e.Count == 1)
|
||||
LOG(Info, "{0}: 1 asset of total size {1}", typeName, Utilities::BytesToText(e.ContentSize));
|
||||
else
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
}
|
||||
LOG(Info, "");
|
||||
|
||||
@@ -897,9 +897,11 @@ namespace FlaxEditor.Modules
|
||||
|
||||
if (type.IsAssignableTo(typeof(AssetEditorWindow)))
|
||||
{
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) });
|
||||
var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID);
|
||||
var assetType = assetItem.GetType();
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType });
|
||||
var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
|
||||
|
||||
win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
|
||||
if (winData.DockState == DockState.Float)
|
||||
{
|
||||
|
||||
@@ -406,6 +406,8 @@ namespace FlaxEngine.Utilities
|
||||
{
|
||||
if (type == ScriptType.Null)
|
||||
return null;
|
||||
if (type.BaseType == null)
|
||||
return type.Type;
|
||||
while (type.Type == null)
|
||||
type = type.BaseType;
|
||||
return type.Type;
|
||||
|
||||
@@ -400,7 +400,7 @@ namespace FlaxEditor.Surface
|
||||
return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
|
||||
}
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
return managedType != null && !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptFunctionType(ScriptType scriptType)
|
||||
@@ -408,7 +408,7 @@ namespace FlaxEditor.Surface
|
||||
if (scriptType.IsGenericType || scriptType.IsStatic || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
return false;
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
return managedType != null && !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static string GetVisualScriptTypeDescription(ScriptType type)
|
||||
|
||||
@@ -2441,10 +2441,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
if (bucket.LoopsLeft == 0)
|
||||
{
|
||||
// End playing animation
|
||||
// End playing animation and reset bucket params
|
||||
value = tryGetValue(node->GetBox(1), Value::Null);
|
||||
bucket.Index = -1;
|
||||
slot.Animation = nullptr;
|
||||
bucket.TimePosition = 0.0f;
|
||||
bucket.BlendInPosition = 0.0f;
|
||||
bucket.BlendOutPosition = 0.0f;
|
||||
bucket.LoopsDone = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2553,9 +2557,15 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va
|
||||
// Function Input
|
||||
case 1:
|
||||
{
|
||||
// Skip when graph is too small (eg. preview) and fallback with default value from the function graph
|
||||
if (context.GraphStack.Count() < 2)
|
||||
{
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the function call
|
||||
AnimGraphNode* functionCallNode = nullptr;
|
||||
ASSERT(context.GraphStack.Count() >= 2);
|
||||
Graph* graph;
|
||||
for (int32 i = context.CallStack.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -658,7 +658,10 @@ public:
|
||||
--_count;
|
||||
T* data = _allocation.Get();
|
||||
if (index < _count)
|
||||
Memory::MoveAssignItems(data + index, data + (index + 1), _count - index);
|
||||
{
|
||||
for (int32 i = index; i < _count; i++)
|
||||
data[i] = MoveTemp(data[i + 1]);
|
||||
}
|
||||
Memory::DestructItems(data + _count, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -409,27 +409,36 @@ protected:
|
||||
else
|
||||
{
|
||||
// Rebuild entire table completely
|
||||
const int32 elementsCount = _elementsCount;
|
||||
const int32 oldSize = _size;
|
||||
AllocationData oldAllocation;
|
||||
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, _size, _size);
|
||||
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, oldSize, oldSize);
|
||||
_allocation.Allocate(_size);
|
||||
BucketType* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
data[i]._state = HashSetBucketState::Empty;
|
||||
BucketType* oldData = oldAllocation.Get();
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
{
|
||||
BucketType& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.GetKey(), pos);
|
||||
if (pos.FreeSlotIndex == -1)
|
||||
{
|
||||
// Grow and retry to handle pathological cases (eg. heavy collisions)
|
||||
EnsureCapacity(_size + 1, true);
|
||||
FindPosition(oldBucket.GetKey(), pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
}
|
||||
BucketType& bucket = _allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket = MoveTemp(oldBucket);
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
oldData[i].Free();
|
||||
_elementsCount = elementsCount;
|
||||
}
|
||||
_deletedCount = 0;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ namespace AllocationUtils
|
||||
capacity |= capacity >> 8;
|
||||
capacity |= capacity >> 16;
|
||||
uint64 capacity64 = (uint64)(capacity + 1) * 2;
|
||||
if (capacity64 > MAX_int32)
|
||||
capacity64 = MAX_int32;
|
||||
return (int32)capacity64;
|
||||
return capacity64 >= MAX_int32 ? MAX_int32 : (int32)capacity64 / 2;
|
||||
}
|
||||
|
||||
// Aligns the input value to the next power of 2 to be used as bigger memory allocation block.
|
||||
|
||||
@@ -67,7 +67,7 @@ void GPUContext::FrameBegin()
|
||||
|
||||
void GPUContext::FrameEnd()
|
||||
{
|
||||
ClearState();
|
||||
ResetState();
|
||||
FlushState();
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ public:
|
||||
/// [Deprecated in v1.10]
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if depth buffer is binded; otherwise, <c>false</c>.</returns>
|
||||
DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in ")
|
||||
DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in future")
|
||||
virtual bool IsDepthBufferBinded() = 0;
|
||||
|
||||
public:
|
||||
@@ -617,8 +617,17 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// Clears the context state.
|
||||
/// [Deprecated in v1.12]
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void ClearState() = 0;
|
||||
API_FUNCTION() DEPRECATED("Use ResetState instead") void ClearState()
|
||||
{
|
||||
ResetState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the context state.
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void ResetState() = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the internal cached context state with a command buffer.
|
||||
|
||||
@@ -201,6 +201,7 @@ bool DeferredMaterialShader::Load()
|
||||
psDesc.DepthWriteEnable = true;
|
||||
psDesc.DepthEnable = true;
|
||||
psDesc.DepthFunc = ComparisonFunc::Less;
|
||||
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::None;
|
||||
psDesc.HS = nullptr;
|
||||
psDesc.DS = nullptr;
|
||||
GPUShaderProgramVS* instancedDepthPassVS;
|
||||
|
||||
@@ -195,5 +195,10 @@ bool ForwardMaterialShader::Load()
|
||||
psDesc.VS = _shader->GetVS("VS_Skinned");
|
||||
_cache.DepthSkinned.Init(psDesc);
|
||||
|
||||
#if PLATFORM_PS5
|
||||
// Fix shader binding issues on forward shading materials on PS5
|
||||
_drawModes = DrawPass::None;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -264,5 +264,10 @@ bool ParticleMaterialShader::Load()
|
||||
// Lazy initialization
|
||||
_cacheVolumetricFog.Desc.PS = nullptr;
|
||||
|
||||
#if PLATFORM_PS5
|
||||
// Fix shader binding issues on forward shading materials on PS5
|
||||
_drawModes = DrawPass::None;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,8 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
|
||||
|
||||
PixelFormat RenderBuffers::GetOutputFormat() const
|
||||
{
|
||||
return _useAlpha ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float;
|
||||
// TODO: fix incorrect alpha leaking into reflections on PS5 with R11G11B10_Float
|
||||
return _useAlpha || PLATFORM_PS5 ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float;
|
||||
}
|
||||
|
||||
bool RenderBuffers::GetUseAlpha() const
|
||||
|
||||
@@ -216,20 +216,21 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span<GPUVertexLayout*>& layouts)
|
||||
return result;
|
||||
}
|
||||
|
||||
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride)
|
||||
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride, bool referenceOrder)
|
||||
{
|
||||
GPUVertexLayout* result = base ? base : reference;
|
||||
if (base && reference && base != reference)
|
||||
{
|
||||
bool elementsModified = false;
|
||||
Elements newElements = base->GetElements();
|
||||
const Elements& refElements = reference->GetElements();
|
||||
if (removeUnused)
|
||||
{
|
||||
for (int32 i = newElements.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
bool missing = true;
|
||||
const VertexElement& e = newElements.Get()[i];
|
||||
for (const VertexElement& ee : reference->GetElements())
|
||||
for (const VertexElement& ee : refElements)
|
||||
{
|
||||
if (ee.Type == e.Type)
|
||||
{
|
||||
@@ -247,7 +248,7 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout*
|
||||
}
|
||||
if (addMissing)
|
||||
{
|
||||
for (const VertexElement& e : reference->GetElements())
|
||||
for (const VertexElement& e : refElements)
|
||||
{
|
||||
bool missing = true;
|
||||
for (const VertexElement& ee : base->GetElements())
|
||||
@@ -282,6 +283,32 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout*
|
||||
}
|
||||
}
|
||||
}
|
||||
if (referenceOrder)
|
||||
{
|
||||
for (int32 i = 0, j = 0; i < newElements.Count() && j < refElements.Count(); j++)
|
||||
{
|
||||
if (newElements[i].Type == refElements[j].Type)
|
||||
{
|
||||
// Elements match so move forward
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find reference element in a new list
|
||||
for (int32 k = i + 1; k < newElements.Count(); k++)
|
||||
{
|
||||
if (newElements[k].Type == refElements[j].Type)
|
||||
{
|
||||
// Move matching element to the reference position
|
||||
VertexElement e = newElements[k];
|
||||
newElements.RemoveAt(k);
|
||||
newElements.Insert(i, e);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (elementsModified)
|
||||
result = Get(newElements, true);
|
||||
}
|
||||
|
||||
@@ -84,8 +84,9 @@ public:
|
||||
/// <param name="removeUnused">True to remove elements from base layout that don't exist in a reference layout.</param>
|
||||
/// <param name="addMissing">True to add missing elements to base layout that exist in a reference layout.</param>
|
||||
/// <param name="missingSlotOverride">Allows to override the input slot for missing elements. Use value -1 to inherit slot from the reference layout.</param>
|
||||
/// <param name="referenceOrder">True to reorder result elements to match the reference layout. For example, if input vertex buffer layout is different than vertex shader then it can match those.</param>
|
||||
/// <returns>Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime.</returns>
|
||||
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1);
|
||||
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1, bool referenceOrder = false);
|
||||
|
||||
public:
|
||||
// [GPUResource]
|
||||
|
||||
@@ -724,7 +724,7 @@ void GPUContextDX11::SetState(GPUPipelineState* state)
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextDX11::ClearState()
|
||||
void GPUContextDX11::ResetState()
|
||||
{
|
||||
if (!_context)
|
||||
return;
|
||||
|
||||
@@ -158,7 +158,7 @@ public:
|
||||
void SetScissor(const Rectangle& scissorRect) override;
|
||||
GPUPipelineState* GetState() const override;
|
||||
void SetState(GPUPipelineState* state) override;
|
||||
void ClearState() override;
|
||||
void ResetState() override;
|
||||
void FlushState() override;
|
||||
void Flush() override;
|
||||
void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override;
|
||||
|
||||
@@ -143,6 +143,8 @@ void CommandQueueDX12::WaitForFence(uint64 fenceValue)
|
||||
|
||||
void CommandQueueDX12::WaitForGPU()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
const uint64 value = _fence.Signal(this);
|
||||
_fence.WaitCPU(value);
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ bool GPUBufferDX12::OnInit()
|
||||
// Create resource
|
||||
ID3D12Resource* resource;
|
||||
#if PLATFORM_WINDOWS
|
||||
D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
|
||||
D3D12_HEAP_FLAGS heapFlags = EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::VertexBuffer | GPUBufferFlags::IndexBuffer) || _desc.InitData ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE;
|
||||
#else
|
||||
D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE;
|
||||
#endif
|
||||
|
||||
@@ -1304,7 +1304,7 @@ void GPUContextDX12::SetState(GPUPipelineState* state)
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextDX12::ClearState()
|
||||
void GPUContextDX12::ResetState()
|
||||
{
|
||||
if (!_commandList)
|
||||
return;
|
||||
|
||||
@@ -201,7 +201,7 @@ public:
|
||||
void SetScissor(const Rectangle& scissorRect) override;
|
||||
GPUPipelineState* GetState() const override;
|
||||
void SetState(GPUPipelineState* state) override;
|
||||
void ClearState() override;
|
||||
void ResetState() override;
|
||||
void FlushState() override;
|
||||
void Flush() override;
|
||||
void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override;
|
||||
|
||||
@@ -628,6 +628,7 @@ bool GPUDeviceDX12::Init()
|
||||
VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf()));
|
||||
DXGI_FORMAT backbufferFormat = RenderToolsDX::ToDxgiFormat(GPU_BACK_BUFFER_PIXEL_FORMAT);
|
||||
UINT modesCount = 0;
|
||||
#ifdef _GAMING_XBOX_SCARLETT
|
||||
VALIDATE_DIRECTX_CALL(dxgiOutput->GetDisplayModeList(backbufferFormat, 0, &modesCount, NULL));
|
||||
Array<DXGIXBOX_MODE_DESC> modes;
|
||||
modes.Resize((int32)modesCount);
|
||||
@@ -642,6 +643,11 @@ bool GPUDeviceDX12::Init()
|
||||
videoOutput.RefreshRate = Math::Max(videoOutput.RefreshRate, mode.RefreshRate.Numerator / (float)mode.RefreshRate.Denominator);
|
||||
}
|
||||
modes.Resize(0);
|
||||
#else
|
||||
videoOutput.Width = 1920;
|
||||
videoOutput.Height = 1080;
|
||||
videoOutput.RefreshRate = 60;
|
||||
#endif
|
||||
|
||||
#if PLATFORM_GDK
|
||||
GDKPlatform::Suspended.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspended>(this);
|
||||
|
||||
@@ -159,7 +159,7 @@ bool GPUTextureDX12::OnInit()
|
||||
initialState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
|
||||
// Create texture
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS && 0
|
||||
D3D12_HEAP_FLAGS heapFlags = useRTV || useDSV ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE;
|
||||
#else
|
||||
D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE;
|
||||
|
||||
@@ -177,7 +177,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void ClearState() override
|
||||
void ResetState() override
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1329,7 +1329,7 @@ void GPUContextVulkan::SetState(GPUPipelineState* state)
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextVulkan::ClearState()
|
||||
void GPUContextVulkan::ResetState()
|
||||
{
|
||||
ResetRenderTarget();
|
||||
ResetSR();
|
||||
|
||||
@@ -193,7 +193,7 @@ public:
|
||||
void SetScissor(const Rectangle& scissorRect) override;
|
||||
GPUPipelineState* GetState() const override;
|
||||
void SetState(GPUPipelineState* state) override;
|
||||
void ClearState() override;
|
||||
void ResetState() override;
|
||||
void FlushState() override;
|
||||
void Flush() override;
|
||||
void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override;
|
||||
|
||||
@@ -554,9 +554,10 @@ void AnimatedModel::StopSlotAnimation(const StringView& slotName, Animation* ani
|
||||
{
|
||||
for (auto& slot : GraphInstance.Slots)
|
||||
{
|
||||
if (slot.Animation == anim && slot.Name == slotName)
|
||||
if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName)
|
||||
{
|
||||
//slot.Animation = nullptr; // TODO: make an immediate version of this method and set the animation to nullptr.
|
||||
if (slot.Animation != nullptr)
|
||||
slot.Reset = true;
|
||||
break;
|
||||
}
|
||||
@@ -573,7 +574,7 @@ void AnimatedModel::PauseSlotAnimation(const StringView& slotName, Animation* an
|
||||
{
|
||||
for (auto& slot : GraphInstance.Slots)
|
||||
{
|
||||
if (slot.Animation == anim && slot.Name == slotName)
|
||||
if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName)
|
||||
{
|
||||
slot.Pause = true;
|
||||
break;
|
||||
@@ -595,7 +596,7 @@ bool AnimatedModel::IsPlayingSlotAnimation(const StringView& slotName, Animation
|
||||
{
|
||||
for (auto& slot : GraphInstance.Slots)
|
||||
{
|
||||
if (slot.Animation == anim && slot.Name == slotName && !slot.Pause)
|
||||
if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName && !slot.Pause)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -412,8 +412,8 @@ public:
|
||||
/// Stops the animation playback on the slot in Anim Graph.
|
||||
/// </summary>
|
||||
/// <param name="slotName">The name of the slot.</param>
|
||||
/// <param name="anim">The animation to stop.</param>
|
||||
API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim);
|
||||
/// <param name="anim">The animation to check. Null to use slot name only.</param>
|
||||
API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Pauses all the animations playback on the all slots in Anim Graph.
|
||||
@@ -424,8 +424,8 @@ public:
|
||||
/// Pauses the animation playback on the slot in Anim Graph.
|
||||
/// </summary>
|
||||
/// <param name="slotName">The name of the slot.</param>
|
||||
/// <param name="anim">The animation to pause.</param>
|
||||
API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim);
|
||||
/// <param name="anim">The animation to check. Null to use slot name only.</param>
|
||||
API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any animation playback is active on any slot in Anim Graph (not paused).
|
||||
@@ -436,8 +436,8 @@ public:
|
||||
/// Checks if the animation playback is active on the slot in Anim Graph (not paused).
|
||||
/// </summary>
|
||||
/// <param name="slotName">The name of the slot.</param>
|
||||
/// <param name="anim">The animation to check.</param>
|
||||
API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim);
|
||||
/// <param name="anim">The animation to check. Null to use slot name only.</param>
|
||||
API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
|
||||
|
||||
private:
|
||||
void ApplyRootMotion(const Transform& rootMotionDelta);
|
||||
|
||||
@@ -16,7 +16,7 @@ const Char* GetCommandLine(int argc, char* argv[])
|
||||
const Char* cmdLine;
|
||||
if (length != 0)
|
||||
{
|
||||
Char* str = (Char*)malloc(length * sizeof(Char));
|
||||
Char* str = (Char*)malloc((length + 1) * sizeof(Char));
|
||||
cmdLine = str;
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
|
||||
@@ -100,6 +100,35 @@ API_STRUCT(NoDefault, Namespace="FlaxEngine.Networking") struct FLAXENGINE_API N
|
||||
return Word0 + Word1 != 0;
|
||||
}
|
||||
|
||||
NetworkClientsMask operator&(const NetworkClientsMask& other) const
|
||||
{
|
||||
return { Word0 & other.Word0, Word1 & other.Word1 };
|
||||
}
|
||||
|
||||
NetworkClientsMask operator|(const NetworkClientsMask& other) const
|
||||
{
|
||||
return { Word0 | other.Word0, Word1 | other.Word1 };
|
||||
}
|
||||
|
||||
NetworkClientsMask operator~() const
|
||||
{
|
||||
return { ~Word0, ~Word1 };
|
||||
}
|
||||
|
||||
NetworkClientsMask& operator|=(const NetworkClientsMask& other)
|
||||
{
|
||||
Word0 |= other.Word0;
|
||||
Word1 |= other.Word1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkClientsMask& operator&=(const NetworkClientsMask& other)
|
||||
{
|
||||
Word0 &= other.Word0;
|
||||
Word1 &= other.Word1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const NetworkClientsMask& other) const
|
||||
{
|
||||
return Word0 == other.Word0 && Word1 == other.Word1;
|
||||
|
||||
@@ -116,17 +116,6 @@ PACK_STRUCT(struct NetworkMessageObjectRpc
|
||||
|
||||
struct NetworkReplicatedObject
|
||||
{
|
||||
ScriptingObjectReference<ScriptingObject> Object;
|
||||
Guid ObjectId;
|
||||
Guid ParentId;
|
||||
uint32 OwnerClientId;
|
||||
uint32 LastOwnerFrame = 0;
|
||||
NetworkObjectRole Role;
|
||||
uint8 Spawned : 1;
|
||||
uint8 Synced : 1;
|
||||
DataContainer<uint32> TargetClientIds;
|
||||
INetworkObject* AsNetworkObject;
|
||||
|
||||
struct
|
||||
{
|
||||
NetworkClientsMask Mask;
|
||||
@@ -139,6 +128,17 @@ struct NetworkReplicatedObject
|
||||
}
|
||||
} RepCache;
|
||||
|
||||
ScriptingObjectReference<ScriptingObject> Object;
|
||||
Guid ObjectId;
|
||||
Guid ParentId;
|
||||
DataContainer<uint32> TargetClientIds;
|
||||
INetworkObject* AsNetworkObject;
|
||||
uint32 OwnerClientId;
|
||||
uint32 LastOwnerFrame = 0;
|
||||
NetworkObjectRole Role;
|
||||
uint8 Spawned : 1;
|
||||
uint8 Synced : 1;
|
||||
|
||||
NetworkReplicatedObject()
|
||||
{
|
||||
Spawned = 0;
|
||||
@@ -152,12 +152,12 @@ struct NetworkReplicatedObject
|
||||
|
||||
bool operator==(const NetworkReplicatedObject& other) const
|
||||
{
|
||||
return Object == other.Object;
|
||||
return ObjectId == other.ObjectId;
|
||||
}
|
||||
|
||||
bool operator==(const ScriptingObject* other) const
|
||||
{
|
||||
return Object == other;
|
||||
return other && ObjectId == other->GetID();
|
||||
}
|
||||
|
||||
bool operator==(const Guid& other) const
|
||||
@@ -176,6 +176,11 @@ inline uint32 GetHash(const NetworkReplicatedObject& key)
|
||||
return GetHash(key.ObjectId);
|
||||
}
|
||||
|
||||
inline uint32 GetHash(const ScriptingObject* key)
|
||||
{
|
||||
return key ? GetHash(key->GetID()) : 0;
|
||||
}
|
||||
|
||||
struct Serializer
|
||||
{
|
||||
NetworkReplicator::SerializeFunc Methods[2];
|
||||
@@ -698,14 +703,11 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients)
|
||||
return;
|
||||
auto& item = it->Item;
|
||||
const bool isClient = NetworkManager::IsClient();
|
||||
const NetworkClientsMask fullTargetClients = targetClients;
|
||||
|
||||
// Skip serialization of objects that none will receive
|
||||
if (!isClient)
|
||||
{
|
||||
BuildCachedTargets(item, targetClients);
|
||||
if (CachedTargets.Count() == 0)
|
||||
// If server has no recipients, skip early.
|
||||
if (!isClient && !targetClients)
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.AsNetworkObject)
|
||||
item.AsNetworkObject->OnNetworkSerialize();
|
||||
@@ -728,18 +730,30 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients)
|
||||
|
||||
#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)
|
||||
if (item.RepCache.Data.Length() == size && Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0)
|
||||
{
|
||||
// Check if only newly joined clients are missing this data to avoid resending it to everyone
|
||||
NetworkClientsMask missingClients = targetClients & ~item.RepCache.Mask;
|
||||
|
||||
// If data is the same and only the client set changed, replicate to missing clients only
|
||||
if (!missingClients)
|
||||
return;
|
||||
targetClients = missingClients;
|
||||
}
|
||||
item.RepCache.Mask = targetClients;
|
||||
item.RepCache.Mask = fullTargetClients;
|
||||
item.RepCache.Data.Copy(stream->GetBuffer(), size);
|
||||
#endif
|
||||
// TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state)
|
||||
constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable;
|
||||
|
||||
// Skip serialization of objects that none will receive
|
||||
if (!isClient)
|
||||
{
|
||||
BuildCachedTargets(item, targetClients);
|
||||
if (CachedTargets.Count() == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Send object to clients
|
||||
NetworkMessageObjectReplicate msgData;
|
||||
msgData.OwnerFrame = NetworkManager::Frame;
|
||||
@@ -1530,7 +1544,21 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj)
|
||||
// Register for despawning (batched during update)
|
||||
auto& despawn = DespawnQueue.AddOne();
|
||||
despawn.Id = obj->GetID();
|
||||
if (item.TargetClientIds.IsValid())
|
||||
{
|
||||
despawn.Targets = item.TargetClientIds;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Snapshot current recipients to avoid sending despawn to clients that connect later (and never got the spawn)
|
||||
Array<uint32, InlinedAllocation<8>> clientIds;
|
||||
for (const NetworkClient* client : NetworkManager::Clients)
|
||||
{
|
||||
if (client->State == NetworkConnectionState::Connected && client->ClientId != item.OwnerClientId)
|
||||
clientIds.Add(client->ClientId);
|
||||
}
|
||||
despawn.Targets.Copy(clientIds);
|
||||
}
|
||||
|
||||
// Prevent spawning
|
||||
for (int32 i = 0; i < SpawnQueue.Count(); i++)
|
||||
@@ -1823,6 +1851,31 @@ void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
||||
{
|
||||
ScopeLock lock(ObjectsLock);
|
||||
NewClients.Add(client);
|
||||
|
||||
// Ensure cached replication acknowledges the new client without resending to others.
|
||||
// Clear the new client's bit in RepCache and schedule a near-term replication.
|
||||
const int32 clientIndex = NetworkManager::Clients.Find(client);
|
||||
if (clientIndex != -1)
|
||||
{
|
||||
const uint64 bitMask = 1ull << (uint64)(clientIndex % 64);
|
||||
const int32 wordIndex = clientIndex / 64;
|
||||
for (auto it = Objects.Begin(); it.IsNotEnd(); ++it)
|
||||
{
|
||||
auto& item = it->Item;
|
||||
ScriptingObject* obj = item.Object.Get();
|
||||
if (!obj || !item.Spawned || item.Role != NetworkObjectRole::OwnedAuthoritative)
|
||||
continue;
|
||||
|
||||
// Mark this client as missing cached data
|
||||
uint64* word = wordIndex == 0 ? &item.RepCache.Mask.Word0 : &item.RepCache.Mask.Word1;
|
||||
*word &= ~bitMask;
|
||||
|
||||
// Force next replication tick for this object so the new client gets data promptly
|
||||
if (Hierarchy)
|
||||
Hierarchy->DirtyObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(sizeof(NetworkClientsMask) * 8 >= (uint32)NetworkManager::Clients.Count()); // Ensure that clients mask can hold all of clients
|
||||
}
|
||||
|
||||
@@ -2275,7 +2328,9 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network
|
||||
}
|
||||
else
|
||||
{
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to despawn object {}", objectId);
|
||||
// If this client never had the object (eg. it was targeted to other clients only), drop the message quietly
|
||||
DespawnedObjects.Add(objectId);
|
||||
NETWORK_REPLICATOR_LOG(Warning, "[NetworkReplicator] Failed to despawn object {}", objectId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -482,9 +482,15 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node,
|
||||
// Function Input
|
||||
case 1:
|
||||
{
|
||||
// Skip when graph is too small (eg. preview) and fallback with default value from the function graph
|
||||
if (context.GraphStack.Count() < 2)
|
||||
{
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the function call
|
||||
Node* functionCallNode = nullptr;
|
||||
ASSERT(context.GraphStack.Count() >= 2);
|
||||
ParticleEmitterGraphCPU* graph;
|
||||
for (int32 i = context.CallStackSize - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -52,6 +52,7 @@ Array<User*, FixedAllocation<8>> PlatformBase::Users;
|
||||
Delegate<User*> PlatformBase::UserAdded;
|
||||
Delegate<User*> PlatformBase::UserRemoved;
|
||||
void* OutOfMemoryBuffer = nullptr;
|
||||
volatile int64 FatalReporting = 0;
|
||||
|
||||
const Char* ToString(NetworkConnectionType value)
|
||||
{
|
||||
@@ -330,11 +331,20 @@ int32 PlatformBase::GetCacheLineSize()
|
||||
|
||||
void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType error)
|
||||
{
|
||||
// Let only one thread to report the error (and wait for it to end to have valid log before crash)
|
||||
RETRY:
|
||||
if (Platform::InterlockedCompareExchange(&FatalReporting, 1, 0) != 0)
|
||||
{
|
||||
Platform::Sleep(1);
|
||||
goto RETRY;
|
||||
}
|
||||
|
||||
// Check if is already during fatal state
|
||||
if (Engine::FatalError != FatalErrorType::None)
|
||||
{
|
||||
// Just send one more error to the log and back
|
||||
LOG(Error, "Error after fatal error: {0}", msg);
|
||||
Platform::AtomicStore(&FatalReporting, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -453,6 +463,8 @@ void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType er
|
||||
}
|
||||
#endif
|
||||
|
||||
Platform::AtomicStore(&FatalReporting, 0);
|
||||
|
||||
// Show error message
|
||||
if (Engine::ReportCrash.IsBinded())
|
||||
Engine::ReportCrash(msg, context);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Engine/Graphics/PixelFormatSampler.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
#include "IncludeX11.h"
|
||||
#include "ThirdParty/X11/Xutil.h"
|
||||
|
||||
// ICCCM
|
||||
#define WM_NormalState 1L // window normal state
|
||||
@@ -178,6 +179,20 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
X11::XSetTransientForHint(display, window, (X11::Window)((LinuxWindow*)settings.Parent)->GetNativePtr());
|
||||
}
|
||||
|
||||
// Provides class hint for WMs like Hyprland to hook onto and apply window rules
|
||||
X11::XClassHint* classHint = X11::XAllocClassHint();
|
||||
if (classHint)
|
||||
{
|
||||
const char* className = settings.IsRegularWindow ? "FlexEditor" : "FlaxPopup";
|
||||
|
||||
classHint->res_name = const_cast<char*>(className);
|
||||
classHint->res_class = const_cast<char*>(className);
|
||||
|
||||
X11::XSetClassHint(display, window, classHint);
|
||||
|
||||
XFree(classHint);
|
||||
}
|
||||
|
||||
_dpi = Platform::GetDpi();
|
||||
_dpiScale = (float)_dpi / (float)DefaultDPI;
|
||||
|
||||
|
||||
@@ -375,6 +375,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input,
|
||||
RENDER_TARGET_POOL_SET_NAME(bloomBuffer1, "PostProcessing.Bloom");
|
||||
RENDER_TARGET_POOL_SET_NAME(bloomBuffer2, "PostProcessing.Bloom");
|
||||
|
||||
// TODO: skip this clear? or do it at once for the whole textures (2 calls instead of per-mip)
|
||||
for (int32 mip = 0; mip < bloomMipCount; mip++)
|
||||
{
|
||||
context->Clear(bloomBuffer1->View(0, mip), Color::Transparent);
|
||||
|
||||
@@ -509,7 +509,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context)
|
||||
|
||||
// Render frame
|
||||
Renderer::Render(_task);
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
|
||||
// Copy frame to cube face
|
||||
{
|
||||
@@ -568,7 +568,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
|
||||
if (_workStep < 7)
|
||||
return; // Continue rendering next frame
|
||||
|
||||
@@ -920,6 +920,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
constexpr int32 vbMax = ARRAY_COUNT(DrawCall::Geometry.VertexBuffers);
|
||||
if (useInstancing)
|
||||
{
|
||||
context->UpdateCB(perDrawCB, &perDraw);
|
||||
GPUBuffer* vb[vbMax + 1];
|
||||
uint32 vbOffsets[vbMax + 1];
|
||||
vb[3] = _instanceBuffer.GetBuffer(); // Pass object index in a vertex stream at slot 3 (used by VS in Surface.shader)
|
||||
@@ -1057,7 +1058,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
materialBinds += list.PreBatchedDrawCalls.Count();
|
||||
if (list.Batches.IsEmpty() && list.Indices.Count() != 0)
|
||||
{
|
||||
// Draw calls list has bot been batched so execute draw calls separately
|
||||
// Draw calls list has not been batched so execute draw calls separately
|
||||
for (int32 j = 0; j < list.Indices.Count(); j++)
|
||||
{
|
||||
perDraw.DrawObjectIndex = listData[j];
|
||||
|
||||
@@ -273,7 +273,7 @@ struct DrawCallsList
|
||||
/// <summary>
|
||||
/// True if draw calls batches list can be rendered using hardware instancing, otherwise false.
|
||||
/// </summary>
|
||||
bool CanUseInstancing;
|
||||
bool CanUseInstancing = true;
|
||||
|
||||
void Clear();
|
||||
bool IsEmpty() const;
|
||||
|
||||
@@ -200,7 +200,7 @@ void Renderer::Render(SceneRenderTask* task)
|
||||
|
||||
// Prepare GPU context
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
context->FlushState();
|
||||
const Viewport viewport = task->GetViewport();
|
||||
context->SetViewportAndScissors(viewport);
|
||||
|
||||
@@ -2137,6 +2137,53 @@ static void* OnMonoDlFallbackClose(void* handle, void* user_data)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_MONO_AOT_MODULE
|
||||
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
|
||||
class MonoAotPreloadTask : public ThreadPoolTask
|
||||
{
|
||||
public:
|
||||
bool Run() override;
|
||||
};
|
||||
|
||||
// Preloads in-build AOT dynamic module in async
|
||||
class MonoAotPreloadService : public EngineService
|
||||
{
|
||||
public:
|
||||
volatile int64 Ready = 0;
|
||||
void* Library = nullptr;
|
||||
|
||||
MonoAotPreloadService()
|
||||
: EngineService(TEXT("AOT Preload"), -800)
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override
|
||||
{
|
||||
New<MonoAotPreloadTask>()->Start();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
MonoAotPreloadService MonoAotPreloadServiceInstance;
|
||||
|
||||
bool MonoAotPreloadTask::Run()
|
||||
{
|
||||
// Load AOT module
|
||||
Stopwatch aotModuleLoadStopwatch;
|
||||
//LOG(Info, "Loading Mono AOT module...");
|
||||
MonoAotPreloadServiceInstance.Library = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE));
|
||||
aotModuleLoadStopwatch.Stop();
|
||||
LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds());
|
||||
|
||||
Platform::AtomicStore(&MonoAotPreloadServiceInstance.Ready, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool InitHostfxr()
|
||||
{
|
||||
#if DOTNET_HOST_MONO_DEBUG
|
||||
@@ -2167,10 +2214,12 @@ bool InitHostfxr()
|
||||
#endif
|
||||
|
||||
#ifdef USE_MONO_AOT_MODULE
|
||||
// Load AOT module
|
||||
Stopwatch aotModuleLoadStopwatch;
|
||||
LOG(Info, "Loading Mono AOT module...");
|
||||
void* libAotModule = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE));
|
||||
// Wait for AOT module preloading
|
||||
while (Platform::AtomicRead(&MonoAotPreloadServiceInstance.Ready) == 0)
|
||||
Platform::Yield();
|
||||
|
||||
// Initialize AOT module
|
||||
void* libAotModule = MonoAotPreloadServiceInstance.Library;
|
||||
if (libAotModule == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to laod Mono AOT module (" TEXT(USE_MONO_AOT_MODULE) ")");
|
||||
@@ -2193,8 +2242,6 @@ bool InitHostfxr()
|
||||
mono_aot_register_module((void**)modules[i]);
|
||||
}
|
||||
Allocator::Free(modules);
|
||||
aotModuleLoadStopwatch.Stop();
|
||||
LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds());
|
||||
#endif
|
||||
|
||||
// Setup debugger
|
||||
|
||||
@@ -33,6 +33,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
ScriptingObjectReferenceBase(ScriptingObjectReferenceBase&& other) noexcept
|
||||
: _object(nullptr)
|
||||
{
|
||||
OnSet(other._object);
|
||||
other.OnSet(nullptr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScriptingObjectReferenceBase"/> class.
|
||||
/// </summary>
|
||||
@@ -96,6 +103,16 @@ protected:
|
||||
void OnSet(ScriptingObject* object);
|
||||
|
||||
void OnDeleted(ScriptingObject* obj);
|
||||
|
||||
ScriptingObjectReferenceBase& operator=(ScriptingObjectReferenceBase&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
OnSet(other._object);
|
||||
other.OnSet(nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -133,6 +150,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
ScriptingObjectReference(ScriptingObjectReference&& other) noexcept
|
||||
: ScriptingObjectReferenceBase(MoveTemp(other))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ScriptingObjectReference"/> class.
|
||||
/// </summary>
|
||||
@@ -173,6 +195,12 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
ScriptingObjectReference& operator=(ScriptingObjectReference&& other) noexcept
|
||||
{
|
||||
ScriptingObjectReferenceBase::operator=(MoveTemp(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE ScriptingObjectReference& operator=(const Guid& id)
|
||||
{
|
||||
OnSet(static_cast<ScriptingObject*>(FindObject(id, T::GetStaticClass())));
|
||||
|
||||
@@ -76,16 +76,19 @@ ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform,
|
||||
{
|
||||
IDxcCompiler3* compiler = nullptr;
|
||||
IDxcLibrary* library = nullptr;
|
||||
IDxcContainerBuilder* builder = nullptr;
|
||||
IDxcContainerReflection* containerReflection = nullptr;
|
||||
DxcCreateInstanceProc createInstance = dxcCreateInstanceProc ? (DxcCreateInstanceProc)dxcCreateInstanceProc : &DxcCreateInstance;
|
||||
if (FAILED(createInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast<void**>(&compiler))) ||
|
||||
FAILED(createInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast<void**>(&library))) ||
|
||||
FAILED(createInstance(CLSID_DxcContainerBuilder, __uuidof(builder), reinterpret_cast<void**>(&builder))) ||
|
||||
FAILED(createInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast<void**>(&containerReflection))))
|
||||
{
|
||||
LOG(Error, "DxcCreateInstance failed");
|
||||
}
|
||||
_compiler = compiler;
|
||||
_library = library;
|
||||
_builder = builder;
|
||||
_containerReflection = containerReflection;
|
||||
static HashSet<void*> PrintVersions;
|
||||
if (PrintVersions.Add(createInstance))
|
||||
@@ -103,14 +106,13 @@ ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform,
|
||||
|
||||
ShaderCompilerDX::~ShaderCompilerDX()
|
||||
{
|
||||
auto compiler = (IDxcCompiler2*)_compiler;
|
||||
if (compiler)
|
||||
if (auto compiler = (IDxcCompiler2*)_compiler)
|
||||
compiler->Release();
|
||||
auto library = (IDxcLibrary*)_library;
|
||||
if (library)
|
||||
if (auto library = (IDxcLibrary*)_library)
|
||||
library->Release();
|
||||
auto containerReflection = (IDxcContainerReflection*)_containerReflection;
|
||||
if (containerReflection)
|
||||
if (auto builder = (IDxcContainerBuilder*)_builder)
|
||||
builder->Release();
|
||||
if (auto containerReflection = (IDxcContainerReflection*)_containerReflection)
|
||||
containerReflection->Release();
|
||||
}
|
||||
|
||||
@@ -254,7 +256,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
|
||||
}
|
||||
|
||||
// Get the output
|
||||
ComPtr<IDxcBlob> shaderBuffer = nullptr;
|
||||
ComPtr<IDxcBlob> shaderBuffer;
|
||||
if (FAILED(results->GetResult(&shaderBuffer)))
|
||||
{
|
||||
LOG(Error, "IDxcOperationResult::GetResult failed.");
|
||||
@@ -460,6 +462,28 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
|
||||
}
|
||||
}
|
||||
|
||||
// Strip reflection data
|
||||
if (!options->GenerateDebugData)
|
||||
{
|
||||
if (auto builder = (IDxcContainerBuilder*)_builder)
|
||||
{
|
||||
if (builder->Load(shaderBuffer) == S_OK)
|
||||
{
|
||||
builder->RemovePart(DXC_PART_PDB);
|
||||
builder->RemovePart(DXC_PART_REFLECTION_DATA);
|
||||
ComPtr<IDxcOperationResult> serializeResult;
|
||||
if (builder->SerializeContainer(&serializeResult) == S_OK)
|
||||
{
|
||||
ComPtr<IDxcBlob> optimizedShaderBuffer;
|
||||
if (SUCCEEDED(serializeResult->GetResult(&optimizedShaderBuffer)))
|
||||
{
|
||||
shaderBuffer = optimizedShaderBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), shaderBuffer->GetBufferPointer(), (int32)shaderBuffer->GetBufferSize()))
|
||||
return true;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ private:
|
||||
Array<char> _funcNameDefineBuffer;
|
||||
void* _compiler;
|
||||
void* _library;
|
||||
void* _builder;
|
||||
void* _containerReflection;
|
||||
|
||||
public:
|
||||
|
||||
@@ -278,6 +278,17 @@ bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* co
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache1, int32 cache1Size, const void* cache2, int32 cache2Size)
|
||||
{
|
||||
auto output = context->Output;
|
||||
output->Write((uint32)(cache1Size + cache2Size + headerSize));
|
||||
output->WriteBytes(header, headerSize);
|
||||
output->WriteBytes(cache1, cache1Size);
|
||||
output->WriteBytes(cache2, cache2Size);
|
||||
output->Write(bindings);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize)
|
||||
{
|
||||
auto output = context->Output;
|
||||
|
||||
@@ -108,6 +108,7 @@ protected:
|
||||
|
||||
static bool WriteShaderFunctionBegin(ShaderCompilationContext* context, ShaderFunctionMeta& meta);
|
||||
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache, int32 cacheSize);
|
||||
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache1, int32 cache1Size, const void* cache2, int32 cache2Size);
|
||||
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize);
|
||||
static bool WriteShaderFunctionEnd(ShaderCompilationContext* context, ShaderFunctionMeta& meta);
|
||||
static bool WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros, void* additionalData);
|
||||
|
||||
@@ -376,7 +376,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context)
|
||||
EnableLightmapsUsage = _giBounceRunningIndex != 0;
|
||||
//
|
||||
Renderer::Render(_task);
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
//
|
||||
IsRunningRadiancePass = false;
|
||||
EnableLightmapsUsage = true;
|
||||
@@ -515,7 +515,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context)
|
||||
}
|
||||
|
||||
// Cleanup after rendering
|
||||
context->ClearState();
|
||||
context->ResetState();
|
||||
|
||||
// Mark job as done
|
||||
Platform::AtomicStore(&_wasJobDone, 1);
|
||||
|
||||
@@ -27,6 +27,19 @@ void CheckBitArray(const BitArray<AllocationType>& array)
|
||||
|
||||
TEST_CASE("Array")
|
||||
{
|
||||
SECTION("Test Capacity")
|
||||
{
|
||||
// Ensure correct collections capacity growing to meet proper memory usage vs safe slack
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1, 0) == 8);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(7, 0) == 8);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1, 16) == 16);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(31, 0) == 32);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(32, 0) == 32);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1000, 0) == 1024);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1024, 0) == 1024);
|
||||
CHECK(AllocationUtils::CalculateCapacityGrow(1025, 0) == 2048);
|
||||
}
|
||||
|
||||
SECTION("Test Allocators")
|
||||
{
|
||||
Array<int32> a1;
|
||||
|
||||
@@ -790,9 +790,18 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value)
|
||||
// Function Input
|
||||
case 1:
|
||||
{
|
||||
// Check the stack count. If only 1 graph is present,
|
||||
// we are processing the graph in isolation (e.g., in the Editor Preview).
|
||||
// In this case, we skip the caller-finding logic and use the node's default value.
|
||||
if (_graphStack.Count() < 2)
|
||||
{
|
||||
// Use the default value from the function input node's box (usually box 1)
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the function call
|
||||
Node* functionCallNode = nullptr;
|
||||
ASSERT(_graphStack.Count() >= 2);
|
||||
Graph* graph;
|
||||
for (int32 i = _callStack.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -303,6 +303,12 @@ namespace FlaxEngine.GUI
|
||||
[EditorDisplay("Text Style"), EditorOrder(2023), ExpandGroups]
|
||||
public Color TextColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color used to display highlighted text.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2024), ExpandGroups]
|
||||
public Color TextColorHighlighted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the horizontal text alignment within the control bounds.
|
||||
/// </summary>
|
||||
@@ -386,6 +392,7 @@ namespace FlaxEngine.GUI
|
||||
var style = Style.Current;
|
||||
Font = new FontReference(style.FontMedium);
|
||||
TextColor = style.Foreground;
|
||||
TextColorHighlighted = style.Foreground;
|
||||
BackgroundColor = style.BackgroundNormal;
|
||||
BackgroundColorHighlighted = BackgroundColor;
|
||||
BackgroundColorSelected = BackgroundColor;
|
||||
@@ -587,8 +594,8 @@ namespace FlaxEngine.GUI
|
||||
X = margin,
|
||||
Size = new Float2(size.X - margin, size.Y),
|
||||
Font = Font,
|
||||
TextColor = Color.White * 0.9f,
|
||||
TextColorHighlighted = Color.White,
|
||||
TextColor = TextColor * 0.9f,
|
||||
TextColorHighlighted = TextColorHighlighted,
|
||||
HorizontalAlignment = HorizontalAlignment,
|
||||
VerticalAlignment = VerticalAlignment,
|
||||
Text = _items[i],
|
||||
@@ -749,7 +756,7 @@ namespace FlaxEngine.GUI
|
||||
// Draw text of the selected item
|
||||
var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height);
|
||||
Render2D.PushClip(textRect);
|
||||
var textColor = TextColor;
|
||||
var textColor = (IsMouseOver || IsNavFocused) ? TextColorHighlighted : TextColor;
|
||||
string text = _items[_selectedIndex];
|
||||
string format = TextFormat != null ? TextFormat : null;
|
||||
if (!string.IsNullOrEmpty(format))
|
||||
|
||||
@@ -146,6 +146,8 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
|
||||
Box* b2 = node->GetBox(1);
|
||||
Value v1 = tryGetValue(b1, 0, Value::Zero);
|
||||
Value v2 = tryGetValue(b2, 1, Value::Zero);
|
||||
if (SanitizeMathValue(v1, node, b1, &value))
|
||||
break;
|
||||
if (b1->HasConnection())
|
||||
v2 = v2.Cast(v1.Type);
|
||||
else
|
||||
@@ -251,7 +253,10 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
|
||||
// Lerp
|
||||
case 25:
|
||||
{
|
||||
Value a = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
auto boxA = node->GetBox(0);
|
||||
Value a = tryGetValue(boxA, 0, Value::Zero);
|
||||
if (SanitizeMathValue(a, node, boxA, &value))
|
||||
break;
|
||||
Value b = tryGetValue(node->GetBox(1), 1, Value::One).Cast(a.Type);
|
||||
Value alpha = tryGetValue(node->GetBox(2), 2, Value::Zero).Cast(ValueType::Float);
|
||||
String text = String::Format(TEXT("lerp({0}, {1}, {2})"), a.Value, b.Value, alpha.Value);
|
||||
@@ -1364,6 +1369,20 @@ SerializedMaterialParam& ShaderGenerator::findOrAddGlobalSDF()
|
||||
return param;
|
||||
}
|
||||
|
||||
bool ShaderGenerator::SanitizeMathValue(Value& value, Node* node, Box* box, Value* resultOnInvalid)
|
||||
{
|
||||
bool invalid = value.Type == VariantType::Object;
|
||||
if (invalid)
|
||||
{
|
||||
OnError(node, box, TEXT("Invalid input type for math operation"));
|
||||
if (resultOnInvalid)
|
||||
*resultOnInvalid = Value::Zero;
|
||||
else
|
||||
value = Value::Zero;
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
String ShaderGenerator::getLocalName(int32 index)
|
||||
{
|
||||
return TEXT("local") + StringUtils::ToString(index);
|
||||
|
||||
@@ -255,6 +255,8 @@ protected:
|
||||
SerializedMaterialParam& findOrAddTextureGroupSampler(int32 index);
|
||||
SerializedMaterialParam& findOrAddGlobalSDF();
|
||||
|
||||
bool SanitizeMathValue(Value& value, Node* node, Box* box, Value* resultOnInvalid = nullptr);
|
||||
|
||||
static String getLocalName(int32 index);
|
||||
static String getParamName(int32 index);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
package com.flaxengine;
|
||||
|
||||
|
||||
@@ -217,7 +217,8 @@ namespace Flax.Build
|
||||
var engineLibraryType = LinkerOutput.SharedLibrary;
|
||||
if (buildOptions.Toolchain?.Compiler == TargetCompiler.MSVC)
|
||||
engineLibraryType = LinkerOutput.ImportLibrary; // MSVC links DLL against import library
|
||||
exeBuildOptions.LinkEnv.InputLibraries.Add(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType)));
|
||||
var engineLibraryPath = Utilities.NormalizePath(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType)));
|
||||
exeBuildOptions.LinkEnv.InputLibraries.Add(engineLibraryPath);
|
||||
exeBuildOptions.LinkEnv.InputFiles.AddRange(mainModuleOptions.OutputFiles);
|
||||
exeBuildOptions.DependencyFiles.AddRange(mainModuleOptions.DependencyFiles);
|
||||
exeBuildOptions.NugetPackageReferences.AddRange(mainModuleOptions.NugetPackageReferences);
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Flax.Build.Graph
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs tasks list sorting based on task dependencies and cost heuristics to to improve parallelism of the graph execution.
|
||||
/// Performs tasks list sorting based on task dependencies and cost heuristics to improve parallelism of the graph execution.
|
||||
/// </summary>
|
||||
public void SortTasks()
|
||||
{
|
||||
@@ -149,12 +149,7 @@ namespace Flax.Build.Graph
|
||||
{
|
||||
if (FileToProducingTaskMap.TryGetValue(prerequisiteFile, out var prerequisiteTask))
|
||||
{
|
||||
HashSet<Task> dependentTasks;
|
||||
if (taskToDependentActionsMap.ContainsKey(prerequisiteTask))
|
||||
{
|
||||
dependentTasks = taskToDependentActionsMap[prerequisiteTask];
|
||||
}
|
||||
else
|
||||
if (!taskToDependentActionsMap.TryGetValue(prerequisiteTask, out var dependentTasks))
|
||||
{
|
||||
dependentTasks = new HashSet<Task>();
|
||||
taskToDependentActionsMap[prerequisiteTask] = dependentTasks;
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace Flax.Deps.Dependencies
|
||||
defines += "-DDISABLE_EXECUTABLES=1-DDISABLE_SHARED_LIBS=1";
|
||||
buildArgs = $" -subset mono+libs -cmakeargs \"{defines}\" /p:FeaturePerfTracing=false /p:FeatureWin32Registry=false /p:FeatureCominteropApartmentSupport=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false";
|
||||
envVars.Add("_GAMING_XBOX", "1");
|
||||
envVars.Add(targetPlatform == TargetPlatform.XboxScarlett ? "_GAMING_XBOX_SCARLETT" : "_GAMING_XBOX_XBOXONE", "1");
|
||||
break;
|
||||
case TargetPlatform.Linux:
|
||||
os = "linux";
|
||||
|
||||
@@ -525,7 +525,7 @@ namespace Flax.Build.Platforms
|
||||
var args = new List<string>();
|
||||
args.AddRange(options.LinkEnv.CustomArgs);
|
||||
{
|
||||
args.Add(string.Format("-o \"{0}\"", outputFilePath));
|
||||
args.Add(string.Format("-o \"{0}\"", outputFilePath.Replace('\\', '/')));
|
||||
|
||||
if (!options.LinkEnv.DebugInformation)
|
||||
{
|
||||
@@ -612,7 +612,7 @@ namespace Flax.Build.Platforms
|
||||
/// <inheritdoc />
|
||||
public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath)
|
||||
{
|
||||
outputFilePath = outputFilePath.Replace('\\', '/');
|
||||
outputFilePath = Utilities.NormalizePath(outputFilePath);
|
||||
|
||||
Task linkTask;
|
||||
switch (options.LinkEnv.Output)
|
||||
|
||||
@@ -788,6 +788,7 @@ namespace Flax.Build.Platforms
|
||||
/// <inheritdoc />
|
||||
public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath)
|
||||
{
|
||||
outputFilePath = Utilities.NormalizePath(outputFilePath);
|
||||
var linkEnvironment = options.LinkEnv;
|
||||
var task = graph.Add<LinkTask>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user