Merge remote-tracking branch 'origin/gi' into large-worlds
# Conflicts: # Source/Engine/Core/Math/Vector3.h
This commit is contained in:
@@ -324,15 +324,14 @@ void Asset::Reload()
|
||||
Content::AssetReloading(this);
|
||||
OnReloading(this);
|
||||
|
||||
// Fire unload event
|
||||
// TODO: maybe just call release storage ref or sth? we cannot call onUnload because managed asset objects gets invalidated
|
||||
//onUnload_MainThread();
|
||||
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
// Unload current data
|
||||
unload(true);
|
||||
_isLoaded = false;
|
||||
if (IsLoaded())
|
||||
{
|
||||
// Unload current data
|
||||
unload(true);
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
// Start reloading process
|
||||
startLoading();
|
||||
|
||||
@@ -83,7 +83,7 @@ void Animation::ClearCache()
|
||||
|
||||
// Free memory
|
||||
MappingCache.Clear();
|
||||
MappingCache.Cleanup();
|
||||
MappingCache.SetCapacity(0);
|
||||
}
|
||||
|
||||
const Animation::NodeToChannel* Animation::GetMapping(SkinnedModel* obj)
|
||||
|
||||
@@ -48,6 +48,11 @@ const MaterialInfo& Material::GetInfo() const
|
||||
return EmptyInfo;
|
||||
}
|
||||
|
||||
GPUShader* Material::GetShader() const
|
||||
{
|
||||
return _materialShader ? _materialShader->GetShader() : nullptr;
|
||||
}
|
||||
|
||||
bool Material::IsReady() const
|
||||
{
|
||||
return _materialShader && _materialShader->IsReady();
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
|
||||
// [IMaterial]
|
||||
const MaterialInfo& GetInfo() const override;
|
||||
GPUShader* GetShader() const override;
|
||||
bool IsReady() const override;
|
||||
DrawPass GetDrawModes() const override;
|
||||
bool CanUseLightmap() const override;
|
||||
|
||||
@@ -143,6 +143,11 @@ const MaterialInfo& MaterialInstance::GetInfo() const
|
||||
return EmptyInfo;
|
||||
}
|
||||
|
||||
GPUShader* MaterialInstance::GetShader() const
|
||||
{
|
||||
return _baseMaterial ? _baseMaterial->GetShader() : nullptr;
|
||||
}
|
||||
|
||||
bool MaterialInstance::IsReady() const
|
||||
{
|
||||
return IsLoaded() && _baseMaterial && _baseMaterial->IsReady();
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
|
||||
// [IMaterial]
|
||||
const MaterialInfo& GetInfo() const override;
|
||||
GPUShader* GetShader() const override;
|
||||
bool IsReady() const override;
|
||||
DrawPass GetDrawModes() const override;
|
||||
bool CanUseLightmap() const override;
|
||||
|
||||
@@ -7,13 +7,22 @@
|
||||
#include "Engine/Content/WeakAssetReference.h"
|
||||
#include "Engine/Content/Upgraders/ModelAssetUpgrader.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
|
||||
#include "Engine/Streaming/StreamingGroup.h"
|
||||
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/Async/GPUTask.h"
|
||||
#include "Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Tools/ModelTool/ModelTool.h"
|
||||
#include "Engine/Tools/ModelTool/MeshAccelerationStructure.h"
|
||||
#if GPU_ENABLE_ASYNC_RESOURCES_CREATION
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#define STREAM_TASK_BASE ThreadPoolTask
|
||||
@@ -37,18 +46,11 @@ REGISTER_BINARY_ASSET_ABSTRACT(ModelBase, "FlaxEngine.ModelBase");
|
||||
class StreamModelLODTask : public STREAM_TASK_BASE
|
||||
{
|
||||
private:
|
||||
|
||||
WeakAssetReference<Model> _asset;
|
||||
int32 _lodIndex;
|
||||
FlaxStorage::LockData _dataLock;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="model">Parent model</param>
|
||||
/// <param name="lodIndex">LOD to stream index</param>
|
||||
StreamModelLODTask(Model* model, int32 lodIndex)
|
||||
: _asset(model)
|
||||
, _lodIndex(lodIndex)
|
||||
@@ -57,23 +59,16 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [ThreadPoolTask]
|
||||
bool HasReference(Object* resource) const override
|
||||
{
|
||||
return _asset == resource;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [ThreadPoolTask]
|
||||
bool Run() override
|
||||
{
|
||||
AssetReference<Model> model = _asset.Get();
|
||||
if (model == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get data
|
||||
BytesContainer data;
|
||||
@@ -96,6 +91,7 @@ protected:
|
||||
|
||||
// Update residency level
|
||||
model->_loadedLODs++;
|
||||
model->ResidencyChanged();
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -116,11 +112,54 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
class StreamModelSDFTask : public GPUUploadTextureMipTask
|
||||
{
|
||||
private:
|
||||
WeakAssetReference<Model> _asset;
|
||||
FlaxStorage::LockData _dataLock;
|
||||
|
||||
public:
|
||||
StreamModelSDFTask(Model* model, GPUTexture* texture, const Span<byte>& data, int32 mipIndex, int32 rowPitch, int32 slicePitch)
|
||||
: GPUUploadTextureMipTask(texture, mipIndex, data, rowPitch, slicePitch, false)
|
||||
, _asset(model)
|
||||
, _dataLock(model->Storage->Lock())
|
||||
{
|
||||
}
|
||||
|
||||
bool HasReference(Object* resource) const override
|
||||
{
|
||||
return _asset == resource;
|
||||
}
|
||||
|
||||
Result run(GPUTasksContext* context) override
|
||||
{
|
||||
AssetReference<Model> model = _asset.Get();
|
||||
if (model == nullptr)
|
||||
return Result::MissingResources;
|
||||
return GPUUploadTextureMipTask::run(context);
|
||||
}
|
||||
|
||||
void OnEnd() override
|
||||
{
|
||||
_dataLock.Release();
|
||||
|
||||
// Base
|
||||
GPUUploadTextureMipTask::OnEnd();
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_BINARY_ASSET_WITH_UPGRADER(Model, "FlaxEngine.Model", ModelAssetUpgrader, true);
|
||||
|
||||
static byte EnableModelSDF = 0;
|
||||
|
||||
Model::Model(const SpawnParams& params, const AssetInfo* info)
|
||||
: ModelBase(params, info, StreamingGroups::Instance()->Models())
|
||||
{
|
||||
if (EnableModelSDF == 0 && GPUDevice::Instance)
|
||||
{
|
||||
const bool enable = GPUDevice::Instance->GetFeatureLevel() >= FeatureLevel::SM5;
|
||||
EnableModelSDF = enable ? 1 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
Model::~Model()
|
||||
@@ -183,7 +222,7 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
|
||||
if (lodIndex == -1)
|
||||
{
|
||||
// Handling model fade-out transition
|
||||
if (modelFrame == frame && info.DrawState->PrevLOD != -1)
|
||||
if (modelFrame == frame && info.DrawState->PrevLOD != -1 && !renderContext.View.IsSingleFrame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
@@ -212,8 +251,11 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
|
||||
lodIndex += info.LODBias + renderContext.View.ModelLODBias;
|
||||
lodIndex = ClampLODIndex(lodIndex);
|
||||
|
||||
if (renderContext.View.IsSingleFrame)
|
||||
{
|
||||
}
|
||||
// Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports)
|
||||
if (modelFrame == frame)
|
||||
else if (modelFrame == frame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->PrevLOD != lodIndex && info.DrawState->LODTransition == 255)
|
||||
@@ -229,7 +271,7 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
}
|
||||
}
|
||||
// Check if there was a gap between frames in drawing this model instance
|
||||
// Check if there was a gap between frames in drawing this model instance
|
||||
else if (modelFrame < frame || info.DrawState->PrevLOD == -1)
|
||||
{
|
||||
// Reset state
|
||||
@@ -238,7 +280,7 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
|
||||
}
|
||||
|
||||
// Draw
|
||||
if (info.DrawState->PrevLOD == lodIndex)
|
||||
if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame)
|
||||
{
|
||||
LODs[lodIndex].Draw(renderContext, info, 0.0f);
|
||||
}
|
||||
@@ -521,17 +563,50 @@ bool Model::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
lodChunk->Data.Copy(meshesStream.GetHandle(), meshesStream.GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
// Download SDF data
|
||||
if (SDF.Texture)
|
||||
{
|
||||
auto sdfChunk = GET_CHUNK(15);
|
||||
if (sdfChunk == nullptr)
|
||||
return true;
|
||||
MemoryWriteStream sdfStream;
|
||||
sdfStream.WriteInt32(1); // Version
|
||||
ModelSDFHeader data(SDF, SDF.Texture->GetDescription());
|
||||
sdfStream.Write(&data);
|
||||
TextureData sdfTextureData;
|
||||
if (SDF.Texture->DownloadData(sdfTextureData))
|
||||
return true;
|
||||
for (int32 mipLevel = 0; mipLevel < sdfTextureData.Items[0].Mips.Count(); mipLevel++)
|
||||
{
|
||||
auto& mip = sdfTextureData.Items[0].Mips[mipLevel];
|
||||
ModelSDFMip mipData(mipLevel, mip);
|
||||
sdfStream.Write(&mipData);
|
||||
sdfStream.Write(mip.Data.Get(), mip.Data.Length());
|
||||
}
|
||||
sdfChunk->Data.Copy(sdfStream.GetHandle(), sdfStream.GetPosition());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(!IsVirtual());
|
||||
|
||||
// Load all chunks with a mesh data
|
||||
for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++)
|
||||
{
|
||||
if (LoadChunk(MODEL_LOD_TO_CHUNK_INDEX(lodIndex)))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SDF.Texture)
|
||||
{
|
||||
// SDF data from file (only if has no cached texture data)
|
||||
if (LoadChunk(15))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No SDF texture
|
||||
ReleaseChunk(15);
|
||||
}
|
||||
}
|
||||
|
||||
// Set mesh header data
|
||||
@@ -560,6 +635,51 @@ bool Model::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
|
||||
#endif
|
||||
|
||||
bool Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, float backfacesThreshold)
|
||||
{
|
||||
if (EnableModelSDF == 2)
|
||||
return true; // Not supported
|
||||
ScopeLock lock(Locker);
|
||||
if (!HasAnyLODInitialized())
|
||||
return true;
|
||||
if (IsInMainThread() && IsVirtual())
|
||||
{
|
||||
// TODO: could be supported if algorithm could run on a GPU and called during rendering
|
||||
LOG(Warning, "Cannot generate SDF for virtual models on a main thread.");
|
||||
return true;
|
||||
}
|
||||
lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1);
|
||||
|
||||
// Generate SDF
|
||||
#if USE_EDITOR
|
||||
cacheData &= Storage != nullptr; // Cache only if has storage linked
|
||||
MemoryWriteStream sdfStream;
|
||||
MemoryWriteStream* outputStream = cacheData ? &sdfStream : nullptr;
|
||||
#else
|
||||
class MemoryWriteStream* outputStream = nullptr;
|
||||
#endif
|
||||
if (ModelTool::GenerateModelSDF(this, nullptr, resolutionScale, lodIndex, &SDF, outputStream, GetPath(), backfacesThreshold))
|
||||
return true;
|
||||
|
||||
#if USE_EDITOR
|
||||
// Set asset data
|
||||
if (cacheData)
|
||||
GetOrCreateChunk(15)->Data.Copy(sdfStream.GetHandle(), sdfStream.GetPosition());
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Model::SetSDF(const SDFData& sdf)
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
if (SDF.Texture == sdf.Texture)
|
||||
return;
|
||||
SAFE_DELETE_GPU_RESOURCE(SDF.Texture);
|
||||
SDF = sdf;
|
||||
ReleaseChunk(15);
|
||||
}
|
||||
|
||||
bool Model::Init(const Span<int32>& meshesCountPerLod)
|
||||
{
|
||||
if (meshesCountPerLod.IsInvalid() || meshesCountPerLod.Length() > MODEL_MAX_LODS)
|
||||
@@ -574,14 +694,12 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
|
||||
// Setup
|
||||
MaterialSlots.Resize(1);
|
||||
MinScreenSize = 0.0f;
|
||||
SAFE_DELETE_GPU_RESOURCE(SDF.Texture);
|
||||
|
||||
// Setup LODs
|
||||
for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++)
|
||||
{
|
||||
LODs[lodIndex].Dispose();
|
||||
}
|
||||
LODs.Resize(meshesCountPerLod.Length());
|
||||
_loadedLODs = meshesCountPerLod.Length();
|
||||
|
||||
// Setup meshes
|
||||
for (int32 lodIndex = 0; lodIndex < meshesCountPerLod.Length(); lodIndex++)
|
||||
@@ -600,6 +718,10 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
|
||||
}
|
||||
}
|
||||
|
||||
// Update resource residency
|
||||
_loadedLODs = meshesCountPerLod.Length();
|
||||
ResidencyChanged();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -716,6 +838,7 @@ Task* Model::CreateStreamingTask(int32 residency)
|
||||
for (int32 i = HighestResidentLODIndex(); i < LODs.Count() - residency; i++)
|
||||
LODs[i].Unload();
|
||||
_loadedLODs = residency;
|
||||
ResidencyChanged();
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -810,6 +933,47 @@ Asset::LoadResult Model::load()
|
||||
}
|
||||
}
|
||||
|
||||
// Load SDF
|
||||
auto chunk15 = GetChunk(15);
|
||||
if (chunk15 && chunk15->IsLoaded() && EnableModelSDF == 1)
|
||||
{
|
||||
MemoryReadStream sdfStream(chunk15->Get(), chunk15->Size());
|
||||
int32 version;
|
||||
sdfStream.ReadInt32(&version);
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
ModelSDFHeader data;
|
||||
sdfStream.Read(&data);
|
||||
if (!SDF.Texture)
|
||||
SDF.Texture = GPUTexture::New();
|
||||
if (SDF.Texture->Init(GPUTextureDescription::New3D(data.Width, data.Height, data.Depth, data.Format, GPUTextureFlags::ShaderResource, data.MipLevels)))
|
||||
return LoadResult::Failed;
|
||||
SDF.LocalToUVWMul = data.LocalToUVWMul;
|
||||
SDF.LocalToUVWAdd = data.LocalToUVWAdd;
|
||||
SDF.WorldUnitsPerVoxel = data.WorldUnitsPerVoxel;
|
||||
SDF.MaxDistance = data.MaxDistance;
|
||||
SDF.LocalBoundsMin = data.LocalBoundsMin;
|
||||
SDF.LocalBoundsMax = data.LocalBoundsMax;
|
||||
SDF.ResolutionScale = data.ResolutionScale;
|
||||
SDF.LOD = data.LOD;
|
||||
for (int32 mipLevel = 0; mipLevel < data.MipLevels; mipLevel++)
|
||||
{
|
||||
ModelSDFMip mipData;
|
||||
sdfStream.Read(&mipData);
|
||||
void* mipBytes = sdfStream.Read(mipData.SlicePitch);
|
||||
auto task = ::New<StreamModelSDFTask>(this, SDF.Texture, Span<byte>((byte*)mipBytes, mipData.SlicePitch), mipData.MipIndex, mipData.RowPitch, mipData.SlicePitch);
|
||||
task->Start();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG(Warning, "Unknown SDF data version {0} in {1}", version, ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILD_DEBUG || BUILD_DEVELOPMENT
|
||||
// Validate LODs
|
||||
for (int32 lodIndex = 1; lodIndex < LODs.Count(); lodIndex++)
|
||||
@@ -840,6 +1004,7 @@ void Model::unload(bool isReloading)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
SAFE_DELETE_GPU_RESOURCE(SDF.Texture);
|
||||
MaterialSlots.Resize(0);
|
||||
for (int32 i = 0; i < LODs.Count(); i++)
|
||||
LODs[i].Dispose();
|
||||
@@ -862,7 +1027,7 @@ bool Model::init(AssetInitData& initData)
|
||||
AssetChunksFlag Model::getChunksToPreload() const
|
||||
{
|
||||
// Note: we don't preload any LODs here because it's done by the Streaming Manager
|
||||
return GET_CHUNK_FLAG(0);
|
||||
return GET_CHUNK_FLAG(0) | GET_CHUNK_FLAG(15);
|
||||
}
|
||||
|
||||
void ModelBase::SetupMaterialSlots(int32 slotsCount)
|
||||
|
||||
@@ -28,6 +28,11 @@ public:
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) Array<ModelLOD, FixedAllocation<MODEL_MAX_LODS>> LODs;
|
||||
|
||||
/// <summary>
|
||||
/// The generated Sign Distant Field (SDF) for this model (merged all meshes). Use GenerateSDF to update it.
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) SDFData SDF;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
@@ -200,6 +205,22 @@ public:
|
||||
API_FUNCTION() bool Save(bool withMeshDataFromGpu = false, const StringView& path = StringView::Empty);
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Generates the Sign Distant Field for this model.
|
||||
/// </summary>
|
||||
/// <remarks>Can be called in async in case of SDF generation on a CPU (assuming model is not during rendering).</remarks>
|
||||
/// <param name="resolutionScale">The SDF texture resolution scale. Use higher values for more precise data but with significant performance and memory overhead.</param>
|
||||
/// <param name="lodIndex">The index of the LOD to use for the SDF building.</param>
|
||||
/// <param name="cacheData">If true, the generated SDF texture data will be cached on CPU (in asset chunk storage) to allow saving it later, otherwise it will be runtime for GPU-only. Ignored for virtual assets or in build.</param>
|
||||
/// <param name="backfacesThreshold">Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
API_FUNCTION() bool GenerateSDF(float resolutionScale = 1.0f, int32 lodIndex = 6, bool cacheData = true, float backfacesThreshold = 0.6f);
|
||||
|
||||
/// <summary>
|
||||
/// Sets set SDF data (releases the current one).
|
||||
/// </summary>
|
||||
API_FUNCTION() void SetSDF(const SDFData& sdf);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// Chunk 1: LOD0
|
||||
// Chunk 2: LOD1
|
||||
// ..
|
||||
//
|
||||
// Chunk 15: SDF
|
||||
#define MODEL_LOD_TO_CHUNK_INDEX(lod) (lod + 1)
|
||||
|
||||
class MeshBase;
|
||||
@@ -24,9 +24,62 @@ class MeshBase;
|
||||
/// </summary>
|
||||
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API ModelBase : public BinaryAsset, public StreamableResource
|
||||
{
|
||||
DECLARE_ASSET_HEADER(ModelBase);
|
||||
protected:
|
||||
DECLARE_ASSET_HEADER(ModelBase);
|
||||
public:
|
||||
/// <summary>
|
||||
/// The Sign Distant Field (SDF) data for the model.
|
||||
/// </summary>
|
||||
API_STRUCT() struct SDFData
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(SDFData);
|
||||
|
||||
/// <summary>
|
||||
/// The SDF volume texture (merged all meshes).
|
||||
/// </summary>
|
||||
API_FIELD() GPUTexture* Texture = nullptr;
|
||||
|
||||
/// <summary>
|
||||
/// The transformation scale from model local-space to the generated SDF texture space (local-space -> uv).
|
||||
/// </summary>
|
||||
API_FIELD() Vector3 LocalToUVWMul;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of world-units per SDF texture voxel.
|
||||
/// </summary>
|
||||
API_FIELD() float WorldUnitsPerVoxel;
|
||||
|
||||
/// <summary>
|
||||
/// The transformation offset from model local-space to the generated SDF texture space (local-space -> uv).
|
||||
/// </summary>
|
||||
API_FIELD() Vector3 LocalToUVWAdd;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum distance stored in the SDF texture. Used to rescale normalized SDF into world-units (in model local space).
|
||||
/// </summary>
|
||||
API_FIELD() float MaxDistance;
|
||||
|
||||
/// <summary>
|
||||
/// The bounding box of the SDF texture in the model local-space.
|
||||
/// </summary>
|
||||
API_FIELD() Vector3 LocalBoundsMin;
|
||||
|
||||
/// <summary>
|
||||
/// The SDF texture resolution scale used for building texture.
|
||||
/// </summary>
|
||||
API_FIELD() float ResolutionScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The bounding box of the SDF texture in the model local-space.
|
||||
/// </summary>
|
||||
API_FIELD() Vector3 LocalBoundsMax;
|
||||
|
||||
/// <summary>
|
||||
/// The model LOD index used for the building.
|
||||
/// </summary>
|
||||
API_FIELD() int32 LOD = 6;
|
||||
};
|
||||
|
||||
protected:
|
||||
explicit ModelBase(const SpawnParams& params, const AssetInfo* info, StreamingGroup* group)
|
||||
: BinaryAsset(params, info)
|
||||
, StreamableResource(group)
|
||||
@@ -34,7 +87,6 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The minimum screen size to draw this model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.
|
||||
/// </summary>
|
||||
|
||||
@@ -30,13 +30,11 @@
|
||||
class StreamSkinnedModelLODTask : public ThreadPoolTask
|
||||
{
|
||||
private:
|
||||
|
||||
WeakAssetReference<SkinnedModel> _asset;
|
||||
int32 _lodIndex;
|
||||
FlaxStorage::LockData _dataLock;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
@@ -50,7 +48,6 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [ThreadPoolTask]
|
||||
bool HasReference(Object* resource) const override
|
||||
{
|
||||
@@ -58,7 +55,6 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [ThreadPoolTask]
|
||||
bool Run() override
|
||||
{
|
||||
@@ -89,6 +85,7 @@ protected:
|
||||
|
||||
// Update residency level
|
||||
model->_loadedLODs++;
|
||||
model->ResidencyChanged();
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -191,7 +188,7 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf
|
||||
if (lodIndex == -1)
|
||||
{
|
||||
// Handling model fade-out transition
|
||||
if (modelFrame == frame && info.DrawState->PrevLOD != -1)
|
||||
if (modelFrame == frame && info.DrawState->PrevLOD != -1 && !renderContext.View.IsSingleFrame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
@@ -220,8 +217,11 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf
|
||||
lodIndex += info.LODBias + renderContext.View.ModelLODBias;
|
||||
lodIndex = ClampLODIndex(lodIndex);
|
||||
|
||||
if (renderContext.View.IsSingleFrame)
|
||||
{
|
||||
}
|
||||
// Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports)
|
||||
if (modelFrame == frame)
|
||||
else if (modelFrame == frame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->PrevLOD != lodIndex && info.DrawState->LODTransition == 255)
|
||||
@@ -237,7 +237,7 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
}
|
||||
}
|
||||
// Check if there was a gap between frames in drawing this model instance
|
||||
// Check if there was a gap between frames in drawing this model instance
|
||||
else if (modelFrame < frame || info.DrawState->PrevLOD == -1)
|
||||
{
|
||||
// Reset state
|
||||
@@ -246,7 +246,7 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf
|
||||
}
|
||||
|
||||
// Draw
|
||||
if (info.DrawState->PrevLOD == lodIndex)
|
||||
if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame)
|
||||
{
|
||||
LODs[lodIndex].Draw(renderContext, info, 0.0f);
|
||||
}
|
||||
@@ -677,11 +677,8 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
|
||||
|
||||
// Setup LODs
|
||||
for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++)
|
||||
{
|
||||
LODs[lodIndex].Dispose();
|
||||
}
|
||||
LODs.Resize(meshesCountPerLod.Length());
|
||||
_loadedLODs = meshesCountPerLod.Length();
|
||||
|
||||
// Setup meshes
|
||||
for (int32 lodIndex = 0; lodIndex < meshesCountPerLod.Length(); lodIndex++)
|
||||
@@ -700,6 +697,10 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
|
||||
}
|
||||
}
|
||||
|
||||
// Update resource residency
|
||||
_loadedLODs = meshesCountPerLod.Length();
|
||||
ResidencyChanged();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -828,6 +829,7 @@ Task* SkinnedModel::CreateStreamingTask(int32 residency)
|
||||
for (int32 i = HighestResidentLODIndex(); i < LODs.Count() - residency; i++)
|
||||
LODs[i].Unload();
|
||||
_loadedLODs = residency;
|
||||
ResidencyChanged();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -18,6 +18,7 @@ public class Content : EngineModule
|
||||
options.PrivateDependencies.Add("lz4");
|
||||
options.PrivateDependencies.Add("AudioTool");
|
||||
options.PrivateDependencies.Add("TextureTool");
|
||||
options.PrivateDependencies.Add("ModelTool");
|
||||
options.PrivateDependencies.Add("Particles");
|
||||
|
||||
if (options.Target.IsEditor)
|
||||
|
||||
@@ -61,13 +61,13 @@ namespace
|
||||
return &node;
|
||||
}
|
||||
|
||||
ShaderGraphNode<>* AddTextureNode(MaterialLayer* layer, const Guid& textureId)
|
||||
ShaderGraphNode<>* AddTextureNode(MaterialLayer* layer, const Guid& textureId, bool normalMap = false)
|
||||
{
|
||||
if (!textureId.IsValid())
|
||||
return nullptr;
|
||||
auto& node = layer->Graph.Nodes.AddOne();
|
||||
node.ID = layer->Graph.Nodes.Count();
|
||||
node.Type = GRAPH_NODE_MAKE_TYPE(5, 1);
|
||||
node.Type = GRAPH_NODE_MAKE_TYPE(5, normalMap ? 4 : 1);
|
||||
node.Boxes.Resize(7);
|
||||
node.Boxes[0] = MaterialGraphBox(&node, 0, VariantType::Vector2); // UVs
|
||||
node.Boxes[6] = MaterialGraphBox(&node, 6, VariantType::Object); // Texture Reference
|
||||
@@ -178,7 +178,7 @@ CreateAssetResult CreateMaterial::Create(CreateAssetContext& context)
|
||||
CONNECT(layer->Root->Boxes[static_cast<int32>(MaterialGraphBoxes::Emissive)], emissiveColor->Boxes[0]);
|
||||
SET_POS(emissiveColor, Vector2(-493.5272f, -2.926111f));
|
||||
}
|
||||
auto normalMap = AddTextureNode(layer, options.Normals.Texture);
|
||||
auto normalMap = AddTextureNode(layer, options.Normals.Texture, true);
|
||||
if (normalMap)
|
||||
{
|
||||
CONNECT(layer->Root->Boxes[static_cast<int32>(MaterialGraphBoxes::Normal)], normalMap->Boxes[1]);
|
||||
|
||||
@@ -48,9 +48,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
static CreateAssetResult ImportModel(CreateAssetContext& context, ModelData& modelData);
|
||||
static CreateAssetResult ImportSkinnedModel(CreateAssetContext& context, ModelData& modelData);
|
||||
static CreateAssetResult ImportAnimation(CreateAssetContext& context, ModelData& modelData);
|
||||
static CreateAssetResult ImportModel(CreateAssetContext& context, ModelData& modelData, const Options* options = nullptr);
|
||||
static CreateAssetResult ImportSkinnedModel(CreateAssetContext& context, ModelData& modelData, const Options* options = nullptr);
|
||||
static CreateAssetResult ImportAnimation(CreateAssetContext& context, ModelData& modelData, const Options* options = nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -150,13 +150,13 @@ CreateAssetResult ImportModelFile::Import(CreateAssetContext& context)
|
||||
switch (options.Type)
|
||||
{
|
||||
case ModelTool::ModelType::Model:
|
||||
result = ImportModel(context, modelData);
|
||||
result = ImportModel(context, modelData, &options);
|
||||
break;
|
||||
case ModelTool::ModelType::SkinnedModel:
|
||||
result = ImportSkinnedModel(context, modelData);
|
||||
result = ImportSkinnedModel(context, modelData, &options);
|
||||
break;
|
||||
case ModelTool::ModelType::Animation:
|
||||
result = ImportAnimation(context, modelData);
|
||||
result = ImportAnimation(context, modelData, &options);
|
||||
break;
|
||||
}
|
||||
if (result != CreateAssetResult::Ok)
|
||||
@@ -199,7 +199,7 @@ CreateAssetResult ImportModelFile::Create(CreateAssetContext& context)
|
||||
return ImportModel(context, modelData);
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModelFile::ImportModel(CreateAssetContext& context, ModelData& modelData)
|
||||
CreateAssetResult ImportModelFile::ImportModel(CreateAssetContext& context, ModelData& modelData, const Options* options)
|
||||
{
|
||||
// Base
|
||||
IMPORT_SETUP(Model, Model::SerializedVersion);
|
||||
@@ -235,10 +235,22 @@ CreateAssetResult ImportModelFile::ImportModel(CreateAssetContext& context, Mode
|
||||
context.Data.Header.Chunks[chunkIndex]->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
||||
}
|
||||
|
||||
// Generate SDF
|
||||
if (options && options->GenerateSDF)
|
||||
{
|
||||
stream.SetPosition(0);
|
||||
if (!ModelTool::GenerateModelSDF(nullptr, &modelData, options->SDFResolution, lodCount - 1, nullptr, &stream, context.TargetAssetPath))
|
||||
{
|
||||
if (context.AllocateChunk(15))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
context.Data.Header.Chunks[15]->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
return CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModelFile::ImportSkinnedModel(CreateAssetContext& context, ModelData& modelData)
|
||||
CreateAssetResult ImportModelFile::ImportSkinnedModel(CreateAssetContext& context, ModelData& modelData, const Options* options)
|
||||
{
|
||||
// Base
|
||||
IMPORT_SETUP(SkinnedModel, SkinnedModel::SerializedVersion);
|
||||
@@ -277,7 +289,7 @@ CreateAssetResult ImportModelFile::ImportSkinnedModel(CreateAssetContext& contex
|
||||
return CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModelFile::ImportAnimation(CreateAssetContext& context, ModelData& modelData)
|
||||
CreateAssetResult ImportModelFile::ImportAnimation(CreateAssetContext& context, ModelData& modelData, const Options* options)
|
||||
{
|
||||
// Base
|
||||
IMPORT_SETUP(Animation, Animation::SerializedVersion);
|
||||
|
||||
@@ -381,10 +381,10 @@ public:
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
auto bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket->Occupy(key);
|
||||
Bucket& bucket = _allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket.Occupy(key);
|
||||
_elementsCount++;
|
||||
return bucket->Value;
|
||||
return bucket.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -484,7 +484,7 @@ public:
|
||||
#endif
|
||||
void ClearDelete()
|
||||
{
|
||||
for (auto i = Begin(); i.IsNotEnd(); ++i)
|
||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value)
|
||||
Delete(i->Value);
|
||||
@@ -547,22 +547,15 @@ public:
|
||||
/// Ensures that collection has given capacity.
|
||||
/// </summary>
|
||||
/// <param name="minCapacity">The minimum required capacity.</param>
|
||||
void EnsureCapacity(int32 minCapacity)
|
||||
/// <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)
|
||||
{
|
||||
if (_size >= minCapacity)
|
||||
return;
|
||||
if (minCapacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
minCapacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
SetCapacity(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup collection data (changes size to 0 without data preserving).
|
||||
/// </summary>
|
||||
FORCE_INLINE void Cleanup()
|
||||
{
|
||||
SetCapacity(0, false);
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -642,7 +635,7 @@ public:
|
||||
void Add(const Iterator& i)
|
||||
{
|
||||
ASSERT(&i._collection != this && i);
|
||||
Bucket& bucket = *i;
|
||||
const Bucket& bucket = *i;
|
||||
Add(bucket.Key, bucket.Value);
|
||||
}
|
||||
|
||||
@@ -655,11 +648,9 @@ public:
|
||||
bool Remove(const KeyComparableType& key)
|
||||
{
|
||||
if (IsEmpty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
if (pos.ObjectIndex != -1)
|
||||
{
|
||||
_allocation.Get()[pos.ObjectIndex].Delete();
|
||||
@@ -697,7 +688,7 @@ public:
|
||||
int32 RemoveValue(const ValueType& value)
|
||||
{
|
||||
int32 result = 0;
|
||||
for (auto i = Begin(); i.IsNotEnd(); ++i)
|
||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value == value)
|
||||
{
|
||||
@@ -718,16 +709,11 @@ public:
|
||||
template<typename KeyComparableType>
|
||||
Iterator Find(const KeyComparableType& key) const
|
||||
{
|
||||
if (HasItems())
|
||||
{
|
||||
const Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
{
|
||||
if (data[i].IsOccupied() && data[i].Key == key)
|
||||
return Iterator(*this, i);
|
||||
}
|
||||
}
|
||||
return End();
|
||||
if (IsEmpty())
|
||||
return End();
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -798,7 +784,7 @@ public:
|
||||
{
|
||||
Clear();
|
||||
SetCapacity(other.Capacity(), false);
|
||||
for (auto i = other.Begin(); i != other.End(); ++i)
|
||||
for (Iterator i = other.Begin(); i != other.End(); ++i)
|
||||
Add(i);
|
||||
ASSERT(Count() == other.Count());
|
||||
ASSERT(Capacity() == other.Capacity());
|
||||
@@ -811,7 +797,7 @@ public:
|
||||
template<typename ArrayAllocation>
|
||||
void GetKeys(Array<KeyType, ArrayAllocation>& result) const
|
||||
{
|
||||
for (auto i = Begin(); i.IsNotEnd(); ++i)
|
||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||
result.Add(i->Key);
|
||||
}
|
||||
|
||||
@@ -822,7 +808,7 @@ public:
|
||||
template<typename ArrayAllocation>
|
||||
void GetValues(Array<ValueType, ArrayAllocation>& result) const
|
||||
{
|
||||
for (auto i = Begin(); i.IsNotEnd(); ++i)
|
||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||
result.Add(i->Value);
|
||||
}
|
||||
|
||||
@@ -897,7 +883,7 @@ protected:
|
||||
while (checksCount < _size)
|
||||
{
|
||||
// Empty bucket
|
||||
auto& bucket = data[bucketIndex];
|
||||
const Bucket& bucket = data[bucketIndex];
|
||||
if (bucket.IsEmpty())
|
||||
{
|
||||
// Found place to insert
|
||||
|
||||
@@ -2,78 +2,62 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Core.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "HashFunctions.h"
|
||||
#include "Config.h"
|
||||
#include "Engine/Core/Memory/Memory.h"
|
||||
#include "Engine/Core/Memory/Allocation.h"
|
||||
#include "Engine/Core/Collections/HashFunctions.h"
|
||||
#include "Engine/Core/Collections/Config.h"
|
||||
|
||||
/// <summary>
|
||||
/// Template for unordered set of values (without duplicates with O(1) lookup access).
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the set.</typeparam>
|
||||
template<typename T>
|
||||
/// <typeparam name="AllocationType">The type of memory allocator.</typeparam>
|
||||
template<typename T, typename AllocationType = HeapAllocation>
|
||||
API_CLASS(InBuild) class HashSet
|
||||
{
|
||||
friend HashSet;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Describes single portion of space for the item in a hash map
|
||||
/// Describes single portion of space for the item in a hash map.
|
||||
/// </summary>
|
||||
struct Bucket
|
||||
{
|
||||
friend HashSet;
|
||||
|
||||
public:
|
||||
|
||||
enum State : byte
|
||||
{
|
||||
Empty,
|
||||
Deleted,
|
||||
Occupied,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/// <summary>The item.</summary>
|
||||
T Item;
|
||||
|
||||
private:
|
||||
|
||||
State _state;
|
||||
|
||||
public:
|
||||
|
||||
Bucket()
|
||||
: _state(Empty)
|
||||
{
|
||||
}
|
||||
|
||||
~Bucket()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void Free()
|
||||
{
|
||||
if (_state == Occupied)
|
||||
Memory::DestructItem(&Item);
|
||||
_state = Empty;
|
||||
}
|
||||
|
||||
void Delete()
|
||||
{
|
||||
_state = Deleted;
|
||||
Memory::DestructItem(&Item);
|
||||
}
|
||||
|
||||
void Occupy(const T& item)
|
||||
template<typename ItemType>
|
||||
void Occupy(const ItemType& item)
|
||||
{
|
||||
Item = item;
|
||||
Memory::ConstructItems(&Item, &item, 1);
|
||||
_state = Occupied;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
FORCE_INLINE bool IsEmpty() const
|
||||
{
|
||||
return _state == Empty;
|
||||
@@ -94,13 +78,15 @@ public:
|
||||
return _state != Occupied;
|
||||
}
|
||||
};
|
||||
|
||||
typedef typename AllocationType::template Data<Bucket> AllocationData;
|
||||
|
||||
private:
|
||||
|
||||
int32 _elementsCount = 0;
|
||||
int32 _deletedCount = 0;
|
||||
int32 _tableSize = 0;
|
||||
Bucket* _table = nullptr;
|
||||
int32 _size = 0;
|
||||
AllocationData _allocation;
|
||||
|
||||
public:
|
||||
|
||||
@@ -117,10 +103,27 @@ public:
|
||||
/// <param name="capacity">The initial capacity.</param>
|
||||
HashSet(int32 capacity)
|
||||
{
|
||||
ASSERT(capacity >= 0);
|
||||
SetCapacity(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HashSet"/> class.
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
HashSet(HashSet&& other) noexcept
|
||||
: _elementsCount(other._elementsCount)
|
||||
, _deletedCount(other._deletedCount)
|
||||
, _size(other._size)
|
||||
{
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
_size = other._size;
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HashSet"/> class.
|
||||
/// </summary>
|
||||
@@ -137,18 +140,39 @@ public:
|
||||
/// <returns>The reference to this.</returns>
|
||||
HashSet& operator=(const HashSet& other)
|
||||
{
|
||||
// Ensure we're not trying to set to itself
|
||||
if (this != &other)
|
||||
Clone(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the data from the other collection.
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
/// <returns>The reference to this.</returns>
|
||||
HashSet& operator=(HashSet&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
Clear();
|
||||
_allocation.Free();
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
_size = other._size;
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="HashSet"/> class.
|
||||
/// </summary>
|
||||
~HashSet()
|
||||
{
|
||||
Cleanup();
|
||||
SetCapacity(0, false);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -156,7 +180,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the amount of the elements in the collection.
|
||||
/// </summary>
|
||||
/// <returns>The amount of elements in the collection.</returns>
|
||||
FORCE_INLINE int32 Count() const
|
||||
{
|
||||
return _elementsCount;
|
||||
@@ -165,16 +188,14 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the amount of the elements that can be contained by the collection.
|
||||
/// </summary>
|
||||
/// <returns>The capacity of the collection.</returns>
|
||||
FORCE_INLINE int32 Capacity() const
|
||||
{
|
||||
return _tableSize;
|
||||
return _size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if collection is empty.
|
||||
/// </summary>
|
||||
/// <returns>True if is empty, otherwise false.</returns>
|
||||
FORCE_INLINE bool IsEmpty() const
|
||||
{
|
||||
return _elementsCount == 0;
|
||||
@@ -183,7 +204,6 @@ public:
|
||||
/// <summary>
|
||||
/// Returns true if collection has one or more elements.
|
||||
/// </summary>
|
||||
/// <returns>True if isn't empty, otherwise false.</returns>
|
||||
FORCE_INLINE bool HasItems() const
|
||||
{
|
||||
return _elementsCount != 0;
|
||||
@@ -197,9 +217,7 @@ public:
|
||||
struct Iterator
|
||||
{
|
||||
friend HashSet;
|
||||
|
||||
private:
|
||||
|
||||
HashSet& _collection;
|
||||
int32 _index;
|
||||
|
||||
@@ -223,41 +241,37 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
Iterator(Iterator&& i)
|
||||
: _collection(i._collection)
|
||||
, _index(i._index)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Checks if iterator is in the end of the collection
|
||||
/// </summary>
|
||||
/// <returns>True if is in the end, otherwise false</returns>
|
||||
FORCE_INLINE bool IsEnd() const
|
||||
{
|
||||
return _index == _collection.Capacity();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if iterator is not in the end of the collection
|
||||
/// </summary>
|
||||
/// <returns>True if is not in the end, otherwise false</returns>
|
||||
FORCE_INLINE bool IsNotEnd() const
|
||||
{
|
||||
return _index != _collection.Capacity();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
FORCE_INLINE Bucket& operator*() const
|
||||
{
|
||||
return _collection._table[_index];
|
||||
return _collection._allocation.Get()[_index];
|
||||
}
|
||||
|
||||
FORCE_INLINE Bucket* operator->() const
|
||||
{
|
||||
return &_collection._table[_index];
|
||||
return &_collection._allocation.Get()[_index];
|
||||
}
|
||||
|
||||
FORCE_INLINE explicit operator bool() const
|
||||
{
|
||||
return _index >= 0 && _index < _collection._tableSize;
|
||||
return _index >= 0 && _index < _collection._size;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator !() const
|
||||
@@ -275,17 +289,16 @@ public:
|
||||
return _index != v._index || &_collection != &v._collection;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
const int32 capacity = _collection.Capacity();
|
||||
if (_index != capacity)
|
||||
{
|
||||
const Bucket* data = _collection._allocation.Get();
|
||||
do
|
||||
{
|
||||
_index++;
|
||||
} while (_index != capacity && _collection._table[_index].IsNotOccupied());
|
||||
} while (_index != capacity && data[_index].IsNotOccupied());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -301,10 +314,11 @@ public:
|
||||
{
|
||||
if (_index > 0)
|
||||
{
|
||||
const Bucket* data = _collection._allocation.Get();
|
||||
do
|
||||
{
|
||||
_index--;
|
||||
} while (_index > 0 && _collection._table[_index].IsNotOccupied());
|
||||
} while (_index > 0 && data[_index].IsNotOccupied());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -324,22 +338,25 @@ public:
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
if (_table)
|
||||
if (_elementsCount + _deletedCount != 0)
|
||||
{
|
||||
// Free all buckets
|
||||
// Note: this will not clear allocated objects space!
|
||||
for (int32 i = 0; i < _tableSize; i++)
|
||||
_table[i].Free();
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i].Free();
|
||||
_elementsCount = _deletedCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the collection and delete value objects.
|
||||
/// Clears the collection and delete value objects.
|
||||
/// Note: collection must contain pointers to the objects that have public destructor and be allocated using New method.
|
||||
/// </summary>
|
||||
#if defined(_MSC_VER)
|
||||
template<typename = typename TEnableIf<TIsPointer<T>::Value>::Type>
|
||||
#endif
|
||||
void ClearDelete()
|
||||
{
|
||||
for (auto i = Begin(); i.IsNotEnd(); ++i)
|
||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value)
|
||||
::Delete(i->Value);
|
||||
@@ -354,86 +371,63 @@ public:
|
||||
/// <param name="preserveContents">Enable/disable preserving collection contents during resizing</param>
|
||||
void SetCapacity(int32 capacity, bool preserveContents = true)
|
||||
{
|
||||
// Validate input
|
||||
ASSERT(capacity >= 0);
|
||||
|
||||
// Check if capacity won't change
|
||||
if (capacity == Capacity())
|
||||
return;
|
||||
|
||||
// Cache previous state
|
||||
auto oldTable = _table;
|
||||
auto oldTableSize = _tableSize;
|
||||
|
||||
// Clear elements counters
|
||||
const auto oldElementsCount = _elementsCount;
|
||||
ASSERT(capacity >= 0);
|
||||
AllocationData oldAllocation;
|
||||
oldAllocation.Swap(_allocation);
|
||||
const int32 oldSize = _size;
|
||||
const int32 oldElementsCount = _elementsCount;
|
||||
_deletedCount = _elementsCount = 0;
|
||||
|
||||
// Check if need to create a new table
|
||||
if (capacity > 0)
|
||||
if (capacity != 0 && (capacity & (capacity - 1)) != 0)
|
||||
{
|
||||
// Align capacity value
|
||||
if (Math::IsPowerOfTwo(capacity) == false)
|
||||
capacity = Math::RoundUpToPowerOf2(capacity);
|
||||
|
||||
// Allocate new table
|
||||
_table = NewArray<Bucket>(capacity);
|
||||
_tableSize = capacity;
|
||||
|
||||
// Check if preserve content
|
||||
if (oldElementsCount != 0 && preserveContents)
|
||||
// Align capacity value to the next power of two (if it's not)
|
||||
capacity++;
|
||||
capacity |= capacity >> 1;
|
||||
capacity |= capacity >> 2;
|
||||
capacity |= capacity >> 4;
|
||||
capacity |= capacity >> 8;
|
||||
capacity |= capacity >> 16;
|
||||
capacity = capacity + 1;
|
||||
}
|
||||
if (capacity)
|
||||
{
|
||||
_allocation.Allocate(capacity);
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < capacity; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
}
|
||||
_size = capacity;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
if (oldElementsCount != 0 && preserveContents)
|
||||
{
|
||||
// TODO; move keys and values on realloc
|
||||
for (int32 i = 0; i < oldSize; i++)
|
||||
{
|
||||
// Try to preserve all values in the collection
|
||||
for (int32 i = 0; i < oldTableSize; i++)
|
||||
{
|
||||
if (oldTable[i].IsOccupied())
|
||||
Add(oldTable[i].Item);
|
||||
}
|
||||
if (oldData[i].IsOccupied())
|
||||
Add(oldData[i].Item);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (oldElementsCount != 0)
|
||||
{
|
||||
// Clear data
|
||||
_table = nullptr;
|
||||
_tableSize = 0;
|
||||
}
|
||||
ASSERT(preserveContents == false || _elementsCount == oldElementsCount);
|
||||
|
||||
// Delete old table
|
||||
if (oldTable)
|
||||
{
|
||||
DeleteArray(oldTable, oldTableSize);
|
||||
for (int32 i = 0; i < oldSize; i++)
|
||||
oldData[i].Free();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases collection capacity by given extra size (content will be preserved)
|
||||
/// Ensures that collection has given capacity.
|
||||
/// </summary>
|
||||
/// <param name="extraSize">Extra size to enlarge collection</param>
|
||||
FORCE_INLINE void IncreaseCapacity(int32 extraSize)
|
||||
{
|
||||
ASSERT(extraSize >= 0);
|
||||
SetCapacity(Capacity() + extraSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that collection has given capacity
|
||||
/// </summary>
|
||||
/// <param name="minCapacity">Minimum required capacity</param>
|
||||
void EnsureCapacity(int32 minCapacity)
|
||||
/// <param name="minCapacity">The minimum required 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)
|
||||
{
|
||||
if (Capacity() >= minCapacity)
|
||||
return;
|
||||
int32 num = Capacity() == 0 ? DICTIONARY_DEFAULT_CAPACITY : Capacity() * 2;
|
||||
SetCapacity(Math::Clamp<int32>(num, minCapacity, MAX_int32 - 1410));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup collection data (changes size to 0 without data preserving)
|
||||
/// </summary>
|
||||
FORCE_INLINE void Cleanup()
|
||||
{
|
||||
SetCapacity(0, false);
|
||||
if (minCapacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
minCapacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -443,7 +437,8 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="item">The element to add to the set.</param>
|
||||
/// <returns>True if element has been added to the collection, otherwise false if the element is already present.</returns>
|
||||
bool Add(const T& item)
|
||||
template<typename ItemType>
|
||||
bool Add(const ItemType& item)
|
||||
{
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
@@ -453,12 +448,12 @@ public:
|
||||
FindPosition(item, pos);
|
||||
|
||||
// Check if object has been already added
|
||||
if (pos.ObjectIndex != INVALID_INDEX)
|
||||
if (pos.ObjectIndex != -1)
|
||||
return false;
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != INVALID_INDEX);
|
||||
auto bucket = &_table[pos.FreeSlotIndex];
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket->Occupy(item);
|
||||
_elementsCount++;
|
||||
|
||||
@@ -472,7 +467,7 @@ public:
|
||||
void Add(const Iterator& i)
|
||||
{
|
||||
ASSERT(&i._collection != this && i);
|
||||
Bucket& bucket = *i;
|
||||
const Bucket& bucket = *i;
|
||||
Add(bucket.Item);
|
||||
}
|
||||
|
||||
@@ -481,22 +476,20 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="item">The element to remove.</param>
|
||||
/// <returns>True if cannot remove item from the collection because cannot find it, otherwise false.</returns>
|
||||
bool Remove(const T& item)
|
||||
template<typename ItemType>
|
||||
bool Remove(const ItemType& item)
|
||||
{
|
||||
if (IsEmpty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
FindPositionResult pos;
|
||||
FindPosition(item, pos);
|
||||
|
||||
if (pos.ObjectIndex != INVALID_INDEX)
|
||||
if (pos.ObjectIndex != -1)
|
||||
{
|
||||
_table[pos.ObjectIndex].Delete();
|
||||
_allocation.Get()[pos.ObjectIndex].Delete();
|
||||
_elementsCount--;
|
||||
_deletedCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -510,8 +503,8 @@ public:
|
||||
ASSERT(&i._collection == this);
|
||||
if (i)
|
||||
{
|
||||
ASSERT(_table[i._index].IsOccupied());
|
||||
_table[i._index].Delete();
|
||||
ASSERT(_allocation.Get()[i._index].IsOccupied());
|
||||
_allocation.Get()[i._index].Delete();
|
||||
_elementsCount--;
|
||||
_deletedCount++;
|
||||
return true;
|
||||
@@ -526,15 +519,14 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="item">Item to find</param>
|
||||
/// <returns>Iterator for the found element or End if cannot find it</returns>
|
||||
Iterator Find(const T& item) const
|
||||
template<typename ItemType>
|
||||
Iterator Find(const ItemType& item) const
|
||||
{
|
||||
if (IsEmpty())
|
||||
return End();
|
||||
|
||||
FindPositionResult pos;
|
||||
FindPosition(item, pos);
|
||||
|
||||
return pos.ObjectIndex != INVALID_INDEX ? Iterator(*this, pos.ObjectIndex) : End();
|
||||
return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -542,15 +534,14 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="item">The item to locate.</param>
|
||||
/// <returns>True if value has been found in a collection, otherwise false</returns>
|
||||
bool Contains(const T& item) const
|
||||
template<typename ItemType>
|
||||
bool Contains(const ItemType& item) const
|
||||
{
|
||||
if (IsEmpty())
|
||||
return false;
|
||||
|
||||
FindPositionResult pos;
|
||||
FindPosition(item, pos);
|
||||
|
||||
return pos.ObjectIndex != INVALID_INDEX;
|
||||
return pos.ObjectIndex != -1;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -561,41 +552,26 @@ public:
|
||||
/// <param name="other">Other collection to clone</param>
|
||||
void Clone(const HashSet& other)
|
||||
{
|
||||
// Clear previous data
|
||||
Clear();
|
||||
|
||||
// Update capacity
|
||||
SetCapacity(other.Capacity(), false);
|
||||
|
||||
// Clone items
|
||||
for (auto i = other.Begin(); i != other.End(); ++i)
|
||||
for (Iterator i = other.Begin(); i != other.End(); ++i)
|
||||
Add(i);
|
||||
|
||||
// Check
|
||||
ASSERT(Count() == other.Count());
|
||||
ASSERT(Capacity() == other.Capacity());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets iterator for beginning of the collection.
|
||||
/// </summary>
|
||||
/// <returns>Iterator for beginning of the collection.</returns>
|
||||
Iterator Begin() const
|
||||
{
|
||||
Iterator i(*this, INVALID_INDEX);
|
||||
Iterator i(*this, -1);
|
||||
++i;
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets iterator for ending of the collection.
|
||||
/// </summary>
|
||||
/// <returns>Iterator for ending of the collection.</returns>
|
||||
Iterator End() const
|
||||
{
|
||||
return Iterator(*this, _tableSize);
|
||||
return Iterator(*this, _size);
|
||||
}
|
||||
|
||||
Iterator begin()
|
||||
@@ -607,7 +583,7 @@ public:
|
||||
|
||||
FORCE_INLINE Iterator end()
|
||||
{
|
||||
return Iterator(*this, _tableSize);
|
||||
return Iterator(*this, _size);
|
||||
}
|
||||
|
||||
const Iterator begin() const
|
||||
@@ -619,11 +595,14 @@ public:
|
||||
|
||||
FORCE_INLINE const Iterator end() const
|
||||
{
|
||||
return Iterator(*this, _tableSize);
|
||||
return Iterator(*this, _size);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// <summary>
|
||||
/// The result container of the set item lookup searching.
|
||||
/// </summary>
|
||||
struct FindPositionResult
|
||||
{
|
||||
int32 ObjectIndex;
|
||||
@@ -632,42 +611,43 @@ protected:
|
||||
|
||||
/// <summary>
|
||||
/// Returns a pair of positions: 1st where the object is, 2nd where
|
||||
/// it would go if you wanted to insert it. 1st is INVALID_INDEX
|
||||
/// if object is not found; 2nd is INVALID_INDEX if it is.
|
||||
/// it would go if you wanted to insert it. 1st is -1
|
||||
/// if object is not found; 2nd is -1 if it is.
|
||||
/// Note: because of deletions where-to-insert is not trivial: it's the
|
||||
/// first deleted bucket we see, as long as we don't find the item later
|
||||
/// </summary>
|
||||
/// <param name="item">The item to find</param>
|
||||
/// <param name="result">Pair of values: where the object is and where it would go if you wanted to insert it</param>
|
||||
void FindPosition(const T& item, FindPositionResult& result) const
|
||||
template<typename ItemType>
|
||||
void FindPosition(const ItemType& item, FindPositionResult& result) const
|
||||
{
|
||||
ASSERT(_table);
|
||||
|
||||
const int32 tableSizeMinusOne = _tableSize - 1;
|
||||
ASSERT(_size);
|
||||
const int32 tableSizeMinusOne = _size - 1;
|
||||
int32 bucketIndex = GetHash(item) & tableSizeMinusOne;
|
||||
int32 insertPos = INVALID_INDEX;
|
||||
int32 insertPos = -1;
|
||||
int32 numChecks = 0;
|
||||
result.FreeSlotIndex = INVALID_INDEX;
|
||||
|
||||
while (numChecks < _tableSize)
|
||||
const Bucket* data = _allocation.Get();
|
||||
result.FreeSlotIndex = -1;
|
||||
while (numChecks < _size)
|
||||
{
|
||||
// Empty bucket
|
||||
if (_table[bucketIndex].IsEmpty())
|
||||
const Bucket& bucket = data[bucketIndex];
|
||||
if (bucket.IsEmpty())
|
||||
{
|
||||
// Found place to insert
|
||||
result.ObjectIndex = INVALID_INDEX;
|
||||
result.FreeSlotIndex = insertPos == INVALID_INDEX ? bucketIndex : insertPos;
|
||||
result.ObjectIndex = -1;
|
||||
result.FreeSlotIndex = insertPos == -1 ? bucketIndex : insertPos;
|
||||
return;
|
||||
}
|
||||
// Deleted bucket
|
||||
if (_table[bucketIndex].IsDeleted())
|
||||
if (bucket.IsDeleted())
|
||||
{
|
||||
// Keep searching but mark to insert
|
||||
if (insertPos == INVALID_INDEX)
|
||||
if (insertPos == -1)
|
||||
insertPos = bucketIndex;
|
||||
}
|
||||
// Occupied bucket by target item
|
||||
else if (_table[bucketIndex].Item == item)
|
||||
else if (bucket.Item == item)
|
||||
{
|
||||
// Found item
|
||||
result.ObjectIndex = bucketIndex;
|
||||
@@ -675,10 +655,9 @@ protected:
|
||||
}
|
||||
|
||||
numChecks++;
|
||||
bucketIndex = (bucketIndex + DICTIONARY_PROB_FUNC(_tableSize, numChecks)) & tableSizeMinusOne;
|
||||
bucketIndex = (bucketIndex + DICTIONARY_PROB_FUNC(_size, numChecks)) & tableSizeMinusOne;
|
||||
}
|
||||
|
||||
result.ObjectIndex = INVALID_INDEX;
|
||||
result.ObjectIndex = -1;
|
||||
result.FreeSlotIndex = insertPos;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace FlaxEditor.Content.Settings
|
||||
}
|
||||
|
||||
// Create new settings asset and link it to the game settings
|
||||
var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, "Settings", CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json");
|
||||
var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, "Settings", Utilities.Utils.GetPropertyNameUI(typeof(T).Name) + ".json");
|
||||
if (Editor.SaveJsonAsset(path, obj))
|
||||
return true;
|
||||
asset = FlaxEngine.Content.LoadAsync<JsonAsset>(path);
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
/// </summary>
|
||||
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API GraphicsSettings : public SettingsBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(GraphicsSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(GraphicsSettings);
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts.
|
||||
/// </summary>
|
||||
@@ -62,8 +62,21 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(1320), DefaultValue(false), EditorDisplay(\"Quality\", \"Allow CSM Blending\")")
|
||||
bool AllowCSMBlending = false;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// If checked, enables Global SDF rendering. This can be used in materials, shaders, and particles.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Global SDF\")")
|
||||
bool EnableGlobalSDF = false;
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// If checked, the 'Generate SDF' option will be checked on model import options by default. Use it if your project uses Global SDF (eg. for Global Illumination or particles).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(2010), EditorDisplay(\"Global SDF\")")
|
||||
bool GenerateSDFOnModelImport = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
|
||||
/// </summary>
|
||||
@@ -71,15 +84,4 @@ public:
|
||||
|
||||
// [SettingsBase]
|
||||
void Apply() override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
|
||||
{
|
||||
DESERIALIZE(UseVSync);
|
||||
DESERIALIZE(AAQuality);
|
||||
DESERIALIZE(SSRQuality);
|
||||
DESERIALIZE(SSAOQuality);
|
||||
DESERIALIZE(VolumetricFogQuality);
|
||||
DESERIALIZE(ShadowsQuality);
|
||||
DESERIALIZE(ShadowMapsQuality);
|
||||
DESERIALIZE(AllowCSMBlending);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,6 +13,26 @@ String BoundingBox::ToString() const
|
||||
return String::Format(TEXT("{}"), *this);
|
||||
}
|
||||
|
||||
void BoundingBox::GetCorners(Vector3 corners[8]) const
|
||||
{
|
||||
corners[0] = Vector3(Minimum.X, Maximum.Y, Maximum.Z);
|
||||
corners[1] = Vector3(Maximum.X, Maximum.Y, Maximum.Z);
|
||||
corners[2] = Vector3(Maximum.X, Minimum.Y, Maximum.Z);
|
||||
corners[3] = Vector3(Minimum.X, Minimum.Y, Maximum.Z);
|
||||
corners[4] = Vector3(Minimum.X, Maximum.Y, Minimum.Z);
|
||||
corners[5] = Vector3(Maximum.X, Maximum.Y, Minimum.Z);
|
||||
corners[6] = Vector3(Maximum.X, Minimum.Y, Minimum.Z);
|
||||
corners[7] = Vector3(Minimum.X, Minimum.Y, Minimum.Z);
|
||||
}
|
||||
|
||||
BoundingBox BoundingBox::MakeOffsetted(const Vector3& offset) const
|
||||
{
|
||||
BoundingBox result;
|
||||
result.Minimum = Minimum + offset;
|
||||
result.Maximum = Maximum + offset;
|
||||
return result;
|
||||
}
|
||||
|
||||
void BoundingBox::FromPoints(const Vector3* points, int32 pointsCount, BoundingBox& result)
|
||||
{
|
||||
ASSERT(points && pointsCount > 0);
|
||||
|
||||
@@ -76,17 +76,7 @@ public:
|
||||
/// Gets the eight corners of the bounding box.
|
||||
/// </summary>
|
||||
/// <param name="corners">An array of points representing the eight corners of the bounding box.</param>
|
||||
void GetCorners(Vector3 corners[8]) const
|
||||
{
|
||||
corners[0] = Vector3(Minimum.X, Maximum.Y, Maximum.Z);
|
||||
corners[1] = Vector3(Maximum.X, Maximum.Y, Maximum.Z);
|
||||
corners[2] = Vector3(Maximum.X, Minimum.Y, Maximum.Z);
|
||||
corners[3] = Vector3(Minimum.X, Minimum.Y, Maximum.Z);
|
||||
corners[4] = Vector3(Minimum.X, Maximum.Y, Minimum.Z);
|
||||
corners[5] = Vector3(Maximum.X, Maximum.Y, Minimum.Z);
|
||||
corners[6] = Vector3(Maximum.X, Minimum.Y, Minimum.Z);
|
||||
corners[7] = Vector3(Minimum.X, Minimum.Y, Minimum.Z);
|
||||
}
|
||||
void GetCorners(Vector3 corners[8]) const;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates volume of the box.
|
||||
@@ -200,13 +190,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>The result.</returns>
|
||||
BoundingBox MakeOffsetted(const Vector3& offset) const
|
||||
{
|
||||
BoundingBox result;
|
||||
result.Minimum = Minimum + offset;
|
||||
result.Maximum = Maximum + offset;
|
||||
return result;
|
||||
}
|
||||
BoundingBox MakeOffsetted(const Vector3& offset) const;
|
||||
|
||||
public:
|
||||
|
||||
@@ -421,6 +405,26 @@ public:
|
||||
{
|
||||
return CollisionsHelper::BoxContainsSphere(*this, sphere);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the distance between a Bounding Box and a point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to test.</param>
|
||||
/// <returns>The distance between bounding box and a point.</returns>
|
||||
FORCE_INLINE float Distance(const Vector3& point) const
|
||||
{
|
||||
return CollisionsHelper::DistanceBoxPoint(*this, point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the distance between two Bounding Boxed.
|
||||
/// </summary>
|
||||
/// <param name="box">The bounding box to test.</param>
|
||||
/// <returns>The distance between bounding boxes.</returns>
|
||||
FORCE_INLINE float Distance(const BoundingBox& box) const
|
||||
{
|
||||
return CollisionsHelper::DistanceBoxBox(*this, box);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
@@ -1124,7 +1124,6 @@ bool CollisionsHelper::BoxIntersectsSphere(const BoundingBox& box, const Boundin
|
||||
Vector3 vector;
|
||||
Vector3::Clamp(sphere.Center, box.Minimum, box.Maximum, vector);
|
||||
const float distance = Vector3::DistanceSquared(sphere.Center, vector);
|
||||
|
||||
return distance <= sphere.Radius * sphere.Radius;
|
||||
}
|
||||
|
||||
@@ -1438,6 +1437,16 @@ bool CollisionsHelper::LineIntersectsRect(const Vector2& p1, const Vector2& p2,
|
||||
return (topoverlap < botoverlap) && (!((botoverlap < t) || (topoverlap > b)));*/
|
||||
}
|
||||
|
||||
Vector2 CollisionsHelper::LineHitsBox(const Vector3& lineStart, const Vector3& lineEnd, const Vector3& boxMin, const Vector3& boxMax)
|
||||
{
|
||||
const Vector3 invDirection = 1.0f / (lineEnd - lineStart);
|
||||
const Vector3 enterIntersection = (boxMin - lineStart) * invDirection;
|
||||
const Vector3 exitIntersection = (boxMax - lineStart) * invDirection;
|
||||
const Vector3 minIntersections = Vector3::Min(enterIntersection, exitIntersection);
|
||||
const Vector3 maxIntersections = Vector3::Max(enterIntersection, exitIntersection);
|
||||
return Vector2(Math::Saturate(minIntersections.MaxValue()), Math::Saturate(maxIntersections.MinValue()));
|
||||
}
|
||||
|
||||
bool CollisionsHelper::IsPointInTriangle(const Vector2& point, const Vector2& a, const Vector2& b, const Vector2& c)
|
||||
{
|
||||
const Vector2 an = a - point;
|
||||
|
||||
@@ -569,6 +569,12 @@ public:
|
||||
/// <returns>True if line intersects with the rectangle</returns>
|
||||
static bool LineIntersectsRect(const Vector2& p1, const Vector2& p2, const Rectangle& rect);
|
||||
|
||||
// Hits axis-aligned box (boxMin, boxMax) with a line (lineStart, lineEnd).
|
||||
// Returns the intersections on the line (x - closest, y - furthest).
|
||||
// Line hits the box if: intersections.x < intersections.y.
|
||||
// Hit point is: hitPoint = lineStart + (lineEnd - lineStart) * intersections.x/y.
|
||||
static Vector2 LineHitsBox(const Vector3& lineStart, const Vector3& lineEnd, const Vector3& boxMin, const Vector3& boxMax);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given 2D point is inside the specified triangle.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,9 +12,9 @@ static_assert(sizeof(Half2) == 4, "Invalid Half2 type size.");
|
||||
static_assert(sizeof(Half3) == 6, "Invalid Half3 type size.");
|
||||
static_assert(sizeof(Half4) == 8, "Invalid Half4 type size.");
|
||||
|
||||
Half2 Half2::Zero(0, 0);
|
||||
Half3 Half3::Zero(0, 0, 0);
|
||||
Half4 Half4::Zero(0, 0, 0, 0);
|
||||
Half2 Half2::Zero(0.0f, 0.0f);
|
||||
Half3 Half3::Zero(0.0f, 0.0f, 0.0f);
|
||||
Half4 Half4::Zero(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
Half2::Half2(const Vector2& v)
|
||||
{
|
||||
|
||||
@@ -45,7 +45,6 @@ class FLAXENGINE_API Float16Compressor
|
||||
static const int32 minD = minC - subC - 1;
|
||||
|
||||
public:
|
||||
|
||||
static Half Compress(const float value)
|
||||
{
|
||||
#if USE_SSE_HALF_CONVERSION
|
||||
@@ -102,14 +101,12 @@ public:
|
||||
struct FLAXENGINE_API Half2
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Zero vector
|
||||
/// </summary>
|
||||
static Half2 Zero;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the X component of the vector.
|
||||
/// </summary>
|
||||
@@ -121,7 +118,6 @@ public:
|
||||
Half Y;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
@@ -129,6 +125,17 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="x">X component</param>
|
||||
/// <param name="y">Y component</param>
|
||||
Half2(Half x, Half y)
|
||||
: X(x)
|
||||
, Y(y)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
@@ -147,7 +154,6 @@ public:
|
||||
Half2(const Vector2& v);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Convert to Vector2
|
||||
/// </summary>
|
||||
@@ -161,14 +167,12 @@ public:
|
||||
struct FLAXENGINE_API Half3
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Zero vector
|
||||
/// </summary>
|
||||
static Half3 Zero;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the X component of the vector.
|
||||
/// </summary>
|
||||
@@ -185,11 +189,17 @@ public:
|
||||
Half Z;
|
||||
|
||||
public:
|
||||
|
||||
Half3()
|
||||
{
|
||||
}
|
||||
|
||||
Half3(Half x, Half y, Half z)
|
||||
: X(x)
|
||||
, Y(y)
|
||||
, Z(z)
|
||||
{
|
||||
}
|
||||
|
||||
Half3(const float x, const float y, const float z)
|
||||
{
|
||||
X = Float16Compressor::Compress(x);
|
||||
@@ -200,7 +210,6 @@ public:
|
||||
Half3(const Vector3& v);
|
||||
|
||||
public:
|
||||
|
||||
Vector3 ToVector3() const;
|
||||
};
|
||||
|
||||
@@ -210,14 +219,12 @@ public:
|
||||
struct FLAXENGINE_API Half4
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Zero vector
|
||||
/// </summary>
|
||||
static Half4 Zero;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the X component of the vector.
|
||||
/// </summary>
|
||||
@@ -239,11 +246,18 @@ public:
|
||||
Half W;
|
||||
|
||||
public:
|
||||
|
||||
Half4()
|
||||
{
|
||||
}
|
||||
|
||||
Half4(Half x, Half y, Half z, Half w)
|
||||
: X(x)
|
||||
, Y(y)
|
||||
, Z(z)
|
||||
, W(w)
|
||||
{
|
||||
}
|
||||
|
||||
Half4(const float x, const float y, const float z)
|
||||
{
|
||||
X = Float16Compressor::Compress(x);
|
||||
@@ -265,7 +279,6 @@ public:
|
||||
explicit Half4(const Rectangle& rect);
|
||||
|
||||
public:
|
||||
|
||||
Vector2 ToVector2() const;
|
||||
Vector3 ToVector3() const;
|
||||
Vector4 ToVector4() const;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Matrix.h"
|
||||
#include "Matrix3x3.h"
|
||||
#include "Vector2.h"
|
||||
#include "Quaternion.h"
|
||||
#include "Transform.h"
|
||||
@@ -15,6 +16,17 @@ const Matrix Matrix::Identity(
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
Matrix::Matrix(const Matrix3x3& matrix)
|
||||
{
|
||||
Platform::MemoryCopy(&M11, &matrix.M11, sizeof(Vector3));
|
||||
Platform::MemoryCopy(&M21, &matrix.M21, sizeof(Vector3));
|
||||
Platform::MemoryCopy(&M31, &matrix.M31, sizeof(Vector3));
|
||||
M14 = 0.0f;
|
||||
M24 = 0.0f;
|
||||
M34 = 0.0f;
|
||||
M44 = 1.0f;
|
||||
}
|
||||
|
||||
String Matrix::ToString() const
|
||||
{
|
||||
return String::Format(TEXT("{}"), *this);
|
||||
@@ -284,8 +296,6 @@ void Matrix::LookAt(const Vector3& eye, const Vector3& target, const Vector3& up
|
||||
xaxis.Normalize();
|
||||
Vector3::Cross(zaxis, xaxis, yaxis);
|
||||
|
||||
result = Identity;
|
||||
|
||||
result.M11 = xaxis.X;
|
||||
result.M21 = xaxis.Y;
|
||||
result.M31 = xaxis.Z;
|
||||
@@ -298,9 +308,14 @@ void Matrix::LookAt(const Vector3& eye, const Vector3& target, const Vector3& up
|
||||
result.M23 = zaxis.Y;
|
||||
result.M33 = zaxis.Z;
|
||||
|
||||
result.M14 = 0.0f;
|
||||
result.M24 = 0.0f;
|
||||
result.M34 = 0.0f;
|
||||
|
||||
result.M41 = -Vector3::Dot(xaxis, eye);
|
||||
result.M42 = -Vector3::Dot(yaxis, eye);
|
||||
result.M43 = -Vector3::Dot(zaxis, eye);
|
||||
result.M44 = 1.0f;
|
||||
}
|
||||
|
||||
void Matrix::OrthoOffCenter(float left, float right, float bottom, float top, float zNear, float zFar, Matrix& result)
|
||||
@@ -587,33 +602,7 @@ void Matrix::Transformation2D(Vector2& scalingCenter, float scalingRotation, con
|
||||
Matrix Matrix::CreateWorld(const Vector3& position, const Vector3& forward, const Vector3& up)
|
||||
{
|
||||
Matrix result;
|
||||
Vector3 vector3, vector31, vector32;
|
||||
|
||||
Vector3::Normalize(forward, vector3);
|
||||
vector3.Negate();
|
||||
Vector3::Normalize(Vector3::Cross(up, vector3), vector31);
|
||||
Vector3::Cross(vector3, vector31, vector32);
|
||||
|
||||
result.M11 = vector31.X;
|
||||
result.M12 = vector31.Y;
|
||||
result.M13 = vector31.Z;
|
||||
result.M14 = 0.0f;
|
||||
|
||||
result.M21 = vector32.X;
|
||||
result.M22 = vector32.Y;
|
||||
result.M23 = vector32.Z;
|
||||
result.M24 = 0.0f;
|
||||
|
||||
result.M31 = vector3.X;
|
||||
result.M32 = vector3.Y;
|
||||
result.M33 = vector3.Z;
|
||||
result.M34 = 0.0f;
|
||||
|
||||
result.M41 = position.X;
|
||||
result.M42 = position.Y;
|
||||
result.M43 = position.Z;
|
||||
result.M44 = 1.0f;
|
||||
|
||||
CreateWorld(position, forward, up, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -649,41 +638,9 @@ void Matrix::CreateWorld(const Vector3& position, const Vector3& forward, const
|
||||
|
||||
Matrix Matrix::CreateFromAxisAngle(const Vector3& axis, float angle)
|
||||
{
|
||||
Matrix matrix;
|
||||
|
||||
const float x = axis.X;
|
||||
const float y = axis.Y;
|
||||
const float z = axis.Z;
|
||||
const float single = Math::Sin(angle);
|
||||
const float single1 = Math::Cos(angle);
|
||||
const float single2 = x * x;
|
||||
const float single3 = y * y;
|
||||
const float single4 = z * z;
|
||||
const float single5 = x * y;
|
||||
const float single6 = x * z;
|
||||
const float single7 = y * z;
|
||||
|
||||
matrix.M11 = single2 + single1 * (1.0f - single2);
|
||||
matrix.M12 = single5 - single1 * single5 + single * z;
|
||||
matrix.M13 = single6 - single1 * single6 - single * y;
|
||||
matrix.M14 = 0.0f;
|
||||
|
||||
matrix.M21 = single5 - single1 * single5 - single * z;
|
||||
matrix.M22 = single3 + single1 * (1.0f - single3);
|
||||
matrix.M23 = single7 - single1 * single7 + single * x;
|
||||
matrix.M24 = 0.0f;
|
||||
|
||||
matrix.M31 = single6 - single1 * single6 + single * y;
|
||||
matrix.M32 = single7 - single1 * single7 - single * x;
|
||||
matrix.M33 = single4 + single1 * (1.0f - single4);
|
||||
matrix.M34 = 0.0f;
|
||||
|
||||
matrix.M41 = 0.0f;
|
||||
matrix.M42 = 0.0f;
|
||||
matrix.M43 = 0.0f;
|
||||
matrix.M44 = 1.0f;
|
||||
|
||||
return matrix;
|
||||
Matrix result;
|
||||
CreateFromAxisAngle(axis, angle, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Matrix::CreateFromAxisAngle(const Vector3& axis, float angle, Matrix& result)
|
||||
|
||||
@@ -150,6 +150,8 @@ public:
|
||||
Platform::MemoryCopy(Raw, values, sizeof(float) * 16);
|
||||
}
|
||||
|
||||
explicit Matrix(const Matrix3x3& matrix);
|
||||
|
||||
public:
|
||||
|
||||
String ToString() const;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Matrix3x3.h"
|
||||
#include "../Types/String.h"
|
||||
#include "Matrix.h"
|
||||
#include "Quaternion.h"
|
||||
#include "../Types/String.h"
|
||||
|
||||
const Matrix3x3 Matrix3x3::Zero(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
const Matrix3x3 Matrix3x3::Identity(
|
||||
@@ -10,11 +11,37 @@ const Matrix3x3 Matrix3x3::Identity(
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f);
|
||||
|
||||
Matrix3x3::Matrix3x3(const Matrix& matrix)
|
||||
{
|
||||
Platform::MemoryCopy(&M11, &matrix.M11, sizeof(Vector3));
|
||||
Platform::MemoryCopy(&M21, &matrix.M21, sizeof(Vector3));
|
||||
Platform::MemoryCopy(&M31, &matrix.M31, sizeof(Vector3));
|
||||
}
|
||||
|
||||
String Matrix3x3::ToString() const
|
||||
{
|
||||
return String::Format(TEXT("{}"), *this);
|
||||
}
|
||||
|
||||
void Matrix3x3::NormalizeScale()
|
||||
{
|
||||
const float scaleX = 1.0f / Vector3(M11, M21, M31).Length();
|
||||
const float scaleY = 1.0f / Vector3(M12, M22, M32).Length();
|
||||
const float scaleZ = 1.0f / Vector3(M13, M23, M33).Length();
|
||||
|
||||
M11 *= scaleX;
|
||||
M21 *= scaleX;
|
||||
M31 *= scaleX;
|
||||
|
||||
M12 *= scaleY;
|
||||
M22 *= scaleY;
|
||||
M32 *= scaleY;
|
||||
|
||||
M13 *= scaleZ;
|
||||
M23 *= scaleZ;
|
||||
M33 *= scaleZ;
|
||||
}
|
||||
|
||||
void Matrix3x3::Invert(const Matrix3x3& value, Matrix3x3& result)
|
||||
{
|
||||
const float d11 = value.M22 * value.M33 + value.M23 * -value.M32;
|
||||
|
||||
@@ -113,6 +113,12 @@ public:
|
||||
Platform::MemoryCopy(Raw, values, sizeof(float) * 9);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Matrix3x3"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The 4 by 4 matrix to initialize from with rotation and scale (translation is skipped).</param>
|
||||
explicit Matrix3x3(const Matrix& matrix);
|
||||
|
||||
public:
|
||||
|
||||
String ToString() const;
|
||||
@@ -255,6 +261,11 @@ public:
|
||||
Transpose(*this, *this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any scaling from the matrix by performing the normalization (each row magnitude is 1).
|
||||
/// </summary>
|
||||
void NormalizeScale();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -8,11 +8,18 @@
|
||||
|
||||
OrientedBoundingBox::OrientedBoundingBox(const BoundingBox& bb)
|
||||
{
|
||||
const Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) / 2.0f;
|
||||
const Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) * 0.5f;
|
||||
Extents = bb.Maximum - center;
|
||||
Matrix::Translation(center, Transformation);
|
||||
}
|
||||
|
||||
OrientedBoundingBox::OrientedBoundingBox(const Vector3& extents, const Matrix3x3& rotationScale, const Vector3& translation)
|
||||
: Extents(extents)
|
||||
, Transformation(rotationScale)
|
||||
{
|
||||
Transformation.SetTranslation(translation);
|
||||
}
|
||||
|
||||
OrientedBoundingBox::OrientedBoundingBox(Vector3 points[], int32 pointCount)
|
||||
{
|
||||
ASSERT(points && pointCount > 0);
|
||||
@@ -104,6 +111,12 @@ void OrientedBoundingBox::GetBoundingBox(BoundingBox& result) const
|
||||
BoundingBox::FromPoints(corners, 8, result);
|
||||
}
|
||||
|
||||
void OrientedBoundingBox::Transform(const Matrix& matrix)
|
||||
{
|
||||
const Matrix tmp = Transformation;
|
||||
Matrix::Multiply(tmp, matrix, Transformation);
|
||||
}
|
||||
|
||||
ContainmentType OrientedBoundingBox::Contains(const Vector3& point, float* distance) const
|
||||
{
|
||||
// Transform the point into the obb coordinates
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
Transformation = transformation;
|
||||
}
|
||||
|
||||
OrientedBoundingBox(const Vector3& extents, const Matrix3x3& rotationScale, const Vector3& translation);
|
||||
|
||||
// Init
|
||||
// @param minimum The minimum vertex of the bounding box.
|
||||
// @param maximum The maximum vertex of the bounding box.
|
||||
@@ -99,10 +101,7 @@ public:
|
||||
|
||||
// Transforms this box using a transformation matrix.
|
||||
// @param mat The transformation matrix.
|
||||
void Transform(const Matrix& mat)
|
||||
{
|
||||
Transformation *= mat;
|
||||
}
|
||||
void Transform(const Matrix& matrix);
|
||||
|
||||
// Scales the OBB by scaling its Extents without affecting the Transformation matrix.
|
||||
// By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Vector3.h"
|
||||
#include "Vector4.h"
|
||||
#include "Matrix.h"
|
||||
#include "Matrix3x3.h"
|
||||
#include "Math.h"
|
||||
#include "../Types/String.h"
|
||||
|
||||
@@ -208,6 +209,59 @@ void Quaternion::RotationMatrix(const Matrix& matrix, Quaternion& result)
|
||||
result.Normalize();
|
||||
}
|
||||
|
||||
void Quaternion::RotationMatrix(const Matrix3x3& matrix, Quaternion& result)
|
||||
{
|
||||
float sqrtV;
|
||||
float half;
|
||||
const float scale = matrix.M11 + matrix.M22 + matrix.M33;
|
||||
|
||||
if (scale > 0.0f)
|
||||
{
|
||||
sqrtV = Math::Sqrt(scale + 1.0f);
|
||||
result.W = sqrtV * 0.5f;
|
||||
sqrtV = 0.5f / sqrtV;
|
||||
|
||||
result.X = (matrix.M23 - matrix.M32) * sqrtV;
|
||||
result.Y = (matrix.M31 - matrix.M13) * sqrtV;
|
||||
result.Z = (matrix.M12 - matrix.M21) * sqrtV;
|
||||
}
|
||||
else if (matrix.M11 >= matrix.M22 && matrix.M11 >= matrix.M33)
|
||||
{
|
||||
sqrtV = Math::Sqrt(1.0f + matrix.M11 - matrix.M22 - matrix.M33);
|
||||
half = 0.5f / sqrtV;
|
||||
|
||||
result = Quaternion(
|
||||
0.5f * sqrtV,
|
||||
(matrix.M12 + matrix.M21) * half,
|
||||
(matrix.M13 + matrix.M31) * half,
|
||||
(matrix.M23 - matrix.M32) * half);
|
||||
}
|
||||
else if (matrix.M22 > matrix.M33)
|
||||
{
|
||||
sqrtV = Math::Sqrt(1.0f + matrix.M22 - matrix.M11 - matrix.M33);
|
||||
half = 0.5f / sqrtV;
|
||||
|
||||
result = Quaternion(
|
||||
(matrix.M21 + matrix.M12) * half,
|
||||
0.5f * sqrtV,
|
||||
(matrix.M32 + matrix.M23) * half,
|
||||
(matrix.M31 - matrix.M13) * half);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqrtV = Math::Sqrt(1.0f + matrix.M33 - matrix.M11 - matrix.M22);
|
||||
half = 0.5f / sqrtV;
|
||||
|
||||
result = Quaternion(
|
||||
(matrix.M31 + matrix.M13) * half,
|
||||
(matrix.M32 + matrix.M23) * half,
|
||||
0.5f * sqrtV,
|
||||
(matrix.M12 - matrix.M21) * half);
|
||||
}
|
||||
|
||||
result.Normalize();
|
||||
}
|
||||
|
||||
void Quaternion::LookAt(const Vector3& eye, const Vector3& target, const Vector3& up, Quaternion& result)
|
||||
{
|
||||
Matrix matrix;
|
||||
|
||||
@@ -10,6 +10,7 @@ struct Vector2;
|
||||
struct Vector3;
|
||||
struct Vector4;
|
||||
struct Matrix;
|
||||
struct Matrix3x3;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a four dimensional mathematical quaternion. Euler angles are stored in: pitch, yaw, roll order (x, y, z).
|
||||
@@ -566,6 +567,11 @@ public:
|
||||
// @param result When the method completes, contains the newly created quaternion
|
||||
static void RotationMatrix(const Matrix& matrix, Quaternion& result);
|
||||
|
||||
// Creates a quaternion given a rotation matrix
|
||||
// @param matrix The rotation matrix
|
||||
// @param result When the method completes, contains the newly created quaternion
|
||||
static void RotationMatrix(const Matrix3x3& matrix, Quaternion& result);
|
||||
|
||||
// Creates a left-handed, look-at quaternion
|
||||
// @param eye The position of the viewer's eye
|
||||
// @param target The camera look-at target
|
||||
|
||||
@@ -49,6 +49,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Vector3 GetNormal() const
|
||||
{
|
||||
return Vector3::Normalize((V1 - V0) ^ (V2 - V0));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// Determines if there is an intersection between the current object and a Ray
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Color.h"
|
||||
#include "Quaternion.h"
|
||||
#include "Matrix.h"
|
||||
#include "Matrix3x3.h"
|
||||
#include "Int2.h"
|
||||
#include "Int3.h"
|
||||
#include "Int4.h"
|
||||
@@ -215,9 +216,15 @@ void Vector3::Transform(const Vector3& vector, const Matrix& transform, Vector3&
|
||||
void Vector3::Transform(const Vector3* vectors, const Matrix& transform, Vector3* results, int32 vectorsCount)
|
||||
{
|
||||
for (int32 i = 0; i < vectorsCount; i++)
|
||||
{
|
||||
Transform(vectors[i], transform, results[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Vector3::Transform(const Vector3& vector, const Matrix3x3& transform, Vector3& result)
|
||||
{
|
||||
result = Vector3(
|
||||
vector.X * transform.M11 + vector.Y * transform.M21 + vector.Z * transform.M31,
|
||||
vector.X * transform.M12 + vector.Y * transform.M22 + vector.Z * transform.M32,
|
||||
vector.X * transform.M13 + vector.Y * transform.M23 + vector.Z * transform.M33);
|
||||
}
|
||||
|
||||
Vector3 Vector3::Transform(const Vector3& vector, const Matrix& transform)
|
||||
|
||||
@@ -11,6 +11,7 @@ struct Double3;
|
||||
struct Double4;
|
||||
struct Quaternion;
|
||||
struct Matrix;
|
||||
struct Matrix3x3;
|
||||
struct Vector2;
|
||||
struct Vector4;
|
||||
struct Color;
|
||||
@@ -215,7 +216,7 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns average arithmetic of all the components
|
||||
/// Returns the average arithmetic of all the components.
|
||||
/// </summary>
|
||||
float AverageArithmetic() const
|
||||
{
|
||||
@@ -223,7 +224,7 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sum of all vector components values.
|
||||
/// Gets the sum of all vector components values.
|
||||
/// </summary>
|
||||
float SumValues() const
|
||||
{
|
||||
@@ -231,7 +232,7 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns minimum value of all the components.
|
||||
/// Returns the minimum value of all the components.
|
||||
/// </summary>
|
||||
float MinValue() const
|
||||
{
|
||||
@@ -239,7 +240,7 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns maximum value of all the components.
|
||||
/// Returns the maximum value of all the components.
|
||||
/// </summary>
|
||||
float MaxValue() const
|
||||
{
|
||||
@@ -255,9 +256,8 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if vector has one or more components equal to +/- infinity
|
||||
/// Returns true if vector has one or more components equal to +/- infinity.
|
||||
/// </summary>
|
||||
/// <returns>True if one or more components equal to +/- infinity</returns>
|
||||
bool IsInfinity() const
|
||||
{
|
||||
return isinf(X) || isinf(Y) || isinf(Z);
|
||||
@@ -714,6 +714,12 @@ public:
|
||||
// @param vectorsCount Amount of vectors to transform
|
||||
static void Transform(const Vector3* vectors, const Matrix& transform, Vector3* results, int32 vectorsCount);
|
||||
|
||||
// Transforms a 3D vector by the given matrix
|
||||
// @param vector The source vector
|
||||
// @param transform The transformation matrix
|
||||
// @param result When the method completes, contains the transformed Vector3
|
||||
static void Transform(const Vector3& vector, const Matrix3x3& transform, Vector3& result);
|
||||
|
||||
// Transforms a 3D vector by the given matrix
|
||||
// @param vector The source vector
|
||||
// @param transform The transformation matrix
|
||||
|
||||
@@ -559,7 +559,7 @@ void Foliage::OnFoliageTypeModelLoaded(int32 index)
|
||||
}
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
{
|
||||
PROFILE_CPU_NAMED("Create Clusters");
|
||||
@@ -606,7 +606,7 @@ void Foliage::RebuildClusters()
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -696,7 +696,7 @@ void Foliage::RebuildClusters()
|
||||
_box = totalBounds;
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
// Insert all instances to the clusters
|
||||
@@ -845,6 +845,10 @@ bool Foliage::Intersects(const Ray& ray, float& distance, Vector3& normal, int32
|
||||
|
||||
void Foliage::Draw(RenderContext& renderContext)
|
||||
{
|
||||
if (renderContext.View.Pass == DrawPass::GlobalSDF)
|
||||
return; // TODO: Foliage rendering to Global SDF
|
||||
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
|
||||
return; // Not supported
|
||||
if (Instances.IsEmpty())
|
||||
return;
|
||||
auto& view = renderContext.View;
|
||||
@@ -1016,11 +1020,6 @@ void Foliage::Draw(RenderContext& renderContext)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Foliage::DrawGeneric(RenderContext& renderContext)
|
||||
{
|
||||
Draw(renderContext);
|
||||
}
|
||||
|
||||
bool Foliage::IntersectsItself(const Ray& ray, float& distance, Vector3& normal)
|
||||
{
|
||||
int32 instanceIndex;
|
||||
@@ -1227,12 +1226,12 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
|
||||
void Foliage::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void Foliage::OnEnable()
|
||||
{
|
||||
_sceneRenderingKey = GetSceneRendering()->AddGeometry(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
@@ -1240,7 +1239,7 @@ void Foliage::OnEnable()
|
||||
|
||||
void Foliage::OnDisable()
|
||||
{
|
||||
GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -197,7 +197,6 @@ public:
|
||||
|
||||
// [Actor]
|
||||
void Draw(RenderContext& renderContext) override;
|
||||
void DrawGeneric(RenderContext& renderContext) override;
|
||||
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
|
||||
@@ -208,4 +208,11 @@ void FoliageType::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
|
||||
DESERIALIZE(PlacementRandomRollAngle);
|
||||
DESERIALIZE_BIT(PlacementAlignToNormal);
|
||||
DESERIALIZE_BIT(PlacementRandomYaw);
|
||||
|
||||
// [Deprecated on 07.02.2022, expires on 07.02.2024]
|
||||
if (modifier->EngineBuild <= 6330)
|
||||
DrawModes |= DrawPass::GlobalSDF;
|
||||
// [Deprecated on 27.04.2022, expires on 27.04.2024]
|
||||
if (modifier->EngineBuild <= 6331)
|
||||
DrawModes |= DrawPass::GlobalSurfaceAtlas;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "DynamicBuffer.h"
|
||||
#include "PixelFormatExtensions.h"
|
||||
#include "GPUDevice.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Utilities.h"
|
||||
@@ -87,3 +88,24 @@ void DynamicBuffer::Dispose()
|
||||
SAFE_DELETE_GPU_RESOURCE(_buffer);
|
||||
Data.Resize(0);
|
||||
}
|
||||
|
||||
void DynamicStructuredBuffer::InitDesc(GPUBufferDescription& desc, int32 numElements)
|
||||
{
|
||||
desc = GPUBufferDescription::Structured(numElements, _stride, _isUnorderedAccess);
|
||||
desc.Usage = GPUResourceUsage::Dynamic;
|
||||
}
|
||||
|
||||
DynamicTypedBuffer::DynamicTypedBuffer(uint32 initialCapacity, PixelFormat format, bool isUnorderedAccess, const String& name)
|
||||
: DynamicBuffer(initialCapacity, PixelFormatExtensions::SizeInBytes(format), name)
|
||||
, _format(format)
|
||||
, _isUnorderedAccess(isUnorderedAccess)
|
||||
{
|
||||
}
|
||||
|
||||
void DynamicTypedBuffer::InitDesc(GPUBufferDescription& desc, int32 numElements)
|
||||
{
|
||||
auto bufferFlags = GPUBufferFlags::ShaderResource;
|
||||
if (_isUnorderedAccess)
|
||||
bufferFlags |= GPUBufferFlags::UnorderedAccess;
|
||||
desc = GPUBufferDescription::Buffer(numElements * _stride, bufferFlags, _format, nullptr, _stride, GPUResourceUsage::Dynamic);
|
||||
}
|
||||
|
||||
@@ -170,3 +170,58 @@ protected:
|
||||
desc = GPUBufferDescription::Index(_stride, numElements, GPUResourceUsage::Dynamic);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Dynamic structured buffer that allows to upload data to the GPU from CPU (supports dynamic resizing).
|
||||
/// </summary>
|
||||
class FLAXENGINE_API DynamicStructuredBuffer : public DynamicBuffer
|
||||
{
|
||||
private:
|
||||
bool _isUnorderedAccess;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">Initial capacity of the buffer (in bytes).</param>
|
||||
/// <param name="stride">Stride in bytes.</param>
|
||||
/// <param name="isUnorderedAccess">True if unordered access usage.</param>
|
||||
/// <param name="name">Buffer name.</param>
|
||||
DynamicStructuredBuffer(uint32 initialCapacity, uint32 stride, bool isUnorderedAccess = false, const String& name = String::Empty)
|
||||
: DynamicBuffer(initialCapacity, stride, name)
|
||||
, _isUnorderedAccess(isUnorderedAccess)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [DynamicBuffer]
|
||||
void InitDesc(GPUBufferDescription& desc, int32 numElements) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Dynamic Typed buffer that allows to upload data to the GPU from CPU (supports dynamic resizing).
|
||||
/// </summary>
|
||||
class FLAXENGINE_API DynamicTypedBuffer : public DynamicBuffer
|
||||
{
|
||||
private:
|
||||
PixelFormat _format;
|
||||
bool _isUnorderedAccess;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">Initial capacity of the buffer (in bytes).</param>
|
||||
/// <param name="format">Format of the data.</param>
|
||||
/// <param name="isUnorderedAccess">True if unordered access usage.</param>
|
||||
/// <param name="name">Buffer name.</param>
|
||||
DynamicTypedBuffer(uint32 initialCapacity, PixelFormat format, bool isUnorderedAccess = false, const String& name = String::Empty);
|
||||
|
||||
protected:
|
||||
|
||||
// [DynamicBuffer]
|
||||
void InitDesc(GPUBufferDescription& desc, int32 numElements) override;
|
||||
};
|
||||
|
||||
@@ -702,6 +702,16 @@ API_ENUM(Attributes="Flags") enum class DrawPass : int32
|
||||
/// </summary>
|
||||
MotionVectors = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// The Global Sign Distance Field (SDF) rendering pass. Used for software raytracing though the scene on a GPU.
|
||||
/// </summary>
|
||||
GlobalSDF = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// The Global Surface Atlas rendering pass. Used for software raytracing though the scene on a GPU to evaluate the object surface material properties.
|
||||
/// </summary>
|
||||
GlobalSurfaceAtlas = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// The debug quad overdraw rendering (editor-only).
|
||||
/// </summary>
|
||||
@@ -712,13 +722,13 @@ API_ENUM(Attributes="Flags") enum class DrawPass : int32
|
||||
/// The default set of draw passes for the scene objects.
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="HideInEditor")
|
||||
Default = Depth | GBuffer | Forward | Distortion | MotionVectors,
|
||||
Default = Depth | GBuffer | Forward | Distortion | MotionVectors | GlobalSDF | GlobalSurfaceAtlas,
|
||||
|
||||
/// <summary>
|
||||
/// The all draw passes combined into a single mask.
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="HideInEditor")
|
||||
All = Depth | GBuffer | Forward | Distortion | MotionVectors,
|
||||
All = Depth | GBuffer | Forward | Distortion | MotionVectors | GlobalSDF | GlobalSurfaceAtlas,
|
||||
};
|
||||
|
||||
DECLARE_ENUM_OPERATORS(DrawPass);
|
||||
@@ -847,6 +857,16 @@ API_ENUM() enum class ViewMode
|
||||
/// Draw geometry overdraw to visualize performance of pixels rendering.
|
||||
/// </summary>
|
||||
QuadOverdraw = 23,
|
||||
|
||||
/// <summary>
|
||||
/// Draw Global Sign Distant Field (SDF) preview.
|
||||
/// </summary>
|
||||
GlobalSDF = 24,
|
||||
|
||||
/// <summary>
|
||||
/// Draw Global Surface Atlas preview.
|
||||
/// </summary>
|
||||
GlobalSurfaceAtlas = 25,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -997,7 +1017,7 @@ API_ENUM(Attributes="Flags") enum class ViewFlags : int64
|
||||
/// <summary>
|
||||
/// Default flags for materials/models previews generating.
|
||||
/// </summary>
|
||||
DefaultAssetPreview = Reflections | Decals | GI | DirectionalLights | PointLights | SpotLights | SkyLights | SpecularLight | AntiAliasing | Bloom | ToneMapping | EyeAdaptation | CameraArtifacts | LensFlares | ContactShadows,
|
||||
DefaultAssetPreview = Reflections | Decals | DirectionalLights | PointLights | SpotLights | SkyLights | SpecularLight | AntiAliasing | Bloom | ToneMapping | EyeAdaptation | CameraArtifacts | LensFlares | ContactShadows,
|
||||
};
|
||||
|
||||
DECLARE_ENUM_OPERATORS(ViewFlags);
|
||||
|
||||
@@ -553,7 +553,7 @@ public:
|
||||
/// <summary>
|
||||
/// Sets the rendering viewport and scissor rectangle.
|
||||
/// </summary>
|
||||
/// <param name="viewport">The viewport.</param>
|
||||
/// <param name="viewport">The viewport (in pixels).</param>
|
||||
API_FUNCTION() FORCE_INLINE void SetViewportAndScissors(const Viewport& viewport)
|
||||
{
|
||||
SetViewport(viewport);
|
||||
@@ -575,13 +575,13 @@ public:
|
||||
/// <summary>
|
||||
/// Sets the rendering viewport.
|
||||
/// </summary>
|
||||
/// <param name="viewport">The viewport.</param>
|
||||
/// <param name="viewport">The viewport (in pixels).</param>
|
||||
API_FUNCTION() virtual void SetViewport(API_PARAM(Ref) const Viewport& viewport) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the scissor rectangle.
|
||||
/// </summary>
|
||||
/// <param name="scissorRect">The scissor rectangle.</param>
|
||||
/// <param name="scissorRect">The scissor rectangle (in pixels).</param>
|
||||
API_FUNCTION() virtual void SetScissor(API_PARAM(Ref) const Rectangle& scissorRect) = 0;
|
||||
|
||||
public:
|
||||
|
||||
@@ -48,7 +48,7 @@ void DecalMaterialShader::Bind(BindParameters& params)
|
||||
bindMeta.Context = context;
|
||||
bindMeta.Constants = cb;
|
||||
bindMeta.Input = nullptr;
|
||||
bindMeta.Buffers = nullptr;
|
||||
bindMeta.Buffers = params.RenderContext.Buffers;
|
||||
bindMeta.CanSampleDepth = true;
|
||||
bindMeta.CanSampleGBuffer = false;
|
||||
MaterialParams::Bind(params.ParamsLink, bindMeta);
|
||||
|
||||
@@ -74,7 +74,7 @@ void DeferredMaterialShader::Bind(BindParameters& params)
|
||||
bindMeta.Context = context;
|
||||
bindMeta.Constants = cb;
|
||||
bindMeta.Input = nullptr;
|
||||
bindMeta.Buffers = nullptr;
|
||||
bindMeta.Buffers = params.RenderContext.Buffers;
|
||||
bindMeta.CanSampleDepth = false;
|
||||
bindMeta.CanSampleGBuffer = false;
|
||||
MaterialParams::Bind(params.ParamsLink, bindMeta);
|
||||
|
||||
@@ -62,7 +62,7 @@ void DeformableMaterialShader::Bind(BindParameters& params)
|
||||
bindMeta.Context = context;
|
||||
bindMeta.Constants = cb;
|
||||
bindMeta.Input = nullptr;
|
||||
bindMeta.Buffers = nullptr;
|
||||
bindMeta.Buffers = params.RenderContext.Buffers;
|
||||
bindMeta.CanSampleDepth = false;
|
||||
bindMeta.CanSampleGBuffer = false;
|
||||
MaterialParams::Bind(params.ParamsLink, bindMeta);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "MaterialInfo.h"
|
||||
|
||||
struct MaterialParamsLink;
|
||||
class GPUShader;
|
||||
class GPUContext;
|
||||
class GPUTextureView;
|
||||
class RenderBuffers;
|
||||
@@ -26,6 +27,12 @@ public:
|
||||
/// <returns>The constant reference to the material descriptor.</returns>
|
||||
virtual const MaterialInfo& GetInfo() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shader resource.
|
||||
/// </summary>
|
||||
/// <returns>The material shader resource.</returns>
|
||||
virtual GPUShader* GetShader() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether material is a surface shader.
|
||||
/// </summary>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPULimits.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
|
||||
#include "Engine/Streaming/Streaming.h"
|
||||
|
||||
bool MaterialInfo8::operator==(const MaterialInfo8& other) const
|
||||
@@ -157,6 +159,9 @@ const Char* ToString(MaterialParameterType value)
|
||||
case MaterialParameterType::TextureGroupSampler:
|
||||
result = TEXT("TextureGroupSampler");
|
||||
break;
|
||||
case MaterialParameterType::GlobalSDF:
|
||||
result = TEXT("GlobalSDF");
|
||||
break;
|
||||
default:
|
||||
result = TEXT("");
|
||||
break;
|
||||
@@ -198,7 +203,6 @@ Variant MaterialParameter::GetValue() const
|
||||
case MaterialParameterType::GPUTexture:
|
||||
return _asGPUTexture.Get();
|
||||
default:
|
||||
CRASH;
|
||||
return Variant::Zero;
|
||||
}
|
||||
}
|
||||
@@ -303,6 +307,8 @@ void MaterialParameter::SetValue(const Variant& value)
|
||||
invalidType = true;
|
||||
}
|
||||
break;
|
||||
case MaterialParameterType::GlobalSDF:
|
||||
break;
|
||||
default:
|
||||
invalidType = true;
|
||||
}
|
||||
@@ -477,6 +483,16 @@ void MaterialParameter::Bind(BindMeta& meta) const
|
||||
case MaterialParameterType::TextureGroupSampler:
|
||||
meta.Context->BindSampler(_registerIndex, Streaming::GetTextureGroupSampler(_asInteger));
|
||||
break;
|
||||
case MaterialParameterType::GlobalSDF:
|
||||
{
|
||||
GlobalSignDistanceFieldPass::BindingData bindingData;
|
||||
if (GlobalSignDistanceFieldPass::Instance()->Get(meta.Buffers, bindingData))
|
||||
Platform::MemoryClear(&bindingData, sizeof(bindingData));
|
||||
for (int32 i = 0; i < 4; i++)
|
||||
meta.Context->BindSR(_registerIndex + i, bindingData.Cascades[i] ? bindingData.Cascades[i]->ViewVolume() : nullptr);
|
||||
*((GlobalSignDistanceFieldPass::ConstantsData*)(meta.Constants.Get() + _offset)) = bindingData.Constants;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -128,6 +128,11 @@ enum class MaterialParameterType : byte
|
||||
/// The texture sampler derived from texture group settings.
|
||||
/// </summary>
|
||||
TextureGroupSampler = 19,
|
||||
|
||||
/// <summary>
|
||||
/// The Global SDF (textures and constants).
|
||||
/// </summary>
|
||||
GlobalSDF = 20,
|
||||
};
|
||||
|
||||
const Char* ToString(MaterialParameterType value);
|
||||
|
||||
@@ -98,8 +98,6 @@ public:
|
||||
/// <returns>The created and loaded material or null if failed.</returns>
|
||||
static MaterialShader* CreateDummy(MemoryReadStream& shaderCacheStream, const MaterialInfo& info);
|
||||
|
||||
GPUShader* GetShader() const;
|
||||
|
||||
/// <summary>
|
||||
/// Clears the loaded data.
|
||||
/// </summary>
|
||||
@@ -114,5 +112,6 @@ public:
|
||||
|
||||
// [IMaterial]
|
||||
const MaterialInfo& GetInfo() const override;
|
||||
GPUShader* GetShader() const override;
|
||||
bool IsReady() const override;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
const int32 envProbeShaderRegisterIndex = srv + 0;
|
||||
const int32 skyLightShaderRegisterIndex = srv + 1;
|
||||
const int32 dirLightShaderRegisterIndex = srv + 2;
|
||||
const bool canUseShadow = view.Pass != DrawPass::Depth;
|
||||
|
||||
// Set fog input
|
||||
if (cache->Fog)
|
||||
@@ -39,7 +40,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
{
|
||||
const auto& dirLight = cache->DirectionalLights.First();
|
||||
const auto shadowPass = ShadowsPass::Instance();
|
||||
const bool useShadow = shadowPass->LastDirLightIndex == 0;
|
||||
const bool useShadow = shadowPass->LastDirLightIndex == 0 && canUseShadow;
|
||||
if (useShadow)
|
||||
{
|
||||
data.DirectionalLightShadow = shadowPass->LastDirLight;
|
||||
@@ -49,7 +50,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
{
|
||||
context->UnBindSR(dirLightShaderRegisterIndex);
|
||||
}
|
||||
dirLight.SetupLightData(&data.DirectionalLight, view, useShadow);
|
||||
dirLight.SetupLightData(&data.DirectionalLight, useShadow);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -62,7 +63,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
if (cache->SkyLights.HasItems())
|
||||
{
|
||||
auto& skyLight = cache->SkyLights.First();
|
||||
skyLight.SetupLightData(&data.SkyLight, view, false);
|
||||
skyLight.SetupLightData(&data.SkyLight, false);
|
||||
const auto texture = skyLight.Image ? skyLight.Image->GetTexture() : nullptr;
|
||||
context->BindSR(skyLightShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture));
|
||||
}
|
||||
@@ -103,7 +104,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
const auto& light = cache->PointLights[i];
|
||||
if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint)
|
||||
{
|
||||
light.SetupLightData(&data.LocalLights[data.LocalLightsCount], view, false);
|
||||
light.SetupLightData(&data.LocalLights[data.LocalLightsCount], false);
|
||||
data.LocalLightsCount++;
|
||||
}
|
||||
}
|
||||
@@ -112,7 +113,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
const auto& light = cache->SpotLights[i];
|
||||
if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint)
|
||||
{
|
||||
light.SetupLightData(&data.LocalLights[data.LocalLightsCount], view, false);
|
||||
light.SetupLightData(&data.LocalLights[data.LocalLightsCount], false);
|
||||
data.LocalLightsCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ void TerrainMaterialShader::Bind(BindParameters& params)
|
||||
bindMeta.Context = context;
|
||||
bindMeta.Constants = cb;
|
||||
bindMeta.Input = nullptr;
|
||||
bindMeta.Buffers = nullptr;
|
||||
bindMeta.Buffers = params.RenderContext.Buffers;
|
||||
bindMeta.CanSampleDepth = false;
|
||||
bindMeta.CanSampleGBuffer = false;
|
||||
MaterialParams::Bind(params.ParamsLink, bindMeta);
|
||||
|
||||
@@ -89,12 +89,12 @@ namespace
|
||||
}
|
||||
else
|
||||
{
|
||||
auto v = Half2(0, 0);
|
||||
auto v = Half2::Zero;
|
||||
for (uint32 i = 0; i < vertexCount; i++)
|
||||
vb1[i].TexCoord = v;
|
||||
}
|
||||
{
|
||||
auto v = Half2(0, 0);
|
||||
auto v = Half2::Zero;
|
||||
for (uint32 i = 0; i < vertexCount; i++)
|
||||
vb1[i].LightmapUVs = v;
|
||||
}
|
||||
|
||||
@@ -531,6 +531,17 @@ void MeshData::TransformBuffer(const Matrix& matrix)
|
||||
}
|
||||
}
|
||||
|
||||
void MeshData::NormalizeBlendWeights()
|
||||
{
|
||||
ASSERT(Positions.Count() == BlendWeights.Count());
|
||||
for (int32 i = 0; i < Positions.Count(); i++)
|
||||
{
|
||||
const float sum = BlendWeights[i].SumValues();
|
||||
const float invSum = sum > ZeroTolerance ? 1.0f / sum : 0.0f;
|
||||
BlendWeights[i] *= invSum;
|
||||
}
|
||||
}
|
||||
|
||||
void MeshData::Merge(MeshData& other)
|
||||
{
|
||||
// Merge index buffer (and remap indices)
|
||||
@@ -600,6 +611,21 @@ bool MaterialSlotEntry::UsesProperties() const
|
||||
Normals.TextureIndex != -1;
|
||||
}
|
||||
|
||||
BoundingBox ModelLodData::GetBox() const
|
||||
{
|
||||
if (Meshes.IsEmpty())
|
||||
return BoundingBox::Empty;
|
||||
BoundingBox bounds;
|
||||
Meshes[0]->CalculateBox(bounds);
|
||||
for (int32 i = 1; i < Meshes.Count(); i++)
|
||||
{
|
||||
BoundingBox b;
|
||||
Meshes[i]->CalculateBox(b);
|
||||
BoundingBox::Merge(bounds, b, bounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
void ModelData::CalculateLODsScreenSizes()
|
||||
{
|
||||
const float autoComputeLodPowerBase = 0.5f;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Common.h"
|
||||
#include "Engine/Core/Math/BoundingSphere.h"
|
||||
#include "Engine/Core/Math/BoundingBox.h"
|
||||
#include "Engine/Core/Math/Int4.h"
|
||||
@@ -281,16 +280,7 @@ public:
|
||||
/// <summary>
|
||||
/// Normalizes the blend weights. Requires to have vertices with positions and blend weights setup.
|
||||
/// </summary>
|
||||
void NormalizeBlendWeights()
|
||||
{
|
||||
ASSERT(Positions.Count() == BlendWeights.Count());
|
||||
for (int32 i = 0; i < Positions.Count(); i++)
|
||||
{
|
||||
const float sum = BlendWeights[i].SumValues();
|
||||
const float invSum = sum > ZeroTolerance ? 1.0f / sum : 0.0f;
|
||||
BlendWeights[i] *= invSum;
|
||||
}
|
||||
}
|
||||
void NormalizeBlendWeights();
|
||||
|
||||
/// <summary>
|
||||
/// Merges this mesh data with the specified other mesh.
|
||||
@@ -409,6 +399,11 @@ public:
|
||||
{
|
||||
Meshes.ClearDelete();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box combined for all meshes in this model LOD.
|
||||
/// </summary>
|
||||
BoundingBox GetBox() const;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -404,7 +404,7 @@ bool UpdateMesh(SkinnedMesh* mesh, MonoArray* verticesObj, MonoArray* trianglesO
|
||||
}
|
||||
else
|
||||
{
|
||||
auto v = Half2(0, 0);
|
||||
auto v = Half2::Zero;
|
||||
for (uint32 i = 0; i < vertexCount; i++)
|
||||
vb[i].TexCoord = v;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@ RenderBuffers::RenderBuffers(const SpawnParams& params)
|
||||
#undef CREATE_TEXTURE
|
||||
}
|
||||
|
||||
String RenderBuffers::CustomBuffer::ToString() const
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
RenderBuffers::~RenderBuffers()
|
||||
{
|
||||
Release();
|
||||
@@ -61,6 +66,15 @@ void RenderBuffers::Prepare()
|
||||
UPDATE_LAZY_KEEP_RT(HalfResDepth);
|
||||
UPDATE_LAZY_KEEP_RT(LuminanceMap);
|
||||
#undef UPDATE_LAZY_KEEP_RT
|
||||
for (int32 i = CustomBuffers.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
CustomBuffer* e = CustomBuffers[i];
|
||||
if (frameIndex - e->LastFrameUsed >= LAZY_FRAMES_COUNT)
|
||||
{
|
||||
Delete(e);
|
||||
CustomBuffers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
|
||||
@@ -184,4 +198,5 @@ void RenderBuffers::Release()
|
||||
UPDATE_LAZY_KEEP_RT(HalfResDepth);
|
||||
UPDATE_LAZY_KEEP_RT(LuminanceMap);
|
||||
#undef UPDATE_LAZY_KEEP_RT
|
||||
CustomBuffers.ClearDelete();
|
||||
}
|
||||
|
||||
@@ -16,23 +16,36 @@
|
||||
#define GBUFFER2_FORMAT PixelFormat::R8G8B8A8_UNorm
|
||||
#define GBUFFER3_FORMAT PixelFormat::R8G8B8A8_UNorm
|
||||
|
||||
// Light accumulation buffer format (direct+indirect light, materials emissive)
|
||||
#define LIGHT_BUFFER_FORMAT PixelFormat::R11G11B10_Float
|
||||
|
||||
/// <summary>
|
||||
/// The scene rendering buffers container.
|
||||
/// </summary>
|
||||
API_CLASS() class FLAXENGINE_API RenderBuffers : public ScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE(RenderBuffers);
|
||||
protected:
|
||||
DECLARE_SCRIPTING_TYPE(RenderBuffers);
|
||||
|
||||
/// <summary>
|
||||
/// The custom rendering state.
|
||||
/// </summary>
|
||||
class CustomBuffer : public Object
|
||||
{
|
||||
public:
|
||||
String Name;
|
||||
uint64 LastFrameUsed = 0;
|
||||
|
||||
String ToString() const override;
|
||||
};
|
||||
|
||||
protected:
|
||||
int32 _width = 0;
|
||||
int32 _height = 0;
|
||||
float _aspectRatio = 0.0f;
|
||||
Viewport _viewport;
|
||||
|
||||
Array<GPUTexture*, FixedAllocation<32>> _resources;
|
||||
|
||||
public:
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
@@ -80,15 +93,16 @@ public:
|
||||
GPUTexture* TemporalAA = nullptr;
|
||||
uint64 LastFrameTemporalAA = 0;
|
||||
|
||||
public:
|
||||
// Maps the custom buffer type into the object that holds the state.
|
||||
Array<CustomBuffer*, HeapAllocation> CustomBuffers;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="RenderBuffers"/> class.
|
||||
/// </summary>
|
||||
~RenderBuffers();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Prepares buffers for rendering a scene. Called before rendering so other parts can reuse calculated value.
|
||||
/// </summary>
|
||||
@@ -102,7 +116,6 @@ public:
|
||||
GPUTexture* RequestHalfResDepth(GPUContext* context);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the buffers width (in pixels).
|
||||
/// </summary>
|
||||
@@ -143,6 +156,31 @@ public:
|
||||
return _viewport;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
const T* FindCustomBuffer(const StringView& name) const
|
||||
{
|
||||
for (CustomBuffer* e : CustomBuffers)
|
||||
{
|
||||
if (e->Name == name)
|
||||
return (const T*)e;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T* GetCustomBuffer(const StringView& name)
|
||||
{
|
||||
for (CustomBuffer* e : CustomBuffers)
|
||||
{
|
||||
if (e->Name == name)
|
||||
return (T*)e;
|
||||
}
|
||||
CustomBuffer* result = New<T>();
|
||||
result->Name = name;
|
||||
CustomBuffers.Add(result);
|
||||
return (T*)result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current GPU memory usage by all the buffers (in bytes).
|
||||
/// </summary>
|
||||
@@ -162,7 +200,6 @@ public:
|
||||
API_FIELD(ReadOnly) GPUTexture* MotionVectors;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Allocates the buffers.
|
||||
/// </summary>
|
||||
|
||||
@@ -229,6 +229,8 @@ SceneRenderTask::~SceneRenderTask()
|
||||
{
|
||||
if (Buffers)
|
||||
Buffers->DeleteObjectNow();
|
||||
if (_customActorsScene)
|
||||
Delete(_customActorsScene);
|
||||
}
|
||||
|
||||
void SceneRenderTask::CameraCut()
|
||||
@@ -270,18 +272,29 @@ void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
|
||||
}
|
||||
}
|
||||
|
||||
void AddActorToSceneRendering(SceneRendering* s, Actor* a)
|
||||
{
|
||||
if (a && a->IsActiveInHierarchy())
|
||||
{
|
||||
int32 key = -1;
|
||||
s->AddActor(a, key);
|
||||
for (Actor* child : a->Children)
|
||||
AddActorToSceneRendering(s, child);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext)
|
||||
{
|
||||
// Draw actors (collect draw calls)
|
||||
if ((ActorsSource & ActorsSources::CustomActors) != 0)
|
||||
{
|
||||
for (auto a : CustomActors)
|
||||
{
|
||||
if (a && a->GetIsActive())
|
||||
{
|
||||
a->DrawHierarchy(renderContext);
|
||||
}
|
||||
}
|
||||
if (_customActorsScene == nullptr)
|
||||
_customActorsScene = New<SceneRendering>();
|
||||
else
|
||||
_customActorsScene->Clear();
|
||||
for (Actor* a : CustomActors)
|
||||
AddActorToSceneRendering(_customActorsScene, a);
|
||||
_customActorsScene->Draw(renderContext);
|
||||
}
|
||||
if ((ActorsSource & ActorsSources::Scenes) != 0)
|
||||
{
|
||||
@@ -403,7 +416,7 @@ void SceneRenderTask::OnEnd(GPUContext* context)
|
||||
// Swap matrices
|
||||
View.PrevView = View.View;
|
||||
View.PrevProjection = View.Projection;
|
||||
Matrix::Multiply(View.PrevView, View.PrevProjection, View.PrevViewProjection);
|
||||
View.PrevViewProjection = View.ViewProjection();
|
||||
}
|
||||
|
||||
bool SceneRenderTask::Resize(int32 width, int32 height)
|
||||
|
||||
@@ -220,6 +220,10 @@ public:
|
||||
API_CLASS() class FLAXENGINE_API SceneRenderTask : public RenderTask
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE(SceneRenderTask);
|
||||
protected:
|
||||
class SceneRendering* _customActorsScene = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="SceneRenderTask"/> class.
|
||||
|
||||
@@ -97,6 +97,16 @@ public:
|
||||
/// </summary>
|
||||
API_FIELD() bool IsOfflinePass = false;
|
||||
|
||||
/// <summary>
|
||||
/// Flag used by single-frame rendering passes (eg. thumbnail rendering, model view caching) to reject LOD transitions animations and other temporal draw effects.
|
||||
/// </summary>
|
||||
API_FIELD() bool IsSingleFrame = false;
|
||||
|
||||
/// <summary>
|
||||
/// Flag used by custom passes to skip any object culling when drawing.
|
||||
/// </summary>
|
||||
API_FIELD() bool IsCullingDisabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// The static flags mask used to hide objects that don't have a given static flags. Eg. use StaticFlags::Lightmap to render only objects that can use lightmap.
|
||||
/// </summary>
|
||||
|
||||
@@ -282,16 +282,18 @@ bool ShaderAssetBase::LoadShaderCache(ShaderCacheResult& result)
|
||||
editorDefine.Definition = "1";
|
||||
#endif
|
||||
InitCompilationOptions(options);
|
||||
if (ShadersCompilation::Compile(options))
|
||||
const bool failed = ShadersCompilation::Compile(options);
|
||||
|
||||
// Encrypt source code
|
||||
Encryption::EncryptBytes(reinterpret_cast<byte*>(source), sourceLength);
|
||||
|
||||
if (failed)
|
||||
{
|
||||
LOG(Error, "Failed to compile shader '{0}'", parent->ToString());
|
||||
return true;
|
||||
}
|
||||
LOG(Info, "Shader '{0}' compiled! Cache size: {1} bytes", parent->ToString(), cacheStream.GetPosition());
|
||||
|
||||
// Encrypt source code
|
||||
Encryption::EncryptBytes(reinterpret_cast<byte*>(source), sourceLength);
|
||||
|
||||
// Save compilation result (based on current caching policy)
|
||||
if (cachingMode == ShaderStorage::CachingMode::AssetInternal)
|
||||
{
|
||||
@@ -345,7 +347,7 @@ bool ShaderAssetBase::LoadShaderCache(ShaderCacheResult& result)
|
||||
result.Data.Link(cacheChunk->Data);
|
||||
}
|
||||
#if COMPILE_WITH_SHADER_CACHE_MANAGER
|
||||
// Check if has cached shader
|
||||
// Check if has cached shader
|
||||
else if (cachedEntry.IsValid() || ShaderCacheManager::TryGetEntry(shaderProfile, parent->GetID(), cachedEntry))
|
||||
{
|
||||
// Load results from cache
|
||||
|
||||
@@ -12,7 +12,7 @@ class GPUShaderProgram;
|
||||
/// <summary>
|
||||
/// The runtime version of the shaders cache supported by the all graphics back-ends. The same for all the shader cache formats (easier to sync and validate).
|
||||
/// </summary>
|
||||
#define GPU_SHADER_CACHE_VERSION 8
|
||||
#define GPU_SHADER_CACHE_VERSION 9
|
||||
|
||||
/// <summary>
|
||||
/// Represents collection of shader programs with permutations and custom names.
|
||||
|
||||
@@ -431,11 +431,6 @@ bool GPUTexture::Init(const GPUTextureDescription& desc)
|
||||
LOG(Warning, "Cannot create texture. Only 2D Texture can be used as a Depth Stencil. Description: {0}", desc.ToString());
|
||||
return true;
|
||||
}
|
||||
if (desc.MipLevels != 1)
|
||||
{
|
||||
LOG(Warning, "Cannot create texture. Volume texture cannot have more than 1 mip level. Description: {0}", desc.ToString());
|
||||
return true;
|
||||
}
|
||||
if (desc.ArraySize != 1)
|
||||
{
|
||||
LOG(Warning, "Cannot create texture. Volume texture cannot create array of volume textures. Description: {0}", desc.ToString());
|
||||
@@ -607,7 +602,25 @@ GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipInde
|
||||
ASSERT(IsAllocated());
|
||||
ASSERT(mipIndex < MipLevels() && data.IsValid());
|
||||
ASSERT(data.Length() >= slicePitch);
|
||||
// TODO: support texture data upload to the GPU on a main thread during rendering without this async task (faster direct upload)
|
||||
|
||||
// Optimize texture upload invoked during rendering
|
||||
if (IsInMainThread() && GPUDevice::Instance->IsRendering())
|
||||
{
|
||||
// Update all array slices
|
||||
const byte* dataSource = data.Get();
|
||||
for (int32 arrayIndex = 0; arrayIndex < _desc.ArraySize; arrayIndex++)
|
||||
{
|
||||
GPUDevice::Instance->GetMainContext()->UpdateTexture(this, arrayIndex, mipIndex, dataSource, rowPitch, slicePitch);
|
||||
dataSource += slicePitch;
|
||||
}
|
||||
if (mipIndex == HighestResidentMipIndex() - 1)
|
||||
{
|
||||
// Mark as mip loaded
|
||||
SetResidentMipLevels(ResidentMipLevels() + 1);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto task = ::New<GPUUploadTextureMipTask>(this, mipIndex, data, rowPitch, slicePitch, copyData);
|
||||
ASSERT_LOW_LAYER(task && task->HasReference(this));
|
||||
return task;
|
||||
@@ -800,5 +813,6 @@ void GPUTexture::SetResidentMipLevels(int32 count)
|
||||
if (_residentMipLevels == count || !IsRegularTexture())
|
||||
return;
|
||||
_residentMipLevels = count;
|
||||
onResidentMipsChanged();
|
||||
OnResidentMipsChanged();
|
||||
ResidentMipsChanged(this);
|
||||
}
|
||||
|
||||
@@ -568,12 +568,17 @@ public:
|
||||
/// Sets the number of resident mipmap levels in the texture (already uploaded to the GPU).
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetResidentMipLevels(int32 count);
|
||||
|
||||
/// <summary>
|
||||
/// Event called when texture residency gets changed. Texture Mip gets loaded into GPU memory and is ready to use.
|
||||
/// </summary>
|
||||
Delegate<GPUTexture*> ResidentMipsChanged;
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool OnInit() = 0;
|
||||
uint64 calculateMemoryUsage() const;
|
||||
virtual void onResidentMipsChanged() = 0;
|
||||
virtual void OnResidentMipsChanged() = 0;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -233,6 +233,7 @@ protected:
|
||||
{
|
||||
Swap(_streamingTexture->_texture, _newTexture);
|
||||
_streamingTexture->GetTexture()->SetResidentMipLevels(_uploadedMipCount);
|
||||
_streamingTexture->ResidencyChanged();
|
||||
SAFE_DELETE_GPU_RESOURCE(_newTexture);
|
||||
|
||||
// Base
|
||||
@@ -447,6 +448,7 @@ Task* StreamingTexture::CreateStreamingTask(int32 residency)
|
||||
{
|
||||
// Do the quick data release
|
||||
_texture->ReleaseGPU();
|
||||
ResidencyChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -732,7 +732,9 @@ void GPUContextDX11::UpdateTexture(GPUTexture* texture, int32 arrayIndex, int32
|
||||
auto textureDX11 = static_cast<GPUTextureDX11*>(texture);
|
||||
|
||||
const int32 subresourceIndex = RenderToolsDX::CalcSubresourceIndex(mipIndex, arrayIndex, texture->MipLevels());
|
||||
const uint32 depthPitch = texture->IsVolume() ? slicePitch / texture->Depth() : slicePitch;
|
||||
uint32 depthPitch = slicePitch;
|
||||
if (texture->IsVolume())
|
||||
depthPitch /= Math::Max(1, texture->Depth() >> mipIndex);
|
||||
_context->UpdateSubresource(textureDX11->GetResource(), subresourceIndex, nullptr, data, (UINT)rowPitch, (UINT)depthPitch);
|
||||
|
||||
//D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
|
||||
@@ -518,12 +518,12 @@ bool GPUDeviceDX11::Init()
|
||||
dsDesc.FrontFace = defaultStencilOp;
|
||||
dsDesc.BackFace = defaultStencilOp;
|
||||
int32 index;
|
||||
#define CREATE_DEPTH_STENCIL_STATE(depthTextEnable, depthWrite) \
|
||||
dsDesc.DepthEnable = depthTextEnable; \
|
||||
#define CREATE_DEPTH_STENCIL_STATE(depthEnable, depthWrite) \
|
||||
dsDesc.DepthEnable = depthEnable; \
|
||||
dsDesc.DepthWriteMask = depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; \
|
||||
for(int32 depthFunc = 1; depthFunc <= 8; depthFunc++) { \
|
||||
dsDesc.DepthFunc = (D3D11_COMPARISON_FUNC)depthFunc; \
|
||||
index = (int32)depthFunc + (depthTextEnable ? 0 : 9) + (depthWrite ? 0 : 18); \
|
||||
index = (int32)depthFunc + (depthEnable ? 0 : 9) + (depthWrite ? 0 : 18); \
|
||||
HRESULT result = _device->CreateDepthStencilState(&dsDesc, &DepthStencilStates[index]); \
|
||||
LOG_DIRECTX_RESULT_WITH_RETURN(result); }
|
||||
CREATE_DEPTH_STENCIL_STATE(false, false);
|
||||
|
||||
@@ -109,37 +109,37 @@ bool GPUTextureDX11::OnInit()
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUTextureDX11::onResidentMipsChanged()
|
||||
void GPUTextureDX11::OnResidentMipsChanged()
|
||||
{
|
||||
// We support changing resident mip maps only for regular textures (render targets and depth buffers don't use that feature at all)
|
||||
ASSERT(IsRegularTexture() && _handlesPerSlice.Count() == 1);
|
||||
ASSERT(!IsVolume());
|
||||
|
||||
// Fill description
|
||||
const int32 firstMipIndex = MipLevels() - ResidentMipLevels();
|
||||
const int32 mipLevels = ResidentMipLevels();
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srDesc;
|
||||
srDesc.Format = _dxgiFormatSRV;
|
||||
if (IsCubeMap())
|
||||
{
|
||||
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
|
||||
srDesc.TextureCube.MostDetailedMip = MipLevels() - ResidentMipLevels();
|
||||
srDesc.TextureCube.MipLevels = ResidentMipLevels();
|
||||
srDesc.TextureCube.MostDetailedMip = firstMipIndex;
|
||||
srDesc.TextureCube.MipLevels = mipLevels;
|
||||
}
|
||||
else if (IsVolume())
|
||||
{
|
||||
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
|
||||
srDesc.Texture3D.MostDetailedMip = firstMipIndex;
|
||||
srDesc.Texture3D.MipLevels = mipLevels;
|
||||
}
|
||||
else
|
||||
{
|
||||
srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srDesc.Texture2D.MostDetailedMip = MipLevels() - ResidentMipLevels();
|
||||
srDesc.Texture2D.MipLevels = ResidentMipLevels();
|
||||
srDesc.Texture2D.MostDetailedMip = firstMipIndex;
|
||||
srDesc.Texture2D.MipLevels = mipLevels;
|
||||
}
|
||||
|
||||
// Create new view
|
||||
ID3D11ShaderResourceView* srView;
|
||||
VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateShaderResourceView(_resource, &srDesc, &srView));
|
||||
|
||||
// Change view
|
||||
if (_handlesPerSlice[0].GetParent() == nullptr)
|
||||
_handlesPerSlice[0].Init(this, nullptr, srView, nullptr, nullptr, Format(), MultiSampleLevel());
|
||||
GPUTextureViewDX11& view = IsVolume() ? _handleVolume : _handlesPerSlice[0];
|
||||
if (view.GetParent() == nullptr)
|
||||
view.Init(this, nullptr, srView, nullptr, nullptr, Format(), MultiSampleLevel());
|
||||
else
|
||||
_handlesPerSlice[0].SetSRV(srView);
|
||||
view.SetSRV(srView);
|
||||
}
|
||||
|
||||
void GPUTextureDX11::OnReleaseGPU()
|
||||
|
||||
@@ -277,7 +277,7 @@ protected:
|
||||
|
||||
// [GPUTexture]
|
||||
bool OnInit() override;
|
||||
void onResidentMipsChanged() override;
|
||||
void OnResidentMipsChanged() override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -208,36 +208,39 @@ bool GPUTextureDX12::OnInit()
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUTextureDX12::onResidentMipsChanged()
|
||||
void GPUTextureDX12::OnResidentMipsChanged()
|
||||
{
|
||||
// We support changing resident mip maps only for regular textures (render targets and depth buffers don't use that feature at all)
|
||||
ASSERT(IsRegularTexture() && _handlesPerSlice.Count() == 1);
|
||||
ASSERT(!IsVolume());
|
||||
|
||||
// Fill description
|
||||
const int32 firstMipIndex = MipLevels() - ResidentMipLevels();
|
||||
const int32 mipLevels = ResidentMipLevels();
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srDesc;
|
||||
srDesc.Format = _dxgiFormatSRV;
|
||||
srDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
if (IsCubeMap())
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
|
||||
srDesc.TextureCube.MostDetailedMip = MipLevels() - ResidentMipLevels();
|
||||
srDesc.TextureCube.MipLevels = ResidentMipLevels();
|
||||
srDesc.TextureCube.MostDetailedMip = firstMipIndex;
|
||||
srDesc.TextureCube.MipLevels = mipLevels;
|
||||
srDesc.TextureCube.ResourceMinLODClamp = 0;
|
||||
}
|
||||
else if (IsVolume())
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
|
||||
srDesc.Texture3D.MostDetailedMip = firstMipIndex;
|
||||
srDesc.Texture3D.MipLevels = mipLevels;
|
||||
srDesc.Texture3D.ResourceMinLODClamp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srDesc.Texture2D.MostDetailedMip = MipLevels() - ResidentMipLevels();
|
||||
srDesc.Texture2D.MipLevels = ResidentMipLevels();
|
||||
srDesc.Texture2D.MostDetailedMip = firstMipIndex;
|
||||
srDesc.Texture2D.MipLevels = mipLevels;
|
||||
srDesc.Texture2D.PlaneSlice = 0;
|
||||
srDesc.Texture2D.ResourceMinLODClamp = 0;
|
||||
}
|
||||
|
||||
// Change view
|
||||
if (_handlesPerSlice[0].GetParent() == nullptr)
|
||||
_handlesPerSlice[0].Init(this, _device, this, Format(), MultiSampleLevel());
|
||||
_handlesPerSlice[0].SetSRV(srDesc);
|
||||
GPUTextureViewDX12& view = IsVolume() ? _handleVolume : _handlesPerSlice[0];
|
||||
if (view.GetParent() == nullptr)
|
||||
view.Init(this, _device, this, Format(), MultiSampleLevel());
|
||||
view.SetSRV(srDesc);
|
||||
}
|
||||
|
||||
void GPUTextureDX12::OnReleaseGPU()
|
||||
|
||||
@@ -216,7 +216,7 @@ protected:
|
||||
|
||||
// [GPUTexture]
|
||||
bool OnInit() override;
|
||||
void onResidentMipsChanged() override;
|
||||
void OnResidentMipsChanged() override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,29 +90,30 @@ bool UploadBufferDX12::UploadTexture(GPUContextDX12* context, ID3D12Resource* te
|
||||
_device->GetDevice()->GetCopyableFootprints(&resourceDesc, subresourceIndex, 1, 0, &footprint, &numRows, &rowPitchAligned, &mipSizeAligned);
|
||||
rowPitchAligned = footprint.Footprint.RowPitch;
|
||||
mipSizeAligned = rowPitchAligned * footprint.Footprint.Height;
|
||||
const uint32 numSlices = resourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? Math::Max(1, resourceDesc.DepthOrArraySize >> mipIndex) : 1;
|
||||
const uint64 sliceSizeAligned = numSlices * mipSizeAligned;
|
||||
|
||||
// Allocate data
|
||||
const DynamicAllocation allocation = Allocate(mipSizeAligned, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
if (allocation.Size != mipSizeAligned)
|
||||
const DynamicAllocation allocation = Allocate(sliceSizeAligned, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
if (allocation.Size != sliceSizeAligned)
|
||||
return true;
|
||||
|
||||
// Check if can copy rows at once
|
||||
byte* ptr = (byte*)srcData;
|
||||
ASSERT(srcSlicePitch <= mipSizeAligned);
|
||||
if (srcRowPitch == rowPitchAligned)
|
||||
ASSERT(srcSlicePitch <= sliceSizeAligned);
|
||||
if (srcSlicePitch == sliceSizeAligned)
|
||||
{
|
||||
// Copy data at once
|
||||
Platform::MemoryCopy(allocation.CPUAddress, ptr, srcSlicePitch);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use per row copy
|
||||
// Copy data per-row
|
||||
byte* dst = static_cast<byte*>(allocation.CPUAddress);
|
||||
ASSERT(srcRowPitch <= rowPitchAligned);
|
||||
for (uint32 i = 0; i < numRows; i++)
|
||||
const uint32 numCopies = numSlices * numRows;
|
||||
for (uint32 i = 0; i < numCopies; i++)
|
||||
{
|
||||
Platform::MemoryCopy(dst, ptr, srcRowPitch);
|
||||
|
||||
dst += rowPitchAligned;
|
||||
ptr += srcRowPitch;
|
||||
}
|
||||
|
||||
@@ -141,9 +141,8 @@ struct DynamicAllocation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if allocation is invalid
|
||||
/// Returns true if allocation is invalid.
|
||||
/// </summary>
|
||||
/// <returns>True if allocation in invalid</returns>
|
||||
bool IsInvalid() const
|
||||
{
|
||||
return CPUAddress == nullptr || Size == 0 || Page == nullptr;
|
||||
|
||||
@@ -212,16 +212,17 @@ namespace RenderToolsDX
|
||||
if (errorCode == DXGI_ERROR_DEVICE_REMOVED || errorCode == DXGI_ERROR_DEVICE_RESET || errorCode == DXGI_ERROR_DRIVER_INTERNAL_ERROR)
|
||||
{
|
||||
HRESULT reason = S_OK;
|
||||
const RendererType rendererType = GPUDevice::Instance ? GPUDevice::Instance->GetRendererType() : RendererType::Unknown;
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
if (GPUDevice::Instance->GetRendererType() == RendererType::DirectX12)
|
||||
if (rendererType == RendererType::DirectX12)
|
||||
{
|
||||
reason = ((ID3D12Device*)GPUDevice::Instance->GetNativePtr())->GetDeviceRemovedReason();
|
||||
}
|
||||
#endif
|
||||
#if GRAPHICS_API_DIRECTX11
|
||||
if (GPUDevice::Instance->GetRendererType() == RendererType::DirectX11 ||
|
||||
GPUDevice::Instance->GetRendererType() == RendererType::DirectX10_1 ||
|
||||
GPUDevice::Instance->GetRendererType() == RendererType::DirectX10)
|
||||
if (rendererType == RendererType::DirectX11 ||
|
||||
rendererType == RendererType::DirectX10_1 ||
|
||||
rendererType == RendererType::DirectX10)
|
||||
{
|
||||
reason = ((ID3D11Device*)GPUDevice::Instance->GetNativePtr())->GetDeviceRemovedReason();
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ protected:
|
||||
return false;
|
||||
}
|
||||
|
||||
void onResidentMipsChanged() override
|
||||
void OnResidentMipsChanged() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "GPUDeviceVulkan.h"
|
||||
#include "RenderToolsVulkan.h"
|
||||
#include "GPUContextVulkan.h"
|
||||
#include "GPUAdapterVulkan.h"
|
||||
#include "CmdBufferVulkan.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
@@ -30,39 +29,48 @@ void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
|
||||
_typesUsageID = id;
|
||||
}
|
||||
|
||||
void DescriptorSetLayoutInfoVulkan::AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor)
|
||||
{
|
||||
_layoutTypes[descriptor.descriptorType]++;
|
||||
|
||||
if (descriptorSetIndex >= _setLayouts.Count())
|
||||
{
|
||||
_setLayouts.Resize(descriptorSetIndex + 1);
|
||||
}
|
||||
|
||||
SetLayout& descSetLayout = _setLayouts[descriptorSetIndex];
|
||||
descSetLayout.LayoutBindings.Add(descriptor);
|
||||
|
||||
_hash = Crc::MemCrc32(&descriptor, sizeof(descriptor), _hash);
|
||||
}
|
||||
|
||||
void DescriptorSetLayoutInfoVulkan::AddBindingsForStage(VkShaderStageFlagBits stageFlags, DescriptorSet::Stage descSet, const SpirvShaderDescriptorInfo* descriptorInfo)
|
||||
{
|
||||
const int32 descriptorSetIndex = (int32)descSet;
|
||||
const int32 descriptorSetIndex = descSet;
|
||||
if (descriptorSetIndex >= _setLayouts.Count())
|
||||
_setLayouts.Resize(descriptorSetIndex + 1);
|
||||
SetLayout& descSetLayout = _setLayouts[descriptorSetIndex];
|
||||
|
||||
VkDescriptorSetLayoutBinding binding;
|
||||
binding.descriptorCount = 1;
|
||||
binding.stageFlags = stageFlags;
|
||||
binding.pImmutableSamplers = nullptr;
|
||||
|
||||
for (uint32 descriptorIndex = 0; descriptorIndex < descriptorInfo->DescriptorTypesCount; descriptorIndex++)
|
||||
{
|
||||
auto& descriptor = descriptorInfo->DescriptorTypes[descriptorIndex];
|
||||
binding.binding = descriptorIndex;
|
||||
binding.descriptorType = descriptor.DescriptorType;
|
||||
AddDescriptor(descriptorSetIndex, binding);
|
||||
binding.descriptorCount = descriptor.Count;
|
||||
|
||||
_layoutTypes[binding.descriptorType]++;
|
||||
descSetLayout.LayoutBindings.Add(binding);
|
||||
_hash = Crc::MemCrc32(&binding, sizeof(binding), _hash);
|
||||
}
|
||||
}
|
||||
|
||||
bool DescriptorSetLayoutInfoVulkan::operator==(const DescriptorSetLayoutInfoVulkan& other) const
|
||||
{
|
||||
if (other._setLayouts.Count() != _setLayouts.Count())
|
||||
return false;
|
||||
if (other._typesUsageID != _typesUsageID)
|
||||
return false;
|
||||
|
||||
for (int32 index = 0; index < other._setLayouts.Count(); index++)
|
||||
{
|
||||
const int32 bindingsCount = _setLayouts[index].LayoutBindings.Count();
|
||||
if (other._setLayouts[index].LayoutBindings.Count() != bindingsCount)
|
||||
return false;
|
||||
if (bindingsCount != 0 && Platform::MemoryCompare(other._setLayouts[index].LayoutBindings.Get(), _setLayouts[index].LayoutBindings.Get(), bindingsCount * sizeof(VkDescriptorSetLayoutBinding)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DescriptorSetLayoutVulkan::DescriptorSetLayoutVulkan(GPUDeviceVulkan* device)
|
||||
: _device(device)
|
||||
{
|
||||
@@ -370,20 +378,20 @@ PipelineLayoutVulkan::~PipelineLayoutVulkan()
|
||||
}
|
||||
}
|
||||
|
||||
uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, uint8* bindingToDynamicOffset)
|
||||
uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, VkBufferView* texelBufferView, uint8* bindingToDynamicOffset)
|
||||
{
|
||||
ASSERT(info.DescriptorTypesCount <= 64);
|
||||
ASSERT(info.DescriptorTypesCount <= SpirvShaderDescriptorInfo::MaxDescriptors);
|
||||
WriteDescriptors = writeDescriptors;
|
||||
WritesCount = info.DescriptorTypesCount;
|
||||
BindingToDynamicOffset = bindingToDynamicOffset;
|
||||
|
||||
uint32 dynamicOffsetIndex = 0;
|
||||
for (uint32 i = 0; i < info.DescriptorTypesCount; i++)
|
||||
{
|
||||
const auto& descriptor = info.DescriptorTypes[i];
|
||||
writeDescriptors->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writeDescriptors->dstBinding = i;
|
||||
writeDescriptors->descriptorCount = 1;
|
||||
writeDescriptors->descriptorType = info.DescriptorTypes[i].DescriptorType;
|
||||
writeDescriptors->descriptorCount = descriptor.Count;
|
||||
writeDescriptors->descriptorType = descriptor.DescriptorType;
|
||||
|
||||
switch (writeDescriptors->descriptorType)
|
||||
{
|
||||
@@ -394,25 +402,28 @@ uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescrip
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
writeDescriptors->pBufferInfo = bufferInfo++;
|
||||
writeDescriptors->pBufferInfo = bufferInfo;
|
||||
bufferInfo += descriptor.Count;
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
writeDescriptors->pImageInfo = imageInfo++;
|
||||
writeDescriptors->pImageInfo = imageInfo;
|
||||
imageInfo += descriptor.Count;
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
writeDescriptors->pTexelBufferView = texelBufferView;
|
||||
texelBufferView += descriptor.Count;
|
||||
break;
|
||||
default:
|
||||
CRASH;
|
||||
CRASH;
|
||||
break;
|
||||
}
|
||||
|
||||
writeDescriptors++;
|
||||
}
|
||||
|
||||
return dynamicOffsetIndex;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,6 @@ protected:
|
||||
uint32 _typesUsageID = ~0;
|
||||
|
||||
void CacheTypesUsageID();
|
||||
void AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor);
|
||||
|
||||
public:
|
||||
|
||||
@@ -138,11 +137,6 @@ public:
|
||||
return _setLayouts;
|
||||
}
|
||||
|
||||
inline const uint32* GetLayoutTypes() const
|
||||
{
|
||||
return _layoutTypes;
|
||||
}
|
||||
|
||||
inline uint32 GetTypesUsageID() const
|
||||
{
|
||||
return _typesUsageID;
|
||||
@@ -160,25 +154,7 @@ public:
|
||||
_setLayouts = info._setLayouts;
|
||||
}
|
||||
|
||||
inline bool operator ==(const DescriptorSetLayoutInfoVulkan& other) const
|
||||
{
|
||||
if (other._setLayouts.Count() != _setLayouts.Count())
|
||||
return false;
|
||||
if (other._typesUsageID != _typesUsageID)
|
||||
return false;
|
||||
|
||||
for (int32 index = 0; index < other._setLayouts.Count(); index++)
|
||||
{
|
||||
const int32 bindingsCount = _setLayouts[index].LayoutBindings.Count();
|
||||
if (other._setLayouts[index].LayoutBindings.Count() != bindingsCount)
|
||||
return false;
|
||||
|
||||
if (bindingsCount != 0 && Platform::MemoryCompare(other._setLayouts[index].LayoutBindings.Get(), _setLayouts[index].LayoutBindings.Get(), bindingsCount * sizeof(VkDescriptorSetLayoutBinding)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool operator==(const DescriptorSetLayoutInfoVulkan& other) const;
|
||||
|
||||
friend inline uint32 GetHash(const DescriptorSetLayoutInfoVulkan& key)
|
||||
{
|
||||
@@ -413,6 +389,7 @@ struct DescriptorSetWriteContainerVulkan
|
||||
{
|
||||
Array<VkDescriptorImageInfo> DescriptorImageInfo;
|
||||
Array<VkDescriptorBufferInfo> DescriptorBufferInfo;
|
||||
Array<VkBufferView> DescriptorTexelBufferView;
|
||||
Array<VkWriteDescriptorSet> DescriptorWrites;
|
||||
Array<byte> BindingToDynamicOffset;
|
||||
|
||||
@@ -420,6 +397,7 @@ struct DescriptorSetWriteContainerVulkan
|
||||
{
|
||||
DescriptorImageInfo.Resize(0);
|
||||
DescriptorBufferInfo.Resize(0);
|
||||
DescriptorTexelBufferView.Resize(0);
|
||||
DescriptorWrites.Resize(0);
|
||||
BindingToDynamicOffset.Resize(0);
|
||||
}
|
||||
@@ -436,26 +414,24 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
uint32 SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, byte* bindingToDynamicOffset);
|
||||
uint32 SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, VkBufferView* texelBufferView, byte* bindingToDynamicOffset);
|
||||
|
||||
bool WriteUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) const
|
||||
bool WriteUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 index = 0) const
|
||||
{
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
||||
ASSERT(bufferInfo);
|
||||
auto* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo + index);
|
||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
||||
return edited;
|
||||
}
|
||||
|
||||
bool WriteDynamicUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 dynamicOffset) const
|
||||
bool WriteDynamicUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 dynamicOffset, uint32 index = 0) const
|
||||
{
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
|
||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
||||
ASSERT(bufferInfo);
|
||||
auto* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo + index);
|
||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
||||
@@ -464,63 +440,61 @@ public:
|
||||
return edited;
|
||||
}
|
||||
|
||||
bool WriteSampler(uint32 descriptorIndex, VkSampler sampler) const
|
||||
bool WriteSampler(uint32 descriptorIndex, VkSampler sampler, uint32 index = 0) const
|
||||
{
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
||||
ASSERT(imageInfo);
|
||||
auto* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo + index);
|
||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->sampler, sampler);
|
||||
return edited;
|
||||
}
|
||||
|
||||
bool WriteImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout) const
|
||||
bool WriteImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout, uint32 index = 0) const
|
||||
{
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
||||
ASSERT(imageInfo);
|
||||
auto* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo + index);
|
||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageView, imageView);
|
||||
edited |= DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageLayout, layout);
|
||||
return edited;
|
||||
}
|
||||
|
||||
bool WriteStorageImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout) const
|
||||
bool WriteStorageImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout, uint32 index = 0) const
|
||||
{
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
|
||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
||||
ASSERT(imageInfo);
|
||||
auto* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo + index);
|
||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageView, imageView);
|
||||
edited |= DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageLayout, layout);
|
||||
return edited;
|
||||
}
|
||||
|
||||
bool WriteStorageTexelBuffer(uint32 descriptorIndex, const VkBufferView* bufferView) const
|
||||
bool WriteStorageTexelBuffer(uint32 descriptorIndex, VkBufferView bufferView, uint32 index = 0) const
|
||||
{
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
|
||||
WriteDescriptors[descriptorIndex].pTexelBufferView = bufferView;
|
||||
auto* bufferInfo = const_cast<VkBufferView*>(WriteDescriptors[descriptorIndex].pTexelBufferView + index);
|
||||
*bufferInfo = bufferView;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteStorageBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) const
|
||||
bool WriteStorageBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 index = 0) const
|
||||
{
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
|
||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
||||
ASSERT(bufferInfo);
|
||||
auto* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo + index);
|
||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
||||
return edited;
|
||||
}
|
||||
|
||||
bool WriteUniformTexelBuffer(uint32 descriptorIndex, const VkBufferView* view) const
|
||||
bool WriteUniformTexelBuffer(uint32 descriptorIndex, VkBufferView view, uint32 index = 0) const
|
||||
{
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
|
||||
return DescriptorSet::CopyAndReturnNotEqual(WriteDescriptors[descriptorIndex].pTexelBufferView, view);
|
||||
auto* bufferInfo = const_cast<VkBufferView*>(WriteDescriptors[descriptorIndex].pTexelBufferView + index);
|
||||
return DescriptorSet::CopyAndReturnNotEqual(*bufferInfo, view);
|
||||
}
|
||||
|
||||
void SetDescriptorSet(VkDescriptorSet descriptorSet) const
|
||||
|
||||
@@ -44,10 +44,10 @@ void GPUBufferViewVulkan::Release()
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPUBufferViewVulkan::DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
|
||||
void GPUBufferViewVulkan::DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView)
|
||||
{
|
||||
ASSERT_LOW_LAYER(View != VK_NULL_HANDLE);
|
||||
bufferView = &View;
|
||||
bufferView = View;
|
||||
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
@@ -60,10 +60,10 @@ void GPUBufferViewVulkan::DescriptorAsStorageBuffer(GPUContextVulkan* context, V
|
||||
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
void GPUBufferViewVulkan::DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
|
||||
void GPUBufferViewVulkan::DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView)
|
||||
{
|
||||
ASSERT_LOW_LAYER(View != VK_NULL_HANDLE);
|
||||
bufferView = &View;
|
||||
bufferView = View;
|
||||
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ bool GPUBufferVulkan::OnInit()
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
if (useSRV && !(_desc.Flags & GPUBufferFlags::Structured))
|
||||
bufferInfo.usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
if (useUAV || _desc.Flags & GPUBufferFlags::RawBuffer)
|
||||
if (useUAV || _desc.Flags & GPUBufferFlags::RawBuffer || _desc.Flags & GPUBufferFlags::Structured)
|
||||
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
if (useUAV && useSRV)
|
||||
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
||||
|
||||
@@ -48,9 +48,9 @@ public:
|
||||
}
|
||||
|
||||
// [DescriptorOwnerResourceVulkan]
|
||||
void DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView) override;
|
||||
void DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView) override;
|
||||
void DescriptorAsStorageBuffer(GPUContextVulkan* context, VkBuffer& buffer, VkDeviceSize& offset, VkDeviceSize& range) override;
|
||||
void DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView) override;
|
||||
void DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -444,117 +444,122 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
|
||||
const auto& descriptor = descriptorInfo.DescriptorTypes[i];
|
||||
const int32 descriptorIndex = descriptor.Binding;
|
||||
DescriptorOwnerResourceVulkan** handles = _handles[(int32)descriptor.BindingType];
|
||||
|
||||
switch (descriptor.DescriptorType)
|
||||
for (uint32 index = 0; index < descriptor.Count; index++)
|
||||
{
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
{
|
||||
// Sampler
|
||||
const VkSampler sampler = _samplerHandles[descriptor.Slot];
|
||||
ASSERT(sampler);
|
||||
needsWrite |= dsWriter.WriteSampler(descriptorIndex, sampler);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
{
|
||||
// Shader Resource (Texture)
|
||||
auto handle = (GPUTextureViewVulkan*)handles[descriptor.Slot];
|
||||
if (!handle)
|
||||
const int32 slot = descriptor.Slot + index;
|
||||
switch (descriptor.DescriptorType)
|
||||
{
|
||||
const auto dummy = _device->HelperResources.GetDummyTexture(descriptor.ResourceType);
|
||||
switch (descriptor.ResourceType)
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
{
|
||||
// Sampler
|
||||
const VkSampler sampler = _samplerHandles[slot];
|
||||
ASSERT(sampler);
|
||||
needsWrite |= dsWriter.WriteSampler(descriptorIndex, sampler, index);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
{
|
||||
// Shader Resource (Texture)
|
||||
auto handle = (GPUTextureViewVulkan*)handles[slot];
|
||||
if (!handle)
|
||||
{
|
||||
case SpirvShaderResourceType::Texture1D:
|
||||
case SpirvShaderResourceType::Texture2D:
|
||||
handle = static_cast<GPUTextureViewVulkan*>(dummy->View(0));
|
||||
break;
|
||||
case SpirvShaderResourceType::Texture3D:
|
||||
handle = static_cast<GPUTextureViewVulkan*>(dummy->ViewVolume());
|
||||
break;
|
||||
case SpirvShaderResourceType::TextureCube:
|
||||
case SpirvShaderResourceType::Texture1DArray:
|
||||
case SpirvShaderResourceType::Texture2DArray:
|
||||
handle = static_cast<GPUTextureViewVulkan*>(dummy->ViewArray());
|
||||
break;
|
||||
const auto dummy = _device->HelperResources.GetDummyTexture(descriptor.ResourceType);
|
||||
switch (descriptor.ResourceType)
|
||||
{
|
||||
case SpirvShaderResourceType::Texture1D:
|
||||
case SpirvShaderResourceType::Texture2D:
|
||||
handle = static_cast<GPUTextureViewVulkan*>(dummy->View(0));
|
||||
break;
|
||||
case SpirvShaderResourceType::Texture3D:
|
||||
handle = static_cast<GPUTextureViewVulkan*>(dummy->ViewVolume());
|
||||
break;
|
||||
case SpirvShaderResourceType::TextureCube:
|
||||
case SpirvShaderResourceType::Texture1DArray:
|
||||
case SpirvShaderResourceType::Texture2DArray:
|
||||
handle = static_cast<GPUTextureViewVulkan*>(dummy->ViewArray());
|
||||
break;
|
||||
}
|
||||
}
|
||||
VkImageView imageView;
|
||||
VkImageLayout layout;
|
||||
handle->DescriptorAsImage(this, imageView, layout);
|
||||
ASSERT(imageView != VK_NULL_HANDLE);
|
||||
needsWrite |= dsWriter.WriteImage(descriptorIndex, imageView, layout, index);
|
||||
break;
|
||||
}
|
||||
VkImageView imageView;
|
||||
VkImageLayout layout;
|
||||
handle->DescriptorAsImage(this, imageView, layout);
|
||||
ASSERT(imageView != VK_NULL_HANDLE);
|
||||
needsWrite |= dsWriter.WriteImage(descriptorIndex, imageView, layout);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
{
|
||||
// Shader Resource (Buffer)
|
||||
auto sr = handles[descriptor.Slot];
|
||||
if (!sr)
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
{
|
||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||
sr = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
||||
// Shader Resource (Buffer)
|
||||
auto sr = handles[slot];
|
||||
if (!sr)
|
||||
{
|
||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||
sr = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
||||
}
|
||||
VkBufferView bufferView;
|
||||
sr->DescriptorAsUniformTexelBuffer(this, bufferView);
|
||||
ASSERT(bufferView != VK_NULL_HANDLE);
|
||||
needsWrite |= dsWriter.WriteUniformTexelBuffer(descriptorIndex, bufferView, index);
|
||||
break;
|
||||
}
|
||||
const VkBufferView* bufferView;
|
||||
sr->DescriptorAsUniformTexelBuffer(this, bufferView);
|
||||
needsWrite |= dsWriter.WriteUniformTexelBuffer(descriptorIndex, bufferView);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
{
|
||||
// Unordered Access (Texture)
|
||||
auto ua = handles[descriptor.Slot];
|
||||
ASSERT(ua);
|
||||
VkImageView imageView;
|
||||
VkImageLayout layout;
|
||||
ua->DescriptorAsStorageImage(this, imageView, layout);
|
||||
needsWrite |= dsWriter.WriteStorageImage(descriptorIndex, imageView, layout);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
{
|
||||
// Unordered Access (Buffer)
|
||||
auto ua = handles[descriptor.Slot];
|
||||
if (!ua)
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
{
|
||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||
ua = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
||||
// Unordered Access (Texture)
|
||||
auto ua = handles[slot];
|
||||
ASSERT(ua);
|
||||
VkImageView imageView;
|
||||
VkImageLayout layout;
|
||||
ua->DescriptorAsStorageImage(this, imageView, layout);
|
||||
needsWrite |= dsWriter.WriteStorageImage(descriptorIndex, imageView, layout, index);
|
||||
break;
|
||||
}
|
||||
VkBuffer buffer;
|
||||
VkDeviceSize offset, range;
|
||||
ua->DescriptorAsStorageBuffer(this, buffer, offset, range);
|
||||
needsWrite |= dsWriter.WriteStorageBuffer(descriptorIndex, buffer, offset, range);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
{
|
||||
// Unordered Access (Buffer)
|
||||
auto ua = handles[descriptor.Slot];
|
||||
if (!ua)
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
{
|
||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||
ua = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
||||
// Unordered Access (Buffer)
|
||||
auto ua = handles[slot];
|
||||
if (!ua)
|
||||
{
|
||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||
ua = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
||||
}
|
||||
VkBuffer buffer;
|
||||
VkDeviceSize offset, range;
|
||||
ua->DescriptorAsStorageBuffer(this, buffer, offset, range);
|
||||
needsWrite |= dsWriter.WriteStorageBuffer(descriptorIndex, buffer, offset, range, index);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
{
|
||||
// Unordered Access (Buffer)
|
||||
auto ua = handles[slot];
|
||||
if (!ua)
|
||||
{
|
||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||
ua = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
||||
}
|
||||
VkBufferView bufferView;
|
||||
ua->DescriptorAsStorageTexelBuffer(this, bufferView);
|
||||
ASSERT(bufferView != VK_NULL_HANDLE);
|
||||
needsWrite |= dsWriter.WriteStorageTexelBuffer(descriptorIndex, bufferView, index);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
{
|
||||
// Constant Buffer
|
||||
auto cb = handles[slot];
|
||||
ASSERT(cb);
|
||||
VkBuffer buffer;
|
||||
VkDeviceSize offset, range;
|
||||
uint32 dynamicOffset;
|
||||
cb->DescriptorAsDynamicUniformBuffer(this, buffer, offset, range, dynamicOffset);
|
||||
needsWrite |= dsWriter.WriteDynamicUniformBuffer(descriptorIndex, buffer, offset, range, dynamicOffset, index);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown or invalid descriptor type
|
||||
CRASH;
|
||||
break;
|
||||
}
|
||||
const VkBufferView* bufferView;
|
||||
ua->DescriptorAsStorageTexelBuffer(this, bufferView);
|
||||
needsWrite |= dsWriter.WriteStorageTexelBuffer(descriptorIndex, bufferView);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
{
|
||||
// Constant Buffer
|
||||
auto cb = handles[descriptor.Slot];
|
||||
ASSERT(cb);
|
||||
VkBuffer buffer;
|
||||
VkDeviceSize offset, range;
|
||||
uint32 dynamicOffset;
|
||||
cb->DescriptorAsDynamicUniformBuffer(this, buffer, offset, range, dynamicOffset);
|
||||
needsWrite |= dsWriter.WriteDynamicUniformBuffer(descriptorIndex, buffer, offset, range, dynamicOffset);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown or invalid descriptor type
|
||||
CRASH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1102,6 +1107,10 @@ void GPUContextVulkan::Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCo
|
||||
vkCmdDispatch(cmdBuffer->GetHandle(), threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
RENDER_STAT_DISPATCH_CALL();
|
||||
|
||||
// Place a barrier between dispatches, so that UAVs can be read+write in subsequent passes
|
||||
// TODO: optimize it by moving inputs/outputs into higher-layer so eg. Global SDF can manually optimize it
|
||||
vkCmdPipelineBarrier(cmdBuffer->GetHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 0, nullptr);
|
||||
|
||||
#if VK_ENABLE_BARRIERS_DEBUG
|
||||
LOG(Warning, "Dispatch");
|
||||
#endif
|
||||
@@ -1136,6 +1145,10 @@ void GPUContextVulkan::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* b
|
||||
vkCmdDispatchIndirect(cmdBuffer->GetHandle(), bufferForArgsVulkan->GetHandle(), offsetForArgs);
|
||||
RENDER_STAT_DISPATCH_CALL();
|
||||
|
||||
// Place a barrier between dispatches, so that UAVs can be read+write in subsequent passes
|
||||
// TODO: optimize it by moving inputs/outputs into higher-layer so eg. Global SDF can manually optimize it
|
||||
vkCmdPipelineBarrier(cmdBuffer->GetHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 0, nullptr);
|
||||
|
||||
#if VK_ENABLE_BARRIERS_DEBUG
|
||||
LOG(Warning, "DispatchIndirect");
|
||||
#endif
|
||||
@@ -1494,7 +1507,7 @@ void GPUContextVulkan::CopyResource(GPUResource* dstResource, GPUResource* srcRe
|
||||
ASSERT(bufferCopy.size == dstBufferVulkan->GetSize());
|
||||
vkCmdCopyBuffer(cmdBuffer->GetHandle(), srcBufferVulkan->GetHandle(), dstBufferVulkan->GetHandle(), 1, &bufferCopy);
|
||||
}
|
||||
// Texture -> Texture
|
||||
// Texture -> Texture
|
||||
else if (srcType == GPUResource::ObjectType::Texture && dstType == GPUResource::ObjectType::Texture)
|
||||
{
|
||||
if (dstTextureVulkan->IsStaging())
|
||||
@@ -1505,7 +1518,7 @@ void GPUContextVulkan::CopyResource(GPUResource* dstResource, GPUResource* srcRe
|
||||
ASSERT(dstTextureVulkan->StagingBuffer && srcTextureVulkan->StagingBuffer);
|
||||
CopyResource(dstTextureVulkan->StagingBuffer, srcTextureVulkan->StagingBuffer);
|
||||
}
|
||||
// Texture -> Staging Texture
|
||||
// Texture -> Staging Texture
|
||||
else
|
||||
{
|
||||
// Transition resources
|
||||
@@ -1635,7 +1648,7 @@ void GPUContextVulkan::CopySubresource(GPUResource* dstResource, uint32 dstSubre
|
||||
ASSERT(bufferCopy.size == dstBufferVulkan->GetSize());
|
||||
vkCmdCopyBuffer(cmdBuffer->GetHandle(), srcBufferVulkan->GetHandle(), dstBufferVulkan->GetHandle(), 1, &bufferCopy);
|
||||
}
|
||||
// Texture -> Texture
|
||||
// Texture -> Texture
|
||||
else if (srcType == GPUResource::ObjectType::Texture && dstType == GPUResource::ObjectType::Texture)
|
||||
{
|
||||
const int32 dstMipMaps = dstTextureVulkan->MipLevels();
|
||||
@@ -1653,7 +1666,7 @@ void GPUContextVulkan::CopySubresource(GPUResource* dstResource, uint32 dstSubre
|
||||
ASSERT(dstTextureVulkan->StagingBuffer && srcTextureVulkan->StagingBuffer);
|
||||
CopyResource(dstTextureVulkan->StagingBuffer, srcTextureVulkan->StagingBuffer);
|
||||
}
|
||||
// Texture -> Staging Texture
|
||||
// Texture -> Staging Texture
|
||||
else
|
||||
{
|
||||
// Transition resources
|
||||
|
||||
@@ -209,11 +209,22 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugUtilsCallback(VkDebugUtilsMessageSever
|
||||
type = TEXT("Perf");
|
||||
}
|
||||
|
||||
if (callbackData->pMessageIdName)
|
||||
LOG(Info, "[Vulkan] {0} {1}:{2}({3}) {4}", type, severity, callbackData->messageIdNumber, String(callbackData->pMessageIdName), String(callbackData->pMessage));
|
||||
else
|
||||
LOG(Info, "[Vulkan] {0} {1}:{2} {3}", type, severity, callbackData->messageIdNumber, String(callbackData->pMessage));
|
||||
// Fix invalid characters in hex values (bug in Debug Layer)
|
||||
char* handleStart = (char*)StringUtils::FindIgnoreCase(callbackData->pMessage, "0x");
|
||||
while (handleStart != nullptr)
|
||||
{
|
||||
while (*handleStart != ' ' && *handleStart != 0)
|
||||
*handleStart++ = Math::Clamp<char>(*handleStart, '0', 'z');
|
||||
if (*handleStart == 0)
|
||||
break;
|
||||
handleStart = (char*)StringUtils::FindIgnoreCase(handleStart, "0x");
|
||||
}
|
||||
|
||||
const String message(callbackData->pMessage);
|
||||
if (callbackData->pMessageIdName)
|
||||
LOG(Info, "[Vulkan] {0} {1}:{2}({3}) {4}", type, severity, callbackData->messageIdNumber, String(callbackData->pMessageIdName), message);
|
||||
else
|
||||
LOG(Info, "[Vulkan] {0} {1}:{2} {3}", type, severity, callbackData->messageIdNumber, message);
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
|
||||
@@ -782,7 +782,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context. Can be used to add memory barriers to the pipeline before binding the descriptor to the pipeline.</param>
|
||||
/// <param name="bufferView">The buffer view.</param>
|
||||
virtual void DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
|
||||
virtual void DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView)
|
||||
{
|
||||
CRASH;
|
||||
}
|
||||
@@ -804,7 +804,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context. Can be used to add memory barriers to the pipeline before binding the descriptor to the pipeline.</param>
|
||||
/// <param name="bufferView">The buffer view.</param>
|
||||
virtual void DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
|
||||
virtual void DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView)
|
||||
{
|
||||
CRASH;
|
||||
}
|
||||
|
||||
@@ -51,9 +51,11 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState()
|
||||
uint32 dynamicOffsetsCount = 0;
|
||||
if (DescriptorInfo.DescriptorTypesCount != 0)
|
||||
{
|
||||
// TODO: merge into a single allocation
|
||||
_pipelineState->DSWriteContainer.DescriptorWrites.AddZeroed(DescriptorInfo.DescriptorTypesCount);
|
||||
_pipelineState->DSWriteContainer.DescriptorImageInfo.AddZeroed(DescriptorInfo.ImageInfosCount);
|
||||
_pipelineState->DSWriteContainer.DescriptorBufferInfo.AddZeroed(DescriptorInfo.BufferInfosCount);
|
||||
_pipelineState->DSWriteContainer.DescriptorTexelBufferView.AddZeroed(DescriptorInfo.TexelBufferViewsCount);
|
||||
|
||||
ASSERT(DescriptorInfo.DescriptorTypesCount < 255);
|
||||
_pipelineState->DSWriteContainer.BindingToDynamicOffset.AddDefault(DescriptorInfo.DescriptorTypesCount);
|
||||
@@ -62,9 +64,10 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState()
|
||||
VkWriteDescriptorSet* currentDescriptorWrite = _pipelineState->DSWriteContainer.DescriptorWrites.Get();
|
||||
VkDescriptorImageInfo* currentImageInfo = _pipelineState->DSWriteContainer.DescriptorImageInfo.Get();
|
||||
VkDescriptorBufferInfo* currentBufferInfo = _pipelineState->DSWriteContainer.DescriptorBufferInfo.Get();
|
||||
VkBufferView* currentTexelBufferView = _pipelineState->DSWriteContainer.DescriptorTexelBufferView.Get();
|
||||
uint8* currentBindingToDynamicOffsetMap = _pipelineState->DSWriteContainer.BindingToDynamicOffset.Get();
|
||||
|
||||
dynamicOffsetsCount = _pipelineState->DSWriter.SetupDescriptorWrites(DescriptorInfo, currentDescriptorWrite, currentImageInfo, currentBufferInfo, currentBindingToDynamicOffsetMap);
|
||||
dynamicOffsetsCount = _pipelineState->DSWriter.SetupDescriptorWrites(DescriptorInfo, currentDescriptorWrite, currentImageInfo, currentBufferInfo, currentTexelBufferView, currentBindingToDynamicOffsetMap);
|
||||
}
|
||||
|
||||
_pipelineState->DynamicOffsets.AddZeroed(dynamicOffsetsCount);
|
||||
@@ -337,9 +340,11 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
||||
if (descriptor == nullptr || descriptor->DescriptorTypesCount == 0)
|
||||
continue;
|
||||
|
||||
// TODO: merge into a single allocation for a whole PSO
|
||||
DSWriteContainer.DescriptorWrites.AddZeroed(descriptor->DescriptorTypesCount);
|
||||
DSWriteContainer.DescriptorImageInfo.AddZeroed(descriptor->ImageInfosCount);
|
||||
DSWriteContainer.DescriptorBufferInfo.AddZeroed(descriptor->BufferInfosCount);
|
||||
DSWriteContainer.DescriptorTexelBufferView.AddZeroed(descriptor->TexelBufferViewsCount);
|
||||
|
||||
ASSERT(descriptor->DescriptorTypesCount < 255);
|
||||
DSWriteContainer.BindingToDynamicOffset.AddDefault(descriptor->DescriptorTypesCount);
|
||||
@@ -349,6 +354,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
||||
VkWriteDescriptorSet* currentDescriptorWrite = DSWriteContainer.DescriptorWrites.Get();
|
||||
VkDescriptorImageInfo* currentImageInfo = DSWriteContainer.DescriptorImageInfo.Get();
|
||||
VkDescriptorBufferInfo* currentBufferInfo = DSWriteContainer.DescriptorBufferInfo.Get();
|
||||
VkBufferView* currentTexelBufferView = DSWriteContainer.DescriptorTexelBufferView.Get();
|
||||
byte* currentBindingToDynamicOffsetMap = DSWriteContainer.BindingToDynamicOffset.Get();
|
||||
uint32 dynamicOffsetsStart[DescriptorSet::GraphicsStagesCount];
|
||||
uint32 dynamicOffsetsCount = 0;
|
||||
@@ -360,12 +366,13 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
||||
if (descriptor == nullptr || descriptor->DescriptorTypesCount == 0)
|
||||
continue;
|
||||
|
||||
const uint32 numDynamicOffsets = DSWriter[stage].SetupDescriptorWrites(*descriptor, currentDescriptorWrite, currentImageInfo, currentBufferInfo, currentBindingToDynamicOffsetMap);
|
||||
const uint32 numDynamicOffsets = DSWriter[stage].SetupDescriptorWrites(*descriptor, currentDescriptorWrite, currentImageInfo, currentBufferInfo, currentTexelBufferView, currentBindingToDynamicOffsetMap);
|
||||
dynamicOffsetsCount += numDynamicOffsets;
|
||||
|
||||
currentDescriptorWrite += descriptor->DescriptorTypesCount;
|
||||
currentImageInfo += descriptor->ImageInfosCount;
|
||||
currentBufferInfo += descriptor->BufferInfosCount;
|
||||
currentTexelBufferView += descriptor->TexelBufferViewsCount;
|
||||
currentBindingToDynamicOffsetMap += descriptor->DescriptorTypesCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -425,29 +425,19 @@ void GPUTextureVulkan::initHandles()
|
||||
}
|
||||
}
|
||||
|
||||
void GPUTextureVulkan::onResidentMipsChanged()
|
||||
void GPUTextureVulkan::OnResidentMipsChanged()
|
||||
{
|
||||
// We support changing resident mip maps only for regular textures (render targets and depth buffers don't use that feature at all)
|
||||
ASSERT(IsRegularTexture() && _handlesPerSlice.Count() == 1);
|
||||
ASSERT(!IsVolume());
|
||||
|
||||
// Change view
|
||||
auto& handle = _handlesPerSlice[0];
|
||||
handle.Release();
|
||||
// Update view
|
||||
VkExtent3D extent;
|
||||
extent.width = Width();
|
||||
extent.height = Height();
|
||||
extent.depth = Depth();
|
||||
const int32 firstMipIndex = MipLevels() - ResidentMipLevels();
|
||||
const int32 mipLevels = ResidentMipLevels();
|
||||
if (IsCubeMap())
|
||||
{
|
||||
handle.Init(_device, this, _image, mipLevels, Format(), MultiSampleLevel(), extent, VK_IMAGE_VIEW_TYPE_CUBE, mipLevels, firstMipIndex, ArraySize());
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.Init(_device, this, _image, mipLevels, Format(), MultiSampleLevel(), extent, VK_IMAGE_VIEW_TYPE_2D, mipLevels, firstMipIndex, ArraySize());
|
||||
}
|
||||
const VkImageViewType viewType = IsVolume() ? VK_IMAGE_VIEW_TYPE_3D : (IsCubeMap() ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D);
|
||||
GPUTextureViewVulkan& view = IsVolume() ? _handleVolume : _handlesPerSlice[0];
|
||||
view.Release();
|
||||
view.Init(_device, this, _image, mipLevels, Format(), MultiSampleLevel(), extent, viewType, mipLevels, firstMipIndex, ArraySize());
|
||||
}
|
||||
|
||||
void GPUTextureVulkan::OnReleaseGPU()
|
||||
|
||||
@@ -182,7 +182,7 @@ protected:
|
||||
|
||||
// [GPUTexture]
|
||||
bool OnInit() override;
|
||||
void onResidentMipsChanged() override;
|
||||
void OnResidentMipsChanged() override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -94,10 +94,16 @@ struct SpirvShaderDescriptorInfo
|
||||
/// The resource type.
|
||||
/// </summary>
|
||||
SpirvShaderResourceType ResourceType;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of slots used by the descriptor (eg. array of textures size).
|
||||
/// </summary>
|
||||
uint32 Count;
|
||||
};
|
||||
|
||||
uint16 ImageInfosCount;
|
||||
uint16 BufferInfosCount;
|
||||
uint32 TexelBufferViewsCount;
|
||||
uint32 DescriptorTypesCount;
|
||||
Descriptor DescriptorTypes[MaxDescriptors];
|
||||
};
|
||||
|
||||
@@ -81,6 +81,7 @@ Actor::Actor(const SpawnParams& params)
|
||||
, _physicsScene(nullptr)
|
||||
, HideFlags(HideFlags::None)
|
||||
{
|
||||
_drawNoCulling = 0;
|
||||
}
|
||||
|
||||
SceneRendering* Actor::GetSceneRendering() const
|
||||
@@ -1232,45 +1233,6 @@ void Actor::Draw(RenderContext& renderContext)
|
||||
{
|
||||
}
|
||||
|
||||
void Actor::DrawGeneric(RenderContext& renderContext)
|
||||
{
|
||||
// Generic drawing uses only GBuffer Fill Pass and simple frustum culling (see SceneRendering for more optimized drawing)
|
||||
if (renderContext.View.Pass & DrawPass::GBuffer)
|
||||
{
|
||||
Draw(renderContext);
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::DrawHierarchy(RenderContext& renderContext)
|
||||
{
|
||||
// Draw actor itself
|
||||
DrawGeneric(renderContext);
|
||||
|
||||
// Draw children
|
||||
if (renderContext.View.IsOfflinePass)
|
||||
{
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
auto child = Children[i];
|
||||
if (child->GetIsActive() && child->GetStaticFlags() & renderContext.View.StaticFlagsMask)
|
||||
{
|
||||
child->DrawHierarchy(renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
auto child = Children[i];
|
||||
if (child->GetIsActive())
|
||||
{
|
||||
child->DrawHierarchy(renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void Actor::OnDebugDraw()
|
||||
|
||||
@@ -30,6 +30,7 @@ DECLARE_SCENE_OBJECT(Actor);
|
||||
friend Level;
|
||||
friend PrefabManager;
|
||||
friend Scene;
|
||||
friend SceneRendering;
|
||||
friend Prefab;
|
||||
friend PrefabInstanceData;
|
||||
|
||||
@@ -39,6 +40,7 @@ protected:
|
||||
int8 _isActiveInHierarchy : 1;
|
||||
int8 _isPrefabRoot : 1;
|
||||
int8 _isEnabled : 1;
|
||||
int8 _drawNoCulling : 1;
|
||||
byte _layer;
|
||||
byte _tag;
|
||||
StaticFlags _staticFlags;
|
||||
@@ -646,23 +648,11 @@ public:
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Draws this actor. Called by Scene Rendering service. This call is more optimized than generic Draw (eg. models are rendered during all passed but other actors are invoked only during GBufferFill pass).
|
||||
/// Draws this actor. Called by Scene Rendering service. This call is more optimized than generic Draw (eg. geometry is rendered during all pass types but other actors are drawn only during GBufferFill pass).
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
virtual void Draw(RenderContext& renderContext);
|
||||
|
||||
/// <summary>
|
||||
/// Draws this actor. Called during custom actor rendering or any other generic rendering from code.
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
virtual void DrawGeneric(RenderContext& renderContext);
|
||||
|
||||
/// <summary>
|
||||
/// Draws this actor and all its children (full scene hierarchy part).
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
void DrawHierarchy(RenderContext& renderContext);
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -548,7 +548,7 @@ void AnimatedModel::UpdateBounds()
|
||||
BoundingBox::Transform(_boxLocal, _world, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void AnimatedModel::UpdateSockets()
|
||||
@@ -692,6 +692,10 @@ void AnimatedModel::Update()
|
||||
|
||||
void AnimatedModel::Draw(RenderContext& renderContext)
|
||||
{
|
||||
if (renderContext.View.Pass == DrawPass::GlobalSDF)
|
||||
return; // TODO: Animated Model rendering to Global SDF
|
||||
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
|
||||
return; // No supported
|
||||
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, _world);
|
||||
|
||||
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass & (int32)renderContext.View.GetShadowsDrawPassMask(ShadowsMode));
|
||||
@@ -728,14 +732,6 @@ void AnimatedModel::Draw(RenderContext& renderContext)
|
||||
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, _world);
|
||||
}
|
||||
|
||||
void AnimatedModel::DrawGeneric(RenderContext& renderContext)
|
||||
{
|
||||
if (renderContext.View.RenderLayersMask.Mask & GetLayerMask() && renderContext.View.CullingFrustum.Intersects(_box))
|
||||
{
|
||||
Draw(renderContext);
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
@@ -817,6 +813,13 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
|
||||
DESERIALIZE(RootMotionTarget);
|
||||
|
||||
Entries.DeserializeIfExists(stream, "Buffer", modifier);
|
||||
|
||||
// [Deprecated on 07.02.2022, expires on 07.02.2024]
|
||||
if (modifier->EngineBuild <= 6330)
|
||||
DrawModes |= DrawPass::GlobalSDF;
|
||||
// [Deprecated on 27.04.2022, expires on 27.04.2024]
|
||||
if (modifier->EngineBuild <= 6331)
|
||||
DrawModes |= DrawPass::GlobalSurfaceAtlas;
|
||||
}
|
||||
|
||||
bool AnimatedModel::IntersectsEntry(int32 entryIndex, const Ray& ray, float& distance, Vector3& normal)
|
||||
@@ -881,5 +884,5 @@ void AnimatedModel::OnTransformChanged()
|
||||
BoundingBox::Transform(_boxLocal, _world, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
@@ -376,7 +376,6 @@ public:
|
||||
// [ModelInstanceActor]
|
||||
bool HasContentLoaded() const override;
|
||||
void Draw(RenderContext& renderContext) override;
|
||||
void DrawGeneric(RenderContext& renderContext) override;
|
||||
#if USE_EDITOR
|
||||
void OnDebugDrawSelected() override;
|
||||
BoundingBox GetEditorBox() const override;
|
||||
|
||||
@@ -249,7 +249,9 @@ bool Camera::HasContentLoaded() const
|
||||
|
||||
void Camera::Draw(RenderContext& renderContext)
|
||||
{
|
||||
if (renderContext.View.Flags & ViewFlags::EditorSprites && _previewModel && _previewModel->IsLoaded())
|
||||
if (renderContext.View.Flags & ViewFlags::EditorSprites
|
||||
&& _previewModel
|
||||
&& _previewModel->IsLoaded())
|
||||
{
|
||||
GeometryDrawStateData drawState;
|
||||
Mesh::DrawInfo draw;
|
||||
@@ -259,14 +261,16 @@ void Camera::Draw(RenderContext& renderContext)
|
||||
draw.Lightmap = nullptr;
|
||||
draw.LightmapUVs = nullptr;
|
||||
draw.Flags = StaticFlags::Transform;
|
||||
draw.DrawModes = (DrawPass)(DrawPass::Default & renderContext.View.Pass);
|
||||
draw.DrawModes = (DrawPass)((DrawPass::Depth | DrawPass::GBuffer | DrawPass::Forward) & renderContext.View.Pass);
|
||||
BoundingSphere::FromBox(_previewModelBox, draw.Bounds);
|
||||
draw.PerInstanceRandom = GetPerInstanceRandom();
|
||||
draw.LODBias = 0;
|
||||
draw.ForcedLOD = -1;
|
||||
draw.VertexColors = nullptr;
|
||||
|
||||
_previewModel->Draw(renderContext, draw);
|
||||
if (draw.DrawModes != DrawPass::None)
|
||||
{
|
||||
_previewModel->Draw(renderContext, draw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +358,7 @@ void Camera::OnEnable()
|
||||
{
|
||||
Cameras.Add(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddCommonNoCulling(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#endif
|
||||
|
||||
// Base
|
||||
@@ -364,7 +368,7 @@ void Camera::OnEnable()
|
||||
void Camera::OnDisable()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveCommonNoCulling(this);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
#endif
|
||||
Cameras.Remove(this);
|
||||
if (CutSceneCamera == this)
|
||||
|
||||
@@ -52,6 +52,7 @@ private:
|
||||
ModelInstanceEntries _previewModelBuffer;
|
||||
BoundingBox _previewModelBox;
|
||||
Matrix _previewModelWorld;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -69,12 +69,13 @@ BoundingBox Decal::GetEditorBox() const
|
||||
void Decal::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void Decal::Draw(RenderContext& renderContext)
|
||||
{
|
||||
if ((renderContext.View.Flags & ViewFlags::Decals) != 0 &&
|
||||
renderContext.View.Pass & DrawPass::GBuffer &&
|
||||
Material &&
|
||||
Material->IsLoaded() &&
|
||||
Material->IsDecal())
|
||||
@@ -121,7 +122,7 @@ bool Decal::IntersectsItself(const Ray& ray, float& distance, Vector3& normal)
|
||||
|
||||
void Decal::OnEnable()
|
||||
{
|
||||
_sceneRenderingKey = GetSceneRendering()->AddCommon(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -135,7 +136,7 @@ void Decal::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
@@ -156,5 +157,5 @@ void Decal::OnTransformChanged()
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
DirectionalLight::DirectionalLight(const SpawnParams& params)
|
||||
: LightWithShadow(params)
|
||||
{
|
||||
_drawNoCulling = 1;
|
||||
Brightness = 8.0f;
|
||||
}
|
||||
|
||||
@@ -19,6 +20,7 @@ void DirectionalLight::Draw(RenderContext& renderContext)
|
||||
AdjustBrightness(renderContext.View, brightness);
|
||||
if (Brightness > ZeroTolerance
|
||||
&& (renderContext.View.Flags & ViewFlags::DirectionalLights) != 0
|
||||
&& renderContext.View.Pass & DrawPass::GBuffer
|
||||
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance))
|
||||
{
|
||||
RendererDirectionalLightData data;
|
||||
@@ -67,7 +69,7 @@ bool DirectionalLight::IntersectsItself(const Ray& ray, float& distance, Vector3
|
||||
|
||||
void DirectionalLight::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddCommonNoCulling(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -81,7 +83,7 @@ void DirectionalLight::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommonNoCulling(this);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
LightWithShadow::OnDisable();
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
/// </summary>
|
||||
API_CLASS() class FLAXENGINE_API DirectionalLight : public LightWithShadow
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(DirectionalLight);
|
||||
public:
|
||||
DECLARE_SCENE_OBJECT(DirectionalLight);
|
||||
private:
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The number of cascades used for slicing the range of depth covered by the light. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades.
|
||||
/// </summary>
|
||||
@@ -19,7 +21,6 @@ public:
|
||||
int32 CascadeCount = 4;
|
||||
|
||||
public:
|
||||
|
||||
// [LightWithShadow]
|
||||
void Draw(RenderContext& renderContext) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
@@ -27,7 +28,6 @@ public:
|
||||
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
|
||||
|
||||
protected:
|
||||
|
||||
// [LightWithShadow]
|
||||
void OnEnable() override;
|
||||
void OnDisable() override;
|
||||
|
||||
@@ -127,12 +127,15 @@ void EnvironmentProbe::UpdateBounds()
|
||||
_sphere = BoundingSphere(GetPosition(), GetScaledRadius());
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void EnvironmentProbe::Draw(RenderContext& renderContext)
|
||||
{
|
||||
if (Brightness > ZeroTolerance && (renderContext.View.Flags & ViewFlags::Reflections) != 0 && HasProbeLoaded())
|
||||
if (Brightness > ZeroTolerance &&
|
||||
(renderContext.View.Flags & ViewFlags::Reflections) != 0 &&
|
||||
renderContext.View.Pass & DrawPass::GBuffer &&
|
||||
HasProbeLoaded())
|
||||
{
|
||||
renderContext.List->EnvironmentProbes.Add(this);
|
||||
}
|
||||
@@ -156,7 +159,7 @@ void EnvironmentProbe::OnDebugDrawSelected()
|
||||
void EnvironmentProbe::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void EnvironmentProbe::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
@@ -199,7 +202,7 @@ bool EnvironmentProbe::IntersectsItself(const Ray& ray, float& distance, Vector3
|
||||
|
||||
void EnvironmentProbe::OnEnable()
|
||||
{
|
||||
_sceneRenderingKey = GetSceneRendering()->AddCommon(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -213,7 +216,7 @@ void EnvironmentProbe::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
ExponentialHeightFog::ExponentialHeightFog(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
{
|
||||
_drawNoCulling = 1;
|
||||
|
||||
// Load shader
|
||||
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Fog"));
|
||||
if (_shader == nullptr)
|
||||
@@ -31,7 +33,11 @@ void ExponentialHeightFog::Draw(RenderContext& renderContext)
|
||||
{
|
||||
// Render only when shader is valid and fog can be rendered
|
||||
// Do not render exponential fog in orthographic views
|
||||
if ((renderContext.View.Flags & ViewFlags::Fog) != 0 && _shader && _shader->IsLoaded() && renderContext.View.IsPerspectiveProjection())
|
||||
if ((renderContext.View.Flags & ViewFlags::Fog) != 0
|
||||
&& renderContext.View.Pass & DrawPass::GBuffer
|
||||
&& _shader
|
||||
&& _shader->IsLoaded()
|
||||
&& renderContext.View.IsPerspectiveProjection())
|
||||
{
|
||||
// Prepare
|
||||
if (_psFog.States[0] == nullptr)
|
||||
@@ -203,7 +209,7 @@ void ExponentialHeightFog::DrawFog(GPUContext* context, RenderContext& renderCon
|
||||
|
||||
void ExponentialHeightFog::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddCommonNoCulling(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -217,7 +223,7 @@ void ExponentialHeightFog::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommonNoCulling(this);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
/// </summary>
|
||||
API_CLASS() class FLAXENGINE_API ExponentialHeightFog : public Actor, public IFogRenderer
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(ExponentialHeightFog);
|
||||
DECLARE_SCENE_OBJECT(ExponentialHeightFog);
|
||||
private:
|
||||
|
||||
AssetReference<Shader> _shader;
|
||||
GPUPipelineStatePermutationsPs<2> _psFog;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The fog density factor.
|
||||
/// </summary>
|
||||
@@ -61,7 +60,6 @@ public:
|
||||
float FogCutoffDistance = 0.0f;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Directional light used for Directional Inscattering.
|
||||
/// </summary>
|
||||
@@ -90,7 +88,6 @@ public:
|
||||
Color DirectionalInscatteringColor = Color(0.25, 0.25f, 0.125f);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Whether to enable Volumetric fog. Graphics quality settings control the resolution of the fog simulation.
|
||||
/// </summary>
|
||||
@@ -133,7 +130,6 @@ public:
|
||||
float VolumetricFogDistance = 6000.0f;
|
||||
|
||||
private:
|
||||
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj)
|
||||
{
|
||||
@@ -142,7 +138,6 @@ private:
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// [Actor]
|
||||
#if USE_EDITOR
|
||||
BoundingBox GetEditorBox() const override
|
||||
@@ -163,7 +158,6 @@ public:
|
||||
void DrawFog(GPUContext* context, RenderContext& renderContext, GPUTextureView* output) override;
|
||||
|
||||
protected:
|
||||
|
||||
// [Actor]
|
||||
void OnEnable() override;
|
||||
void OnDisable() override;
|
||||
|
||||
@@ -34,12 +34,12 @@ MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32
|
||||
void ModelInstanceActor::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void ModelInstanceActor::OnEnable()
|
||||
{
|
||||
_sceneRenderingKey = GetSceneRendering()->AddGeometry(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
@@ -47,8 +47,8 @@ void ModelInstanceActor::OnEnable()
|
||||
|
||||
void ModelInstanceActor::OnDisable()
|
||||
{
|
||||
GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
@@ -66,12 +66,12 @@ void PointLight::UpdateBounds()
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void PointLight::OnEnable()
|
||||
{
|
||||
_sceneRenderingKey = GetSceneRendering()->AddCommon(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -85,7 +85,7 @@ void PointLight::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
LightWithShadow::OnDisable();
|
||||
@@ -106,6 +106,7 @@ void PointLight::Draw(RenderContext& renderContext)
|
||||
const float radius = GetScaledRadius();
|
||||
if ((renderContext.View.Flags & ViewFlags::PointLights) != 0
|
||||
&& brightness > ZeroTolerance
|
||||
&& renderContext.View.Pass & DrawPass::GBuffer
|
||||
&& radius > ZeroTolerance
|
||||
&& (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance))
|
||||
{
|
||||
@@ -165,7 +166,7 @@ void PointLight::OnDebugDrawSelected()
|
||||
void PointLight::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void PointLight::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user