Merge remote-tracking branch 'origin/gi' into large-worlds

# Conflicts:
#	Source/Engine/Core/Math/Vector3.h
This commit is contained in:
Wojtek Figat
2022-05-21 19:45:13 +02:00
280 changed files with 7660 additions and 2355 deletions

View File

@@ -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();

View File

@@ -83,7 +83,7 @@ void Animation::ClearCache()
// Free memory
MappingCache.Clear();
MappingCache.Cleanup();
MappingCache.SetCapacity(0);
}
const Animation::NodeToChannel* Animation::GetMapping(SkinnedModel* obj)

View File

@@ -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();

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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)

View File

@@ -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:

View File

@@ -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>

View File

@@ -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;

View File

@@ -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)

View File

@@ -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]);

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}
};

View File

@@ -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);

View File

@@ -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);
}
};

View File

@@ -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);

View File

@@ -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<>

View File

@@ -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;

View File

@@ -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>

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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)

View File

@@ -150,6 +150,8 @@ public:
Platform::MemoryCopy(Raw, values, sizeof(float) * 16);
}
explicit Matrix(const Matrix3x3& matrix);
public:
String ToString() const;

View File

@@ -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;

View File

@@ -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>

View File

@@ -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

View File

@@ -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.

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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:

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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++;
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -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.

View File

@@ -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>

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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:

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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);

View File

@@ -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()

View File

@@ -277,7 +277,7 @@ protected:
// [GPUTexture]
bool OnInit() override;
void onResidentMipsChanged() override;
void OnResidentMipsChanged() override;
void OnReleaseGPU() override;
};

View File

@@ -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()

View File

@@ -216,7 +216,7 @@ protected:
// [GPUTexture]
bool OnInit() override;
void onResidentMipsChanged() override;
void OnResidentMipsChanged() override;
void OnReleaseGPU() override;
};

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -57,7 +57,7 @@ protected:
return false;
}
void onResidentMipsChanged() override
void OnResidentMipsChanged() override
{
}
};

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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>

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -182,7 +182,7 @@ protected:
// [GPUTexture]
bool OnInit() override;
void onResidentMipsChanged() override;
void OnResidentMipsChanged() override;
void OnReleaseGPU() override;
};

View File

@@ -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];
};

View File

@@ -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()

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -52,6 +52,7 @@ private:
ModelInstanceEntries _previewModelBuffer;
BoundingBox _previewModelBox;
Matrix _previewModelWorld;
int32 _sceneRenderingKey = -1;
#endif
public:

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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