Add multi-threaded scene rendering with Job System
This commit is contained in:
309
Source/Engine/Core/Collections/ConcurrentArray.h
Normal file
309
Source/Engine/Core/Collections/ConcurrentArray.h
Normal file
@@ -0,0 +1,309 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "Engine/Core/Memory/Memory.h"
|
||||
#include "Engine/Core/Memory/Allocation.h"
|
||||
|
||||
/// <summary>
|
||||
/// Template for dynamic array with variable capacity that support concurrent elements appending (atomic add).
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the array.</typeparam>
|
||||
/// <typeparam name="AllocationType">The type of memory allocator.</typeparam>
|
||||
template<typename T, typename AllocationType = HeapAllocation>
|
||||
class ConcurrentArray
|
||||
{
|
||||
friend ConcurrentArray;
|
||||
public:
|
||||
typedef T ItemType;
|
||||
typedef typename AllocationType::template Data<T> AllocationData;
|
||||
|
||||
private:
|
||||
volatile int64 _count;
|
||||
volatile int64 _capacity;
|
||||
AllocationData _allocation;
|
||||
CriticalSection _locker;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentArray"/> class.
|
||||
/// </summary>
|
||||
FORCE_INLINE ConcurrentArray()
|
||||
: _count(0)
|
||||
, _capacity(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentArray"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The initial capacity.</param>
|
||||
ConcurrentArray(int32 capacity)
|
||||
: _count(0)
|
||||
, _capacity(capacity)
|
||||
{
|
||||
if (capacity > 0)
|
||||
_allocation.Allocate(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentArray"/> class.
|
||||
/// </summary>
|
||||
/// <param name="data">The initial data.</param>
|
||||
/// <param name="length">The amount of items.</param>
|
||||
ConcurrentArray(const T* data, int32 length)
|
||||
{
|
||||
ASSERT(length >= 0);
|
||||
_count = _capacity = length;
|
||||
if (length > 0)
|
||||
{
|
||||
_allocation.Allocate(length);
|
||||
Memory::ConstructItems(_allocation.Get(), data, length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentArray"/> class.
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to copy.</param>
|
||||
ConcurrentArray(const ConcurrentArray& other)
|
||||
{
|
||||
_count = _capacity = other._count;
|
||||
if (_capacity > 0)
|
||||
{
|
||||
_allocation.Allocate(_capacity);
|
||||
Memory::ConstructItems(_allocation.Get(), other.Get(), (int32)other._count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurrentArray"/> class.
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
ConcurrentArray(ConcurrentArray&& other) noexcept
|
||||
{
|
||||
_count = other._count;
|
||||
_capacity = other._capacity;
|
||||
other._count = 0;
|
||||
other._capacity = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assignment operator that deletes the current collection of items and the copies items from the other array.
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to copy.</param>
|
||||
/// <returns>The reference to this.</returns>
|
||||
ConcurrentArray& operator=(const ConcurrentArray& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
Memory::DestructItems(_allocation.Get(), (int32)_count);
|
||||
if (_capacity < other.Count())
|
||||
{
|
||||
_allocation.Free();
|
||||
_capacity = other.Count();
|
||||
_allocation.Allocate(_capacity);
|
||||
}
|
||||
_count = other.Count();
|
||||
Memory::ConstructItems(_allocation.Get(), other.Get(), (int32)_count);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The move assignment operator that deletes the current collection of items and the moves items from the other array.
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
/// <returns>The reference to this.</returns>
|
||||
ConcurrentArray& operator=(ConcurrentArray&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
Memory::DestructItems(_allocation.Get(), (int32)_count);
|
||||
_allocation.Free();
|
||||
_count = other._count;
|
||||
_capacity = other._capacity;
|
||||
other._count = 0;
|
||||
other._capacity = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ConcurrentArray"/> class.
|
||||
/// </summary>
|
||||
~ConcurrentArray()
|
||||
{
|
||||
Memory::DestructItems(_allocation.Get(), (int32)_count);
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the amount of the items in the collection.
|
||||
/// </summary>
|
||||
FORCE_INLINE int32 Count() const
|
||||
{
|
||||
return (int32)Platform::AtomicRead((volatile int64*)&_count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of the items that can be contained by collection without resizing.
|
||||
/// </summary>
|
||||
FORCE_INLINE int32 Capacity() const
|
||||
{
|
||||
return (int32)Platform::AtomicRead((volatile int64*)&_capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the critical section locking the collection during resizing.
|
||||
/// </summary>
|
||||
FORCE_INLINE const CriticalSection& Locker() const
|
||||
{
|
||||
return _locker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the first item in the collection (linear allocation).
|
||||
/// </summary>
|
||||
FORCE_INLINE T* Get()
|
||||
{
|
||||
return _allocation.Get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the first item in the collection (linear allocation).
|
||||
/// </summary>
|
||||
FORCE_INLINE const T* Get() const
|
||||
{
|
||||
return _allocation.Get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item at the given index.
|
||||
/// </summary>
|
||||
/// <returns>The reference to the item.</returns>
|
||||
FORCE_INLINE T& operator[](int32 index)
|
||||
{
|
||||
ASSERT(index >= 0 && index < Count());
|
||||
return _allocation.Get()[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item at the given index.
|
||||
/// </summary>
|
||||
/// <returns>The reference to the item.</returns>
|
||||
FORCE_INLINE const T& operator[](int32 index) const
|
||||
{
|
||||
ASSERT(index >= 0 && index < Count());
|
||||
return _allocation.Get()[index];
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE T* begin()
|
||||
{
|
||||
return &_allocation.Get()[0];
|
||||
}
|
||||
|
||||
FORCE_INLINE T* end()
|
||||
{
|
||||
return &_allocation.Get()[Count()];
|
||||
}
|
||||
|
||||
FORCE_INLINE const T* begin() const
|
||||
{
|
||||
return &_allocation.Get()[0];
|
||||
}
|
||||
|
||||
FORCE_INLINE const T* end() const
|
||||
{
|
||||
return &_allocation.Get()[Count()];
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Clear the collection without changing its capacity.
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
_locker.Lock();
|
||||
Memory::DestructItems(_allocation.Get(), (int32)_count);
|
||||
_count = 0;
|
||||
_locker.Unlock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the capacity of the collection.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The new capacity.</param>
|
||||
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
|
||||
void SetCapacity(const int32 capacity, bool preserveContents = true)
|
||||
{
|
||||
_locker.Lock();
|
||||
if (capacity == _capacity)
|
||||
return;
|
||||
ASSERT(capacity >= 0);
|
||||
const int32 count = preserveContents ? ((int32)_count < capacity ? (int32)_count : capacity) : 0;
|
||||
_allocation.Relocate(capacity, (int32)_count, count);
|
||||
_capacity = capacity;
|
||||
_count = count;
|
||||
_locker.Unlock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the collection to the specified size. If the size is equal or less to the current capacity no additional memory reallocation in performed.
|
||||
/// </summary>
|
||||
/// <param name="size">The new collection size.</param>
|
||||
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize might not contain the previous data.</param>
|
||||
void Resize(int32 size, bool preserveContents = true)
|
||||
{
|
||||
_locker.Lock();
|
||||
if (_count > size)
|
||||
{
|
||||
Memory::DestructItems(_allocation.Get() + size, (int32)_count - size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureCapacity(size, preserveContents);
|
||||
Memory::ConstructItems(_allocation.Get() + _count, size - (int32)_count);
|
||||
}
|
||||
_count = size;
|
||||
_locker.Unlock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the collection has given capacity (or more).
|
||||
/// </summary>
|
||||
/// <param name="minCapacity">The minimum capacity.</param>
|
||||
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
|
||||
void EnsureCapacity(int32 minCapacity, bool preserveContents = true)
|
||||
{
|
||||
_locker.Lock();
|
||||
if (_capacity < minCapacity)
|
||||
{
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow((int32)_capacity, minCapacity);
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
_locker.Unlock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified item to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to add.</param>
|
||||
/// <returns>Index of the added element.</returns>
|
||||
int32 Add(const T& item)
|
||||
{
|
||||
const int32 count = (int32)Platform::AtomicRead(&_count);
|
||||
const int32 capacity = (int32)Platform::AtomicRead(&_capacity);
|
||||
const int32 minCapacity = count + PLATFORM_THREADS_LIMIT; // Ensure there is a room for all threads (eg. all possible threads add item at once)
|
||||
if (minCapacity >= capacity)
|
||||
EnsureCapacity(minCapacity);
|
||||
const int32 index = (int32)Platform::InterlockedIncrement(&_count) - 1;
|
||||
Memory::ConstructItems(_allocation.Get() + index, &item, 1);
|
||||
return index;
|
||||
}
|
||||
};
|
||||
@@ -84,12 +84,3 @@ void SkinnedMeshDrawData::OnDataChanged(bool dropHistory)
|
||||
_isDirty = true;
|
||||
_hasValidData = true;
|
||||
}
|
||||
|
||||
void SkinnedMeshDrawData::Flush(GPUContext* context)
|
||||
{
|
||||
if (_isDirty)
|
||||
{
|
||||
_isDirty = false;
|
||||
context->UpdateBuffer(BoneMatrices, Data.Get(), Data.Count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,14 @@ public:
|
||||
return BoneMatrices != nullptr && BoneMatrices->IsAllocated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance has been modified and needs to be flushed with GPU buffer.
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsDirty() const
|
||||
{
|
||||
return _isDirty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setups the data container for the specified bones amount.
|
||||
/// </summary>
|
||||
@@ -75,8 +83,10 @@ public:
|
||||
void OnDataChanged(bool dropHistory);
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the bones data buffer with the GPU by sending the data fro the CPU.
|
||||
/// After bones Data has been send to the GPU buffer.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context.</param>
|
||||
void Flush(class GPUContext* context);
|
||||
void OnFlush()
|
||||
{
|
||||
_isDirty = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ CriticalSection RenderTask::TasksLocker;
|
||||
int32 RenderTask::TasksDoneLastFrame;
|
||||
Array<PostProcessEffect*> SceneRenderTask::GlobalCustomPostFx;
|
||||
MainRenderTask* MainRenderTask::Instance;
|
||||
CriticalSection RenderContext::GPULocker;
|
||||
|
||||
PostProcessEffect::PostProcessEffect(const SpawnParams& params)
|
||||
: Script(params)
|
||||
|
||||
@@ -436,6 +436,11 @@ API_STRUCT(NoDefault) struct RenderContext
|
||||
/// </summary>
|
||||
API_FIELD() RenderView View;
|
||||
|
||||
/// <summary>
|
||||
/// The GPU access locking critical section to protect data access when performing multi-threaded rendering.
|
||||
/// </summary>
|
||||
static CriticalSection GPULocker;
|
||||
|
||||
RenderContext() = default;
|
||||
RenderContext(SceneRenderTask* task) noexcept;
|
||||
};
|
||||
@@ -462,6 +467,11 @@ API_STRUCT(NoDefault) struct RenderContextBatch
|
||||
/// </summary>
|
||||
API_FIELD() Array<RenderContext, RendererAllocation> Contexts;
|
||||
|
||||
/// <summary>
|
||||
/// The Job System labels to wait on, after draw calls collecting.
|
||||
/// </summary>
|
||||
API_FIELD() Array<int64, InlinedAllocation<8>> WaitLabels;
|
||||
|
||||
RenderContextBatch() = default;
|
||||
RenderContextBatch(SceneRenderTask* task);
|
||||
RenderContextBatch(const RenderContext& context);
|
||||
|
||||
@@ -555,7 +555,7 @@ void AnimatedModel::UpdateBounds()
|
||||
BoundingBox::Transform(_boxLocal, _transform, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
void AnimatedModel::UpdateSockets()
|
||||
@@ -713,7 +713,13 @@ void AnimatedModel::Draw(RenderContext& renderContext)
|
||||
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(_transform.Translation, renderContext.View.Position + renderContext.View.Origin));
|
||||
if (_skinningData.IsReady())
|
||||
{
|
||||
_skinningData.Flush(GPUDevice::Instance->GetMainContext());
|
||||
// Flush skinning data with GPU
|
||||
if (_skinningData.IsDirty())
|
||||
{
|
||||
RenderContext::GPULocker.Lock();
|
||||
GPUDevice::Instance->GetMainContext()->UpdateBuffer(_skinningData.BoneMatrices, _skinningData.Data.Get(), _skinningData.Data.Count());
|
||||
RenderContext::GPULocker.Unlock();
|
||||
}
|
||||
|
||||
SkinnedMesh::DrawInfo draw;
|
||||
draw.Buffer = &Entries;
|
||||
@@ -749,7 +755,13 @@ void AnimatedModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(_transform.Translation, renderContext.View.Position + renderContext.View.Origin));
|
||||
if (_skinningData.IsReady())
|
||||
{
|
||||
_skinningData.Flush(GPUDevice::Instance->GetMainContext());
|
||||
// Flush skinning data with GPU
|
||||
if (_skinningData.IsDirty())
|
||||
{
|
||||
RenderContext::GPULocker.Lock();
|
||||
GPUDevice::Instance->GetMainContext()->UpdateBuffer(_skinningData.BoneMatrices, _skinningData.Data.Get(), _skinningData.Data.Count());
|
||||
RenderContext::GPULocker.Unlock();
|
||||
}
|
||||
|
||||
SkinnedMesh::DrawInfo draw;
|
||||
draw.Buffer = &Entries;
|
||||
@@ -948,7 +960,7 @@ void AnimatedModel::OnTransformChanged()
|
||||
BoundingBox::Transform(_boxLocal, _transform, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
void AnimatedModel::WaitForModelLoad()
|
||||
|
||||
@@ -20,7 +20,7 @@ void ModelInstanceActor::SetEntries(const Array<ModelInstanceEntry>& value)
|
||||
Entries[i] = value[i];
|
||||
}
|
||||
if (anyChanged && _sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
void ModelInstanceActor::SetMaterial(int32 entryIndex, MaterialBase* material)
|
||||
@@ -33,7 +33,7 @@ void ModelInstanceActor::SetMaterial(int32 entryIndex, MaterialBase* material)
|
||||
return;
|
||||
Entries[entryIndex].Material = material;
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 entryIndex)
|
||||
@@ -45,7 +45,7 @@ MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32
|
||||
const auto result = material->CreateVirtualInstance();
|
||||
Entries[entryIndex].Material = result;
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -56,12 +56,12 @@ void ModelInstanceActor::WaitForModelLoad()
|
||||
void ModelInstanceActor::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
void ModelInstanceActor::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
@@ -72,5 +72,5 @@ void ModelInstanceActor::OnDisable()
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ API_CLASS(Abstract) class FLAXENGINE_API ModelInstanceActor : public Actor
|
||||
{
|
||||
DECLARE_SCENE_OBJECT_ABSTRACT(ModelInstanceActor);
|
||||
protected:
|
||||
int32 _sceneRenderingKey = -1;
|
||||
int32 _sceneRenderingKey = -1; // Uses SceneRendering::DrawCategory::SceneDrawAsync
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -210,7 +210,7 @@ void SplineModel::OnSplineUpdated()
|
||||
BoundingSphere::Merge(_sphere, _instances[i].Sphere, _sphere);
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
void SplineModel::UpdateDeformationBuffer()
|
||||
@@ -309,8 +309,7 @@ void SplineModel::UpdateDeformationBuffer()
|
||||
}
|
||||
|
||||
// Flush data with GPU
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
context->UpdateBuffer(_deformationBuffer, _deformationBufferData, size);
|
||||
GPUDevice::Instance->GetMainContext()->UpdateBuffer(_deformationBuffer, _deformationBufferData, size);
|
||||
|
||||
// Static splines are rarely updated so release scratch memory
|
||||
if (IsTransformStatic())
|
||||
@@ -359,7 +358,11 @@ void SplineModel::Draw(RenderContext& renderContext)
|
||||
|
||||
// Build mesh deformation buffer for the whole spline
|
||||
if (_deformationDirty)
|
||||
{
|
||||
RenderContext::GPULocker.Lock();
|
||||
UpdateDeformationBuffer();
|
||||
RenderContext::GPULocker.Unlock();
|
||||
}
|
||||
|
||||
// Draw all segments
|
||||
DrawCall drawCall;
|
||||
|
||||
@@ -173,7 +173,7 @@ void StaticModel::OnModelChanged()
|
||||
if (Model && !Model->IsLoaded())
|
||||
UpdateBounds();
|
||||
else if (!Model && _sceneRenderingKey != -1)
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
void StaticModel::OnModelLoaded()
|
||||
@@ -190,7 +190,7 @@ void StaticModel::OnModelLoaded()
|
||||
}
|
||||
else
|
||||
{
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ void StaticModel::OnModelResidencyChanged()
|
||||
{
|
||||
if (_sceneRenderingKey == -1 && _scene && Model && Model->GetLoadedLODs() > 0 && _residencyChangedModel)
|
||||
{
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
_residencyChangedModel->ResidencyChanged.Unbind<StaticModel, &StaticModel::OnModelResidencyChanged>(this);
|
||||
_residencyChangedModel = nullptr;
|
||||
}
|
||||
@@ -219,11 +219,12 @@ void StaticModel::UpdateBounds()
|
||||
}
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
|
||||
void StaticModel::FlushVertexColors()
|
||||
{
|
||||
RenderContext::GPULocker.Lock();
|
||||
for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++)
|
||||
{
|
||||
auto& vertexColorsData = _vertexColorsData[lodIndex];
|
||||
@@ -236,7 +237,7 @@ void StaticModel::FlushVertexColors()
|
||||
if (vertexColorsBuffer->GetSize() != size)
|
||||
{
|
||||
if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count())))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size);
|
||||
}
|
||||
@@ -245,6 +246,7 @@ void StaticModel::FlushVertexColors()
|
||||
SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer);
|
||||
}
|
||||
}
|
||||
RenderContext::GPULocker.Unlock();
|
||||
}
|
||||
|
||||
bool StaticModel::HasContentLoaded() const
|
||||
@@ -559,7 +561,7 @@ void StaticModel::OnEnable()
|
||||
}
|
||||
else
|
||||
{
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,7 +576,7 @@ void StaticModel::OnDisable()
|
||||
|
||||
if (_sceneRenderingKey != -1)
|
||||
{
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey, SceneRendering::SceneDrawAsync);
|
||||
}
|
||||
if (_residencyChangedModel)
|
||||
{
|
||||
|
||||
@@ -1,25 +1,14 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#define SCENE_RENDERING_USE_PROFILER 0
|
||||
#define SCENE_RENDERING_USE_PROFILER_PER_ACTOR 0
|
||||
|
||||
#include "SceneRendering.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Threading/JobSystem.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#endif
|
||||
|
||||
FORCE_INLINE bool FrustumsListCull(const BoundingSphere& bounds, const BoundingFrustum* frustums, int32 frustumsCount)
|
||||
{
|
||||
for (int32 i = 0; i < frustumsCount; i++)
|
||||
{
|
||||
if (frustums[i].Intersects(bounds))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ISceneRenderingListener::~ISceneRenderingListener()
|
||||
{
|
||||
@@ -38,99 +27,65 @@ void ISceneRenderingListener::ListenSceneRendering(SceneRendering* scene)
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE bool FrustumsListCull(const BoundingSphere& bounds, const Array<BoundingFrustum>& frustums)
|
||||
{
|
||||
const int32 count = frustums.Count();
|
||||
const BoundingFrustum* data = frustums.Get();
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
if (data[i].Intersects(bounds))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory category)
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
auto& view = renderContextBatch.GetMainContext().View;
|
||||
const Vector3 origin = view.Origin;
|
||||
for (auto& renderContext : renderContextBatch.Contexts)
|
||||
renderContext.List->Scenes.Add(this);
|
||||
auto& list = Actors[(int32)category];
|
||||
_drawListData = list.Get();
|
||||
_drawListSize = list.Count();
|
||||
_drawBatch = &renderContextBatch;
|
||||
|
||||
// Setup frustum data
|
||||
Array<BoundingFrustum, RendererAllocation> frustumsData;
|
||||
BoundingFrustum* frustums = &view.CullingFrustum;
|
||||
int32 frustumsCount = renderContextBatch.Contexts.Count();
|
||||
auto& frustumsData = _drawFrustumsData;
|
||||
const int32 frustumsCount = renderContextBatch.Contexts.Count();
|
||||
if (frustumsCount != 1)
|
||||
{
|
||||
frustumsData.Resize(frustumsCount);
|
||||
frustums = frustumsData.Get();
|
||||
for (int32 i = 0; i < frustumsCount; i++)
|
||||
frustums[i] = renderContextBatch.Contexts[i].View.CullingFrustum;
|
||||
frustumsData.Get()[i] = renderContextBatch.Contexts[i].View.CullingFrustum;
|
||||
}
|
||||
|
||||
#define CHECK_ACTOR ((view.RenderLayersMask.Mask & e.LayerMask) && (e.NoCulling || FrustumsListCull(e.Bounds, frustums, frustumsCount)))
|
||||
#define CHECK_ACTOR_SINGLE_FRUSTUM ((view.RenderLayersMask.Mask & e.LayerMask) && (e.NoCulling || frustums->Intersects(e.Bounds)))
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
#define DRAW_ACTOR(mode) PROFILE_CPU_ACTOR(e.Actor); e.Actor->Draw(mode)
|
||||
#else
|
||||
#define DRAW_ACTOR(mode) e.Actor->Draw(mode)
|
||||
#endif
|
||||
|
||||
// Draw all visual components
|
||||
if (view.IsOfflinePass)
|
||||
_drawListIndex = -1;
|
||||
if (_drawListSize >= 64 && category == SceneDrawAsync)
|
||||
{
|
||||
// Offline pass with additional static flags culling
|
||||
for (int32 i = 0; i < list.Count(); i++)
|
||||
{
|
||||
auto e = list.Get()[i];
|
||||
e.Bounds.Center -= origin;
|
||||
if (CHECK_ACTOR && e.Actor->GetStaticFlags() & view.StaticFlagsMask)
|
||||
{
|
||||
DRAW_ACTOR(renderContextBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (origin.IsZero() && frustumsCount == 1)
|
||||
{
|
||||
// Fast path for no origin shifting with a single context
|
||||
RenderContext& renderContext = renderContextBatch.GetMainContext();
|
||||
for (int32 i = 0; i < list.Count(); i++)
|
||||
{
|
||||
auto e = list.Get()[i];
|
||||
if (CHECK_ACTOR_SINGLE_FRUSTUM)
|
||||
{
|
||||
DRAW_ACTOR(renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (origin.IsZero())
|
||||
{
|
||||
// Fast path for no origin shifting
|
||||
for (int32 i = 0; i < list.Count(); i++)
|
||||
{
|
||||
auto e = list.Get()[i];
|
||||
if (CHECK_ACTOR)
|
||||
{
|
||||
DRAW_ACTOR(renderContextBatch);
|
||||
}
|
||||
}
|
||||
// Run in async via Job System
|
||||
Function<void(int32)> func;
|
||||
func.Bind<SceneRendering, &SceneRendering::DrawActorsJob>(this);
|
||||
const uint64 waitLabel = JobSystem::Dispatch(func, JobSystem::GetThreadsCount());
|
||||
renderContextBatch.WaitLabels.Add(waitLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generic case
|
||||
for (int32 i = 0; i < list.Count(); i++)
|
||||
{
|
||||
auto e = list.Get()[i];
|
||||
e.Bounds.Center -= origin;
|
||||
if (CHECK_ACTOR)
|
||||
{
|
||||
DRAW_ACTOR(renderContextBatch);
|
||||
}
|
||||
}
|
||||
// Scene is small so draw on a main-thread
|
||||
DrawActorsJob(0);
|
||||
}
|
||||
|
||||
#undef CHECK_ACTOR
|
||||
#undef DRAW_ACTOR
|
||||
#if USE_EDITOR
|
||||
if (view.Pass & DrawPass::GBuffer && category == SceneDraw)
|
||||
{
|
||||
// Draw physics shapes
|
||||
if (view.Flags & ViewFlags::PhysicsDebug || view.Mode == ViewMode::PhysicsColliders)
|
||||
{
|
||||
const PhysicsDebugCallback* physicsDebugData = PhysicsDebug.Get();
|
||||
for (int32 i = 0; i < PhysicsDebug.Count(); i++)
|
||||
{
|
||||
PhysicsDebug[i](view);
|
||||
physicsDebugData[i](view);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,7 +99,7 @@ void SceneRendering::CollectPostFxVolumes(RenderContext& renderContext)
|
||||
#endif
|
||||
for (int32 i = 0; i < PostFxProviders.Count(); i++)
|
||||
{
|
||||
PostFxProviders[i]->Collect(renderContext);
|
||||
PostFxProviders.Get()[i]->Collect(renderContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +129,7 @@ void SceneRendering::AddActor(Actor* a, int32& key, DrawCategory category)
|
||||
key = 0;
|
||||
for (; key < list.Count(); key++)
|
||||
{
|
||||
if (list[key].Actor == nullptr)
|
||||
if (list.Get()[key].Actor == nullptr)
|
||||
break;
|
||||
}
|
||||
if (key == list.Count())
|
||||
@@ -217,3 +172,65 @@ void SceneRendering::RemoveActor(Actor* a, int32& key, DrawCategory category)
|
||||
}
|
||||
key = -1;
|
||||
}
|
||||
|
||||
#define FOR_EACH_BATCH_ACTOR const int64 count = _drawListSize; while (true) { const int64 index = Platform::InterlockedIncrement(&_drawListIndex); if (index >= count) break; auto e = _drawListData[index];
|
||||
#define CHECK_ACTOR ((view.RenderLayersMask.Mask & e.LayerMask) && (e.NoCulling || FrustumsListCull(e.Bounds, _drawFrustumsData)))
|
||||
#define CHECK_ACTOR_SINGLE_FRUSTUM ((view.RenderLayersMask.Mask & e.LayerMask) && (e.NoCulling || view.CullingFrustum.Intersects(e.Bounds)))
|
||||
#if SCENE_RENDERING_USE_PROFILER_PER_ACTOR
|
||||
#define DRAW_ACTOR(mode) PROFILE_CPU_ACTOR(e.Actor); e.Actor->Draw(mode)
|
||||
#else
|
||||
#define DRAW_ACTOR(mode) e.Actor->Draw(mode)
|
||||
#endif
|
||||
|
||||
void SceneRendering::DrawActorsJob(int32 i)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
auto& mainContext = _drawBatch->GetMainContext();
|
||||
const auto& view = mainContext.View;
|
||||
if (view.IsOfflinePass)
|
||||
{
|
||||
// Offline pass with additional static flags culling
|
||||
FOR_EACH_BATCH_ACTOR
|
||||
e.Bounds.Center -= view.Origin;
|
||||
if (CHECK_ACTOR && e.Actor->GetStaticFlags() & view.StaticFlagsMask)
|
||||
{
|
||||
DRAW_ACTOR(*_drawBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (view.Origin.IsZero() && _drawFrustumsData.Count() == 1)
|
||||
{
|
||||
// Fast path for no origin shifting with a single context
|
||||
FOR_EACH_BATCH_ACTOR
|
||||
if (CHECK_ACTOR_SINGLE_FRUSTUM)
|
||||
{
|
||||
DRAW_ACTOR(mainContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (view.Origin.IsZero())
|
||||
{
|
||||
// Fast path for no origin shifting
|
||||
FOR_EACH_BATCH_ACTOR
|
||||
if (CHECK_ACTOR)
|
||||
{
|
||||
DRAW_ACTOR(*_drawBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generic case
|
||||
FOR_EACH_BATCH_ACTOR
|
||||
e.Bounds.Center -= view.Origin;
|
||||
if (CHECK_ACTOR)
|
||||
{
|
||||
DRAW_ACTOR(*_drawBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef FOR_EACH_BATCH_ACTOR
|
||||
#undef CHECK_ACTOR
|
||||
#undef DRAW_ACTOR
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Math/BoundingSphere.h"
|
||||
#include "Engine/Core/Math/BoundingFrustum.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
|
||||
@@ -81,6 +82,7 @@ public:
|
||||
enum DrawCategory
|
||||
{
|
||||
SceneDraw = 0,
|
||||
SceneDrawAsync,
|
||||
PreRender,
|
||||
PostRender,
|
||||
MAX
|
||||
@@ -161,4 +163,13 @@ public:
|
||||
ViewportIcons.Remove(obj);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
Array<BoundingFrustum> _drawFrustumsData;
|
||||
DrawActor* _drawListData;
|
||||
int64 _drawListSize;
|
||||
volatile int64 _drawListIndex;
|
||||
RenderContextBatch* _drawBatch;
|
||||
|
||||
void DrawActorsJob(int32 i);
|
||||
};
|
||||
|
||||
@@ -581,7 +581,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
|
||||
// Check if need to perform any particles sorting
|
||||
if (emitter->Graph.SortModules.HasItems() && renderContext.View.Pass != DrawPass::Depth)
|
||||
{
|
||||
PROFILE_GPU_CPU("Sort Particles");
|
||||
PROFILE_GPU_CPU_NAMED("Sort Particles");
|
||||
|
||||
// Prepare pipeline
|
||||
if (GPUParticlesSorting == nullptr)
|
||||
|
||||
@@ -136,7 +136,7 @@ void MaterialComplexityMaterialShader::Draw(RenderContext& renderContext, GPUCon
|
||||
auto& decalsWrapper = _wrappers[4];
|
||||
if (decals.HasItems() && boxModel && boxModel->CanBeRendered() && decalsWrapper.IsReady())
|
||||
{
|
||||
PROFILE_GPU_CPU("Decals");
|
||||
PROFILE_GPU_CPU_NAMED("Decals");
|
||||
DrawCall drawCall;
|
||||
MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
|
||||
drawCall.WorldDeterminantSign = 1.0f;
|
||||
@@ -166,14 +166,14 @@ void MaterialComplexityMaterialShader::Draw(RenderContext& renderContext, GPUCon
|
||||
auto& distortionList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::Distortion];
|
||||
if (!distortionList.IsEmpty())
|
||||
{
|
||||
PROFILE_GPU_CPU("Distortion");
|
||||
PROFILE_GPU_CPU_NAMED("Distortion");
|
||||
renderContext.View.Pass = DrawPass::Distortion;
|
||||
renderContext.List->ExecuteDrawCalls(renderContext, distortionList);
|
||||
}
|
||||
auto& forwardList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::Forward];
|
||||
if (!forwardList.IsEmpty())
|
||||
{
|
||||
PROFILE_GPU_CPU("Forward");
|
||||
PROFILE_GPU_CPU_NAMED("Forward");
|
||||
renderContext.View.Pass = DrawPass::Forward;
|
||||
renderContext.List->ExecuteDrawCalls(renderContext, forwardList);
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
|
||||
|
||||
// Clear GBuffer
|
||||
{
|
||||
PROFILE_GPU_CPU("Clear");
|
||||
PROFILE_GPU_CPU_NAMED("Clear");
|
||||
|
||||
context->ClearDepth(*renderContext.Buffers->DepthBuffer);
|
||||
context->Clear(lightBuffer, Color::Transparent);
|
||||
@@ -222,7 +222,7 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
|
||||
// Draw sky
|
||||
if (renderContext.List->Sky && _skyModel && _skyModel->CanBeRendered())
|
||||
{
|
||||
PROFILE_GPU_CPU("Sky");
|
||||
PROFILE_GPU_CPU_NAMED("Sky");
|
||||
DrawSky(renderContext, context);
|
||||
}
|
||||
|
||||
|
||||
@@ -482,16 +482,20 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
const float minObjectRadius = 20.0f; // Skip too small objects
|
||||
_cullingPosDistance = Vector4(viewPosition, distance);
|
||||
int32 actorsDrawn = 0;
|
||||
SceneRendering::DrawCategory drawCategories[] = { SceneRendering::SceneDraw, SceneRendering::SceneDrawAsync };
|
||||
for (auto* scene : renderContext.List->Scenes)
|
||||
{
|
||||
auto& list = scene->Actors[SceneRendering::SceneDraw];
|
||||
for (auto& e : list)
|
||||
for (SceneRendering::DrawCategory drawCategory : drawCategories)
|
||||
{
|
||||
if (viewMask & e.LayerMask && e.Bounds.Radius >= minObjectRadius && CollisionsHelper::DistanceSpherePoint(e.Bounds, viewPosition) < distance)
|
||||
auto& list = scene->Actors[drawCategory];
|
||||
for (auto& e : list)
|
||||
{
|
||||
//PROFILE_CPU_ACTOR(e.Actor);
|
||||
e.Actor->Draw(renderContext);
|
||||
actorsDrawn++;
|
||||
if (e.Bounds.Radius >= minObjectRadius && viewMask & e.LayerMask && CollisionsHelper::DistanceSpherePoint(e.Bounds, viewPosition) < distance)
|
||||
{
|
||||
//PROFILE_CPU_ACTOR(e.Actor);
|
||||
e.Actor->Draw(renderContext);
|
||||
actorsDrawn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,16 +549,20 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
|
||||
BoundingBox cascadeBoundsWorld = cascadeBounds.MakeOffsetted(sdfData.Origin);
|
||||
_cascadeCullingBounds = cascadeBoundsWorld;
|
||||
int32 actorsDrawn = 0;
|
||||
for (SceneRendering* scene : renderContext.List->Scenes)
|
||||
SceneRendering::DrawCategory drawCategories[] = { SceneRendering::SceneDraw, SceneRendering::SceneDrawAsync };
|
||||
for (auto* scene : renderContext.List->Scenes)
|
||||
{
|
||||
auto& list = scene->Actors[SceneRendering::SceneDraw];
|
||||
for (const auto& e : list)
|
||||
for (SceneRendering::DrawCategory drawCategory : drawCategories)
|
||||
{
|
||||
if (viewMask & e.LayerMask && e.Bounds.Radius >= minObjectRadius && CollisionsHelper::BoxIntersectsSphere(cascadeBoundsWorld, e.Bounds))
|
||||
auto& list = scene->Actors[drawCategory];
|
||||
for (const auto& e : list)
|
||||
{
|
||||
//PROFILE_CPU_ACTOR(e.Actor);
|
||||
e.Actor->Draw(renderContext);
|
||||
actorsDrawn++;
|
||||
if (e.Bounds.Radius >= minObjectRadius && viewMask & e.LayerMask && CollisionsHelper::BoxIntersectsSphere(cascadeBoundsWorld, e.Bounds))
|
||||
{
|
||||
//PROFILE_CPU_ACTOR(e.Actor);
|
||||
e.Actor->Draw(renderContext);
|
||||
actorsDrawn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
// Render all point lights
|
||||
for (int32 lightIndex = 0; lightIndex < mainCache->PointLights.Count(); lightIndex++)
|
||||
{
|
||||
PROFILE_GPU_CPU("Point Light");
|
||||
PROFILE_GPU_CPU_NAMED("Point Light");
|
||||
|
||||
// Cache data
|
||||
auto& light = mainCache->PointLights[lightIndex];
|
||||
@@ -296,7 +296,7 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
// Render all spot lights
|
||||
for (int32 lightIndex = 0; lightIndex < mainCache->SpotLights.Count(); lightIndex++)
|
||||
{
|
||||
PROFILE_GPU_CPU("Spot Light");
|
||||
PROFILE_GPU_CPU_NAMED("Spot Light");
|
||||
|
||||
// Cache data
|
||||
auto& light = mainCache->SpotLights[lightIndex];
|
||||
@@ -353,7 +353,7 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
// Render all directional lights
|
||||
for (int32 lightIndex = 0; lightIndex < mainCache->DirectionalLights.Count(); lightIndex++)
|
||||
{
|
||||
PROFILE_GPU_CPU("Directional Light");
|
||||
PROFILE_GPU_CPU_NAMED("Directional Light");
|
||||
|
||||
// Cache data
|
||||
auto& light = mainCache->DirectionalLights[lightIndex];
|
||||
@@ -390,7 +390,7 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
// Render all sky lights
|
||||
for (int32 lightIndex = 0; lightIndex < mainCache->SkyLights.Count(); lightIndex++)
|
||||
{
|
||||
PROFILE_GPU_CPU("Sky Light");
|
||||
PROFILE_GPU_CPU_NAMED("Sky Light");
|
||||
|
||||
// Cache data
|
||||
auto& light = mainCache->SkyLights[lightIndex];
|
||||
|
||||
@@ -408,7 +408,7 @@ void RenderList::Clear()
|
||||
_instanceBuffer.Clear();
|
||||
}
|
||||
|
||||
void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals)
|
||||
void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, const DrawCall& drawCall, bool receivesDecals)
|
||||
{
|
||||
#if ENABLE_ASSERTION_LOW_LAYERS
|
||||
// Ensure that draw modes are non-empty and in conjunction with material draw modes
|
||||
@@ -417,8 +417,7 @@ void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCa
|
||||
#endif
|
||||
|
||||
// Append draw call data
|
||||
const int32 index = DrawCalls.Count();
|
||||
DrawCalls.Add(drawCall);
|
||||
const int32 index = DrawCalls.Add(drawCall);
|
||||
|
||||
// Add draw call to proper draw lists
|
||||
if (drawModes & DrawPass::Depth)
|
||||
@@ -446,7 +445,7 @@ void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCa
|
||||
}
|
||||
}
|
||||
|
||||
void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals)
|
||||
void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, const DrawCall& drawCall, bool receivesDecals)
|
||||
{
|
||||
#if ENABLE_ASSERTION_LOW_LAYERS
|
||||
// Ensure that draw modes are non-empty and in conjunction with material draw modes
|
||||
@@ -455,8 +454,7 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
|
||||
#endif
|
||||
|
||||
// Append draw call data
|
||||
const int32 index = DrawCalls.Count();
|
||||
DrawCalls.Add(drawCall);
|
||||
const int32 index = DrawCalls.Add(drawCall);
|
||||
|
||||
// Add draw call to proper draw lists
|
||||
const RenderContext& mainRenderContext = renderContextBatch.Contexts.Get()[0];
|
||||
@@ -524,8 +522,8 @@ namespace
|
||||
void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list, const DrawCall* drawCalls)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
const int32 listSize = (int32)list.Indices.Count();
|
||||
const auto* listData = list.Indices.Get();
|
||||
const int32 listSize = list.Indices.Count();
|
||||
const Float3 planeNormal = renderContext.View.Direction;
|
||||
const float planePoint = -Float3::Dot(planeNormal, renderContext.View.Position);
|
||||
|
||||
@@ -541,7 +539,7 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
|
||||
const uint32 sortKeyXor = reverseDistance ? MAX_uint32 : 0;
|
||||
for (int32 i = 0; i < listSize; i++)
|
||||
{
|
||||
const DrawCall& drawCall = drawCalls[list.Indices[i]];
|
||||
const DrawCall& drawCall = drawCalls[listData[i]];
|
||||
const float distance = Float3::Dot(planeNormal, drawCall.ObjectPosition) - planePoint;
|
||||
const uint32 sortKey = RenderTools::ComputeDistanceSortKey(distance) ^ sortKeyXor;
|
||||
int32 batchKey = GetHash(drawCall.Geometry.IndexBuffer);
|
||||
@@ -571,14 +569,14 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
|
||||
list.Batches.Clear();
|
||||
for (int32 i = 0; i < listSize;)
|
||||
{
|
||||
const DrawCall& drawCall = drawCalls[list.Indices[i]];
|
||||
const DrawCall& drawCall = drawCalls[listData[i]];
|
||||
int32 batchSize = 1;
|
||||
int32 instanceCount = drawCall.InstanceCount;
|
||||
|
||||
// Check the following draw calls to merge them (using instancing)
|
||||
for (int32 j = i + 1; j < listSize; j++)
|
||||
{
|
||||
const DrawCall& other = drawCalls[list.Indices[j]];
|
||||
const DrawCall& other = drawCalls[listData[j]];
|
||||
if (!CanBatchWith(drawCall, other))
|
||||
break;
|
||||
|
||||
@@ -610,6 +608,8 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
if (list.IsEmpty())
|
||||
return;
|
||||
PROFILE_GPU_CPU("Drawing");
|
||||
const auto* listData = list.Indices.Get();
|
||||
const auto* batchesData = list.Batches.Get();
|
||||
const auto context = GPUDevice::Instance->GetMainContext();
|
||||
bool useInstancing = list.CanUseInstancing && CanUseInstancing(renderContext.View.Pass) && GPUDevice::Instance->Limits.HasInstancing;
|
||||
|
||||
@@ -623,7 +623,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
int32 batchesCount = 0;
|
||||
for (int32 i = 0; i < list.Batches.Count(); i++)
|
||||
{
|
||||
auto& batch = list.Batches[i];
|
||||
auto& batch = batchesData[i];
|
||||
if (batch.BatchSize > 1)
|
||||
batchesCount += batch.BatchSize;
|
||||
}
|
||||
@@ -646,14 +646,14 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
|
||||
// Write to instance buffer
|
||||
for (int32 i = 0; i < list.Batches.Count(); i++)
|
||||
{
|
||||
auto& batch = list.Batches[i];
|
||||
auto& batch = batchesData[i];
|
||||
if (batch.BatchSize > 1)
|
||||
{
|
||||
IMaterial::InstancingHandler handler;
|
||||
drawCalls[list.Indices[batch.StartIndex]].Material->CanUseInstancing(handler);
|
||||
drawCalls[listData[batch.StartIndex]].Material->CanUseInstancing(handler);
|
||||
for (int32 j = 0; j < batch.BatchSize; j++)
|
||||
{
|
||||
auto& drawCall = drawCalls[list.Indices[batch.StartIndex + j]];
|
||||
auto& drawCall = drawCalls[listData[batch.StartIndex + j]];
|
||||
handler.WriteDrawCall(instanceData, drawCall);
|
||||
instanceData++;
|
||||
}
|
||||
@@ -685,8 +685,8 @@ DRAW:
|
||||
uint32 vbOffsets[4];
|
||||
for (int32 i = 0; i < list.Batches.Count(); i++)
|
||||
{
|
||||
auto& batch = list.Batches[i];
|
||||
const DrawCall& drawCall = drawCalls[list.Indices[batch.StartIndex]];
|
||||
auto& batch = batchesData[i];
|
||||
const DrawCall& drawCall = drawCalls[listData[batch.StartIndex]];
|
||||
|
||||
int32 vbCount = 0;
|
||||
while (vbCount < ARRAY_COUNT(drawCall.Geometry.VertexBuffers) && drawCall.Geometry.VertexBuffers[vbCount])
|
||||
@@ -789,11 +789,11 @@ DRAW:
|
||||
bindParams.DrawCallsCount = 1;
|
||||
for (int32 i = 0; i < list.Batches.Count(); i++)
|
||||
{
|
||||
auto& batch = list.Batches[i];
|
||||
auto& batch = batchesData[i];
|
||||
|
||||
for (int32 j = 0; j < batch.BatchSize; j++)
|
||||
{
|
||||
const DrawCall& drawCall = drawCalls[list.Indices[batch.StartIndex + j]];
|
||||
const DrawCall& drawCall = drawCalls[listData[batch.StartIndex + j]];
|
||||
bindParams.FirstDrawCall = &drawCall;
|
||||
drawCall.Material->Bind(bindParams);
|
||||
|
||||
@@ -815,10 +815,11 @@ DRAW:
|
||||
auto& batch = BatchedDrawCalls[list.PreBatchedDrawCalls[i]];
|
||||
auto drawCall = batch.DrawCall;
|
||||
bindParams.FirstDrawCall = &drawCall;
|
||||
const auto* instancesData = batch.Instances.Get();
|
||||
|
||||
for (int32 j = 0; j < batch.Instances.Count(); j++)
|
||||
{
|
||||
auto& instance = batch.Instances[j];
|
||||
auto& instance = instancesData[j];
|
||||
drawCall.ObjectPosition = instance.InstanceOrigin;
|
||||
drawCall.PerInstanceRandom = instance.PerInstanceRandom;
|
||||
auto lightmapArea = instance.InstanceLightmapArea.ToFloat4();
|
||||
@@ -840,7 +841,7 @@ DRAW:
|
||||
// Draw calls list has nto been batched so execute draw calls separately
|
||||
for (int32 j = 0; j < list.Indices.Count(); j++)
|
||||
{
|
||||
const DrawCall& drawCall = drawCalls[list.Indices[j]];
|
||||
const DrawCall& drawCall = drawCalls[listData[j]];
|
||||
bindParams.FirstDrawCall = &drawCall;
|
||||
drawCall.Material->Bind(bindParams);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Collections/ConcurrentArray.h"
|
||||
#include "Engine/Core/Math/Half.h"
|
||||
#include "Engine/Graphics/PostProcessSettings.h"
|
||||
#include "Engine/Graphics/DynamicBuffer.h"
|
||||
@@ -236,7 +237,7 @@ struct DrawCallsList
|
||||
/// <summary>
|
||||
/// The list of draw calls indices to render.
|
||||
/// </summary>
|
||||
Array<int32> Indices;
|
||||
ConcurrentArray<int32> Indices;
|
||||
|
||||
/// <summary>
|
||||
/// The list of external draw calls indices to render.
|
||||
@@ -290,7 +291,7 @@ public:
|
||||
/// <summary>
|
||||
/// Draw calls list (for all draw passes).
|
||||
/// </summary>
|
||||
Array<DrawCall> DrawCalls;
|
||||
ConcurrentArray<DrawCall> DrawCalls;
|
||||
|
||||
/// <summary>
|
||||
/// Draw calls list with pre-batched instances (for all draw passes).
|
||||
@@ -481,7 +482,7 @@ public:
|
||||
/// <param name="staticFlags">The object static flags.</param>
|
||||
/// <param name="drawCall">The draw call data.</param>
|
||||
/// <param name="receivesDecals">True if the rendered mesh can receive decals.</param>
|
||||
void AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals);
|
||||
void AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, const DrawCall& drawCall, bool receivesDecals);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the draw call to the draw lists and references it in other render contexts. Performs additional per-context frustum culling.
|
||||
@@ -493,7 +494,7 @@ public:
|
||||
/// <param name="bounds">The object bounds.</param>
|
||||
/// <param name="drawCall">The draw call data.</param>
|
||||
/// <param name="receivesDecals">True if the rendered mesh can receive decals.</param>
|
||||
void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals);
|
||||
void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, const DrawCall& drawCall, bool receivesDecals);
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the collected draw calls list.
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
#include "AntiAliasing/SMAA.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Level/Scene/SceneRendering.h"
|
||||
#include "Engine/Core/Config/GraphicsSettings.h"
|
||||
#include "Engine/Threading/JobSystem.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/QuadOverdrawPass.h"
|
||||
@@ -261,6 +263,9 @@ void Renderer::DrawSceneDepth(GPUContext* context, SceneRenderTask* task, GPUTex
|
||||
// Draw scene actors
|
||||
RenderContextBatch renderContextBatch(renderContext);
|
||||
Level::DrawActors(renderContextBatch);
|
||||
for (const int64 label : renderContextBatch.WaitLabels)
|
||||
JobSystem::Wait(label);
|
||||
renderContextBatch.WaitLabels.Clear();
|
||||
}
|
||||
|
||||
// Sort draw calls
|
||||
@@ -341,7 +346,16 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
if (drawShadows)
|
||||
ShadowsPass::Instance()->SetupShadows(renderContext, renderContextBatch);
|
||||
|
||||
task->OnCollectDrawCalls(renderContextBatch);
|
||||
// Dispatch drawing (via JobSystem - multiple job batches for every scene)
|
||||
JobSystem::SetJobStartingOnDispatch(false);
|
||||
task->OnCollectDrawCalls(renderContextBatch, SceneRendering::DrawCategory::SceneDraw);
|
||||
task->OnCollectDrawCalls(renderContextBatch, SceneRendering::DrawCategory::SceneDrawAsync);
|
||||
|
||||
// Wait for async jobs to finish
|
||||
JobSystem::SetJobStartingOnDispatch(true);
|
||||
for (const int64 label : renderContextBatch.WaitLabels)
|
||||
JobSystem::Wait(label);
|
||||
renderContextBatch.WaitLabels.Clear();
|
||||
}
|
||||
|
||||
// Sort draw calls
|
||||
|
||||
@@ -369,3 +369,12 @@ void JobSystem::SetJobStartingOnDispatch(bool value)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 JobSystem::GetThreadsCount()
|
||||
{
|
||||
#if JOB_SYSTEM_ENABLED
|
||||
return ThreadsCount;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -41,4 +41,9 @@ API_CLASS(Static) class FLAXENGINE_API JobSystem
|
||||
/// Sets whether automatically start jobs execution on Dispatch. If disabled jobs won't be executed until it gets re-enabled. Can be used to optimize execution of multiple dispatches that should overlap.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void SetJobStartingOnDispatch(bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of job system threads.
|
||||
/// </summary>
|
||||
API_PROPERTY() static int32 GetThreadsCount();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user