Merge remote-tracking branch 'origin/master' into sdl_platform

This commit is contained in:
2025-11-19 18:30:08 +02:00
91 changed files with 1492 additions and 1018 deletions

BIN
Content/Editor/Primitives/Cube.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Shaders/SDF.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/VolumetricFog.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -15,26 +15,32 @@
#include "Editor/ProjectInfo.h" #include "Editor/ProjectInfo.h"
#include "Editor/Utilities/EditorUtilities.h" #include "Editor/Utilities/EditorUtilities.h"
GDKPlatformTools::GDKPlatformTools() String GetGDK()
{ {
// Find GDK String gdk;
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), _gdkPath); Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), gdk);
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath)) if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
{ {
_gdkPath.Clear(); gdk.Clear();
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), _gdkPath); Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), gdk);
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath)) if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
{ {
_gdkPath.Clear(); gdk.Clear();
} }
else else
{ {
if (_gdkPath.EndsWith(TEXT("GRDK\\"))) if (gdk.EndsWith(TEXT("GRDK\\")))
_gdkPath.Remove(_gdkPath.Length() - 6); gdk.Remove(gdk.Length() - 6);
else if (_gdkPath.EndsWith(TEXT("GRDK"))) else if (gdk.EndsWith(TEXT("GRDK")))
_gdkPath.Remove(_gdkPath.Length() - 5); gdk.Remove(gdk.Length() - 5);
} }
} }
return gdk;
}
GDKPlatformTools::GDKPlatformTools()
{
_gdkPath = GetGDK();
} }
DotNetAOTModes GDKPlatformTools::UseAOT() const DotNetAOTModes GDKPlatformTools::UseAOT() const
@@ -121,7 +127,7 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla
validName.Add('\0'); validName.Add('\0');
sb.Append(TEXT("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")); sb.Append(TEXT("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
sb.Append(TEXT("<Game configVersion=\"0\">\n")); sb.Append(TEXT("<Game configVersion=\"1\">\n"));
sb.AppendFormat(TEXT(" <Identity Name=\"{0}\" Publisher=\"{1}\" Version=\"{2}\"/>\n"), sb.AppendFormat(TEXT(" <Identity Name=\"{0}\" Publisher=\"{1}\" Version=\"{2}\"/>\n"),
validName.Get(), validName.Get(),
platformSettings->PublisherName.HasChars() ? platformSettings->PublisherName : TEXT("CN=") + gameSettings->CompanyName, platformSettings->PublisherName.HasChars() ? platformSettings->PublisherName : TEXT("CN=") + gameSettings->CompanyName,

View File

@@ -526,6 +526,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
#if PLATFORM_TOOLS_XBOX_SCARLETT #if PLATFORM_TOOLS_XBOX_SCARLETT
case BuildPlatform::XboxScarlett: case BuildPlatform::XboxScarlett:
{ {
options.Platform = PlatformType::XboxScarlett;
const char* platformDefineName = "PLATFORM_XBOX_SCARLETT"; const char* platformDefineName = "PLATFORM_XBOX_SCARLETT";
COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE); COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE);
break; break;

View File

@@ -265,7 +265,7 @@ bool DeployDataStep::Perform(CookingData& data)
} }
if (version.IsEmpty()) if (version.IsEmpty())
{ {
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for the current host platform."), maxVer, minVer)); data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for {} platform."), maxVer, minVer, platformName));
return true; return true;
} }
} }

View File

@@ -123,6 +123,8 @@ namespace FlaxEditor.CustomEditors.Editors
{ {
base.Refresh(); base.Refresh();
if (Picker == null)
return;
var differentValues = HasDifferentValues; var differentValues = HasDifferentValues;
Picker.DifferentValues = differentValues; Picker.DifferentValues = differentValues;
if (!differentValues) if (!differentValues)

View File

@@ -1390,6 +1390,7 @@ namespace FlaxEditor
public void BuildAllMeshesSDF() public void BuildAllMeshesSDF()
{ {
var models = new List<Model>(); var models = new List<Model>();
var forceRebuild = Input.GetKey(KeyboardKeys.F);
Scene.ExecuteOnGraph(node => Scene.ExecuteOnGraph(node =>
{ {
if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel) if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel)
@@ -1399,7 +1400,7 @@ namespace FlaxEditor
model != null && model != null &&
!models.Contains(model) && !models.Contains(model) &&
!model.IsVirtual && !model.IsVirtual &&
model.SDF.Texture == null) (forceRebuild || model.SDF.Texture == null))
{ {
models.Add(model); models.Add(model);
} }
@@ -1412,7 +1413,17 @@ namespace FlaxEditor
{ {
var model = models[i]; var model = models[i];
Log($"[{i}/{models.Count}] Generating SDF for {model}"); Log($"[{i}/{models.Count}] Generating SDF for {model}");
if (!model.GenerateSDF()) float resolutionScale = 1.0f, backfacesThreshold = 0.6f;
int lodIndex = 6;
bool useGPU = true;
var sdf = model.SDF;
if (sdf.Texture != null)
{
// Preserve options set on this model
resolutionScale = sdf.ResolutionScale;
lodIndex = sdf.LOD;
}
if (!model.GenerateSDF(resolutionScale, lodIndex, true, backfacesThreshold, useGPU))
model.Save(); model.Save();
} }
}); });

View File

@@ -722,6 +722,7 @@ namespace FlaxEditor.Modules
_menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG); _menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG);
_menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh); _menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh);
_menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF); _menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF);
_menuToolsBuildAllMeshesSDF.LinkTooltip("Generates Sign Distance Field texture for all meshes used in loaded scenes. Use with 'F' key pressed to force rebuild SDF for meshes with existing one.");
cm.AddSeparator(); cm.AddSeparator();
cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow); cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow);
_menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel()); _menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel());

View File

@@ -431,27 +431,6 @@ namespace FlaxEditor.Surface
/// </summary> /// </summary>
public bool HasIndependentBoxes => Archetype.IndependentBoxes != null; public bool HasIndependentBoxes => Archetype.IndependentBoxes != null;
/// <summary>
/// Gets a value indicating whether this node has dependent boxes with assigned valid types. Otherwise any box has no dependent type assigned.
/// </summary>
public bool HasDependentBoxesSetup
{
get
{
if (Archetype.DependentBoxes == null || Archetype.IndependentBoxes == null)
return true;
for (int i = 0; i < Archetype.DependentBoxes.Length; i++)
{
var b = GetBox(Archetype.DependentBoxes[i]);
if (b != null && b.CurrentType == b.DefaultType)
return false;
}
return true;
}
}
private static readonly List<SurfaceNode> UpdateStack = new List<SurfaceNode>(); private static readonly List<SurfaceNode> UpdateStack = new List<SurfaceNode>();
/// <summary> /// <summary>

View File

@@ -469,7 +469,8 @@ namespace FlaxEditor.Surface
bool handled = base.OnMouseDown(location, button); bool handled = base.OnMouseDown(location, button);
if (!handled) if (!handled)
CustomMouseDown?.Invoke(ref location, button, ref handled); CustomMouseDown?.Invoke(ref location, button, ref handled);
if (handled) var root = Root;
if (handled || root == null)
{ {
// Clear flags // Clear flags
_isMovingSelection = false; _isMovingSelection = false;
@@ -523,11 +524,11 @@ namespace FlaxEditor.Surface
if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation)) if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation))
{ {
// Check if user is pressing control // Check if user is pressing control
if (Root.GetKey(KeyboardKeys.Control)) if (root.GetKey(KeyboardKeys.Control))
{ {
AddToSelection(controlUnderMouse); AddToSelection(controlUnderMouse);
} }
else if (Root.GetKey(KeyboardKeys.Shift)) else if (root.GetKey(KeyboardKeys.Shift))
{ {
RemoveFromSelection(controlUnderMouse); RemoveFromSelection(controlUnderMouse);
} }
@@ -539,7 +540,7 @@ namespace FlaxEditor.Surface
} }
// Start moving selected nodes // Start moving selected nodes
if (!Root.GetKey(KeyboardKeys.Shift)) if (!root.GetKey(KeyboardKeys.Shift))
{ {
StartMouseCapture(); StartMouseCapture();
_movingSelectionViewPos = _rootControl.Location; _movingSelectionViewPos = _rootControl.Location;
@@ -559,7 +560,7 @@ namespace FlaxEditor.Surface
// Start selecting or commenting // Start selecting or commenting
StartMouseCapture(); StartMouseCapture();
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift)) if (!root.GetKey(KeyboardKeys.Control) && !root.GetKey(KeyboardKeys.Shift))
{ {
ClearSelection(); ClearSelection();
} }

View File

@@ -178,19 +178,31 @@ namespace FlaxEditor.Surface
// Update boxes types for nodes that dependant box types based on incoming connections // Update boxes types for nodes that dependant box types based on incoming connections
{ {
bool keepUpdating = false; bool keepUpdating = true;
int updateLimit = 100; int updatesMin = 2, updatesMax = 100;
do do
{ {
keepUpdating = false;
for (int i = 0; i < RootControl.Children.Count; i++) for (int i = 0; i < RootControl.Children.Count; i++)
{ {
if (RootControl.Children[i] is SurfaceNode node && !node.HasDependentBoxesSetup) if (RootControl.Children[i] is SurfaceNode node)
{ {
node.UpdateBoxesTypes(); node.UpdateBoxesTypes();
keepUpdating = true; var arch = node.Archetype;
if (arch.DependentBoxes != null && arch.IndependentBoxes != null)
{
foreach (var boxId in arch.DependentBoxes)
{
var b = node.GetBox(boxId);
if (b != null && b.CurrentType == b.DefaultType)
{
keepUpdating = true;
}
}
}
} }
} }
} while (keepUpdating && updateLimit-- > 0); } while ((keepUpdating && --updatesMax > 0) || --updatesMin > 0);
} }
Loaded?.Invoke(this); Loaded?.Invoke(this);

View File

@@ -74,11 +74,6 @@ struct TextureDataResult
PixelFormat Format; PixelFormat Format;
Int2 Mip0Size; Int2 Mip0Size;
BytesContainer* Mip0DataPtr; BytesContainer* Mip0DataPtr;
TextureDataResult()
: Lock(FlaxStorage::LockData::Invalid)
{
}
}; };
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false) bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false)

View File

@@ -14,7 +14,6 @@ using FlaxEditor.Surface;
using FlaxEditor.Viewport.Previews; using FlaxEditor.Viewport.Previews;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using FlaxEngine.Utilities;
namespace FlaxEditor.Windows.Assets namespace FlaxEditor.Windows.Assets
{ {
@@ -430,7 +429,7 @@ namespace FlaxEditor.Windows.Assets
for (var i = 0; i < parameters.Length; i++) for (var i = 0; i < parameters.Length; i++)
{ {
var p = parameters[i]; var p = parameters[i];
if (p.IsOverride) if (p.IsOverride && p.IsPublic)
{ {
p.IsOverride = false; p.IsOverride = false;
actions.Add(new EditParamOverrideAction actions.Add(new EditParamOverrideAction

View File

@@ -90,25 +90,15 @@ namespace FlaxEditor.Windows.Assets
var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage)."); var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage).");
gpu.CheckBox.Checked = sdfOptions.GPU; gpu.CheckBox.Checked = sdfOptions.GPU;
gpu.CheckBox.StateChanged += c => { Window._sdfOptions.GPU = c.Checked; };
var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "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."); var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "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.");
var backfacesThreshold = backfacesThresholdProp.FloatValue(); var backfacesThreshold = backfacesThresholdProp.FloatValue();
var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last();
backfacesThreshold.ValueBox.MinValue = 0.001f; backfacesThreshold.ValueBox.MinValue = 0.001f;
backfacesThreshold.ValueBox.MaxValue = 1.0f; backfacesThreshold.ValueBox.MaxValue = 1.0f;
backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold; backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold;
backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; }; backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; };
// Toggle Backfaces Threshold visibility (CPU-only option)
gpu.CheckBox.StateChanged += c =>
{
Window._sdfOptions.GPU = c.Checked;
backfacesThresholdLabel.Visible = !c.Checked;
backfacesThreshold.ValueBox.Visible = !c.Checked;
};
backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked;
backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked;
var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building."); var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building.");
lodIndex.IntValue.MinValue = 0; lodIndex.IntValue.MinValue = 0;
lodIndex.IntValue.MaxValue = Asset.LODsCount - 1; lodIndex.IntValue.MaxValue = Asset.LODsCount - 1;

View File

@@ -296,13 +296,15 @@ namespace FlaxEditor.Windows.Profiler
var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex); var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex);
if (resources == null || resources.Length == 0) if (resources == null || resources.Length == 0)
return; return;
var resourcesOrdered = resources.OrderByDescending(x => x.MemoryUsage); var resourcesOrdered = resources.OrderByDescending(x => x?.MemoryUsage ?? 0);
// Add rows // Add rows
var rowColor2 = Style.Current.Background * 1.4f; var rowColor2 = Style.Current.Background * 1.4f;
int rowIndex = 0; int rowIndex = 0;
foreach (var e in resourcesOrdered) foreach (var e in resourcesOrdered)
{ {
if (e == null)
continue;
ClickableRow row; ClickableRow row;
if (_tableRowsCache.Count != 0) if (_tableRowsCache.Count != 0)
{ {

View File

@@ -138,6 +138,7 @@ const Char* SplashScreenQuotes[] =
TEXT("Good Luck Have Fun"), TEXT("Good Luck Have Fun"),
TEXT("GG Well Played"), TEXT("GG Well Played"),
TEXT("Now with documentation."), TEXT("Now with documentation."),
TEXT("We do this not because it is easy,\nbut because we thought it would be easy"),
}; };
SplashScreen::~SplashScreen() SplashScreen::~SplashScreen()

View File

@@ -246,11 +246,19 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float
const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f; const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f;
#define ADD_OUTGOING_EVENT(type) context.Data->OutgoingEvents.Add({ k.Value.Instance, (AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime, AnimGraphInstanceData::OutgoingEvent::type }) #define ADD_OUTGOING_EVENT(type) context.Data->OutgoingEvents.Add({ k.Value.Instance, (AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime, AnimGraphInstanceData::OutgoingEvent::type })
if ((k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration if ((k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration()))) && (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration())
&& Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
// Handle the edge case of an event on 0 or on max animation duration during looping // Handle the edge case of an event on 0 or on max animation duration during looping
|| (loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == anim->GetDuration()) || (!loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration()))
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f) || (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f) || (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|| (Math::FloorToInt(animPos) == 1 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 1.0f)
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 1 && k.Time == 1.0f)
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 0.0f)
) )
{ {
int32 stateIndex = -1; int32 stateIndex = -1;

View File

@@ -243,7 +243,8 @@ Asset::LoadResult MaterialInstance::load()
ParamsChanged(); ParamsChanged();
} }
baseMaterial->RemoveReference(); if (baseMaterial)
baseMaterial->RemoveReference();
return LoadResult::Ok; return LoadResult::Ok;
} }

View File

@@ -262,6 +262,7 @@ bool Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, f
LOG(Warning, "Cannot generate SDF for virtual models on a main thread."); LOG(Warning, "Cannot generate SDF for virtual models on a main thread.");
return true; return true;
} }
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1); lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1);
// Generate SDF // Generate SDF

View File

@@ -61,7 +61,7 @@ public:
model->GetLODData(_lodIndex, data); model->GetLODData(_lodIndex, data);
if (data.IsInvalid()) if (data.IsInvalid())
{ {
LOG(Warning, "Missing data chunk"); LOG(Warning, "Missing data chunk with LOD{} for model '{}'", _lodIndex, model->ToString());
return true; return true;
} }
MemoryReadStream stream(data.Get(), data.Length()); MemoryReadStream stream(data.Get(), data.Length());
@@ -234,6 +234,7 @@ bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path)
LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data)."); LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data).");
return true; return true;
} }
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
ScopeLock lock(Locker); ScopeLock lock(Locker);
// Use a temporary chunks for data storage for virtual assets // Use a temporary chunks for data storage for virtual assets

View File

@@ -18,7 +18,7 @@ public:
/// <param name="id">The asset id.</param> /// <param name="id">The asset id.</param>
/// <returns>Loaded asset of null.</returns> /// <returns>Loaded asset of null.</returns>
template<typename T> template<typename T>
T* LoadAsync(const Guid& id) T* Load(const Guid& id)
{ {
for (auto& e : *this) for (auto& e : *this)
{ {
@@ -26,8 +26,10 @@ public:
return (T*)e.Get(); return (T*)e.Get();
} }
auto asset = (T*)::LoadAsset(id, T::TypeInitializer); auto asset = (T*)::LoadAsset(id, T::TypeInitializer);
if (asset) if (asset && !asset->WaitForLoaded())
Add(asset); Add(asset);
else
asset = nullptr;
return asset; return asset;
} }

View File

@@ -700,6 +700,7 @@ Asset* Content::GetAsset(const StringView& outputPath)
{ {
if (outputPath.IsEmpty()) if (outputPath.IsEmpty())
return nullptr; return nullptr;
PROFILE_CPU();
ScopeLock lock(AssetsLocker); ScopeLock lock(AssetsLocker);
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i) for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
{ {

View File

@@ -75,8 +75,6 @@ FlaxChunk* FlaxChunk::Clone() const
const int32 FlaxStorage::MagicCode = 1180124739; const int32 FlaxStorage::MagicCode = 1180124739;
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
struct Header struct Header
{ {
int32 MagicCode; int32 MagicCode;

View File

@@ -146,7 +146,6 @@ public:
struct LockData struct LockData
{ {
friend FlaxStorage; friend FlaxStorage;
static LockData Invalid;
private: private:
FlaxStorage* _storage; FlaxStorage* _storage;
@@ -159,6 +158,11 @@ public:
} }
public: public:
LockData()
: _storage(nullptr)
{
}
LockData(const LockData& other) LockData(const LockData& other)
: _storage(other._storage) : _storage(other._storage)
{ {

View File

@@ -620,14 +620,9 @@ bool Collision::RayIntersectsTriangle(const Ray& ray, const Vector3& a, const Ve
Real rayDistance = edge2.X * distanceCrossEdge1.X + edge2.Y * distanceCrossEdge1.Y + edge2.Z * distanceCrossEdge1.Z; Real rayDistance = edge2.X * distanceCrossEdge1.X + edge2.Y * distanceCrossEdge1.Y + edge2.Z * distanceCrossEdge1.Z;
rayDistance *= inverseDeterminant; rayDistance *= inverseDeterminant;
// Check if the triangle is behind the ray origin // Check if the triangle is in front the ray origin
if (rayDistance < 0.0f)
{
return false;
}
distance = rayDistance; distance = rayDistance;
return true; return rayDistance >= 0.0f;
} }
bool CollisionsHelper::RayIntersectsTriangle(const Ray& ray, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Real& distance, Vector3& normal) bool CollisionsHelper::RayIntersectsTriangle(const Ray& ray, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Real& distance, Vector3& normal)

View File

@@ -12,11 +12,6 @@ String Ray::ToString() const
return String::Format(TEXT("{}"), *this); return String::Format(TEXT("{}"), *this);
} }
Vector3 Ray::GetPoint(Real distance) const
{
return Position + Direction * distance;
}
Ray Ray::GetPickRay(float x, float y, const Viewport& viewport, const Matrix& vp) Ray Ray::GetPickRay(float x, float y, const Viewport& viewport, const Matrix& vp)
{ {
Vector3 nearPoint(x, y, 0.0f); Vector3 nearPoint(x, y, 0.0f);

View File

@@ -79,7 +79,10 @@ public:
/// </summary> /// </summary>
/// <param name="distance">The distance from ray origin.</param> /// <param name="distance">The distance from ray origin.</param>
/// <returns>The calculated point.</returns> /// <returns>The calculated point.</returns>
Vector3 GetPoint(Real distance) const; FORCE_INLINE Vector3 GetPoint(Real distance) const
{
return Position + Direction * distance;
}
/// <summary> /// <summary>
/// Determines if there is an intersection between ray and a point. /// Determines if there is an intersection between ray and a point.

View File

@@ -215,6 +215,11 @@ public:
return String(_data.Get(), _data.Count()); return String(_data.Get(), _data.Count());
} }
StringAnsi ToStringAnsi() const
{
return StringAnsi(_data.Get(), _data.Count());
}
StringView ToStringView() const; StringView ToStringView() const;
}; };

View File

@@ -771,7 +771,7 @@ Task* TextureBase::RequestMipDataAsync(int32 mipIndex)
FlaxStorage::LockData TextureBase::LockData() FlaxStorage::LockData TextureBase::LockData()
{ {
return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData::Invalid; return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData();
} }
void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const

View File

@@ -92,9 +92,8 @@ float GPUTimerQueryDX11::GetResult()
{ {
if (!_finalized) if (!_finalized)
{ {
#if BUILD_DEBUG if (!HasResult())
ASSERT(HasResult()); return 0;
#endif
UINT64 timeStart, timeEnd; UINT64 timeStart, timeEnd;
auto context = _device->GetIM(); auto context = _device->GetIM();

View File

@@ -29,6 +29,7 @@
#include "GPUVertexLayoutDX12.h" #include "GPUVertexLayoutDX12.h"
#include "CommandQueueDX12.h" #include "CommandQueueDX12.h"
#include "DescriptorHeapDX12.h" #include "DescriptorHeapDX12.h"
#include "RootSignatureDX12.h"
#include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTask.h"
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h" #include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
#include "Engine/Debug/Exceptions/NotImplementedException.h" #include "Engine/Debug/Exceptions/NotImplementedException.h"
@@ -307,6 +308,7 @@ void GPUContextDX12::Reset()
_device->DummyVB = _device->CreateBuffer(TEXT("DummyVertexBuffer")); _device->DummyVB = _device->CreateBuffer(TEXT("DummyVertexBuffer"));
auto* layout = GPUVertexLayout::Get({ { VertexElement::Types::Attribute3, 0, 0, 0, PixelFormat::R32G32B32A32_Float } }); auto* layout = GPUVertexLayout::Get({ { VertexElement::Types::Attribute3, 0, 0, 0, PixelFormat::R32G32B32A32_Float } });
_device->DummyVB->Init(GPUBufferDescription::Vertex(layout, sizeof(Color), 1, &Color::Transparent)); _device->DummyVB->Init(GPUBufferDescription::Vertex(layout, sizeof(Color), 1, &Color::Transparent));
SetResourceState(_device->DummyVB, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, 0);
} }
((GPUBufferDX12*)_device->DummyVB)->GetVBView(dummyVBView); ((GPUBufferDX12*)_device->DummyVB)->GetVBView(dummyVBView);
_commandList->IASetVertexBuffers(GPU_MAX_VB_BINDED, 1, &dummyVBView); _commandList->IASetVertexBuffers(GPU_MAX_VB_BINDED, 1, &dummyVBView);
@@ -628,7 +630,9 @@ void GPUContextDX12::flushPS()
LOG(Error, "Missing Vertex Layout (not assigned to GPUBuffer). Vertex Shader won't read valid data resulting incorrect visuals."); LOG(Error, "Missing Vertex Layout (not assigned to GPUBuffer). Vertex Shader won't read valid data resulting incorrect visuals.");
} }
#endif #endif
_commandList->SetPipelineState(_currentState->GetState(_rtDepth, _rtCount, _rtHandles, _vertexLayout)); ID3D12PipelineState* pso = _currentState->GetState(_rtDepth, _rtCount, _rtHandles, _vertexLayout);
ASSERT(pso);
_commandList->SetPipelineState(pso);
if (_primitiveTopology != _currentState->PrimitiveTopology) if (_primitiveTopology != _currentState->PrimitiveTopology)
{ {
_primitiveTopology = _currentState->PrimitiveTopology; _primitiveTopology = _currentState->PrimitiveTopology;

View File

@@ -12,6 +12,9 @@
#include "GPUSamplerDX12.h" #include "GPUSamplerDX12.h"
#include "GPUVertexLayoutDX12.h" #include "GPUVertexLayoutDX12.h"
#include "GPUSwapChainDX12.h" #include "GPUSwapChainDX12.h"
#include "RootSignatureDX12.h"
#include "UploadBufferDX12.h"
#include "CommandQueueDX12.h"
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/Engine/CommandLine.h" #include "Engine/Engine/CommandLine.h"
#include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTask.h"
@@ -21,20 +24,23 @@
#include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/Config/PlatformSettings.h" #include "Engine/Core/Config/PlatformSettings.h"
#include "UploadBufferDX12.h" #include "Engine/Core/Types/StringBuilder.h"
#include "CommandQueueDX12.h"
#include "Engine/Core/Utilities.h" #include "Engine/Core/Utilities.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "CommandSignatureDX12.h" #include "CommandSignatureDX12.h"
static bool CheckDX12Support(IDXGIAdapter* adapter) static bool CheckDX12Support(IDXGIAdapter* adapter)
{ {
#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE
return true;
#else
// Try to create device // Try to create device
if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
{ {
return true; return true;
} }
return false; return false;
#endif
} }
GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements, bool explicitOffsets) GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements, bool explicitOffsets)
@@ -55,6 +61,310 @@ GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements&
} }
} }
RootSignatureDX12::RootSignatureDX12()
{
// Clear structures
Platform::MemoryClear(this, sizeof(*this));
// Descriptor tables
{
// SRVs
D3D12_DESCRIPTOR_RANGE& range = _ranges[0];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
range.NumDescriptors = GPU_MAX_SR_BINDED;
range.BaseShaderRegister = 0;
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
{
// UAVs
D3D12_DESCRIPTOR_RANGE& range = _ranges[1];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
range.NumDescriptors = GPU_MAX_UA_BINDED;
range.BaseShaderRegister = 0;
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
{
// Samplers
D3D12_DESCRIPTOR_RANGE& range = _ranges[2];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT;
range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT;
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
// Root parameters
for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++)
{
// CBs
D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_CB + i];
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
param.Descriptor.ShaderRegister = i;
param.Descriptor.RegisterSpace = 0;
}
{
// SRVs
D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SR];
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
param.DescriptorTable.NumDescriptorRanges = 1;
param.DescriptorTable.pDescriptorRanges = &_ranges[0];
}
{
// UAVs
D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_UA];
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
param.DescriptorTable.NumDescriptorRanges = 1;
param.DescriptorTable.pDescriptorRanges = &_ranges[1];
}
{
// Samplers
D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SAMPLER];
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
param.DescriptorTable.NumDescriptorRanges = 1;
param.DescriptorTable.pDescriptorRanges = &_ranges[2];
}
// Static samplers
static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(_staticSamplers), "Update static samplers setup.");
// Linear Clamp
InitSampler(0, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
// Point Clamp
InitSampler(1, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
// Linear Wrap
InitSampler(2, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_WRAP);
// Point Wrap
InitSampler(3, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_WRAP);
// Shadow
InitSampler(4, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL);
// Shadow PCF
InitSampler(5, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL);
// Init
_desc.NumParameters = ARRAY_COUNT(_parameters);
_desc.pParameters = _parameters;
_desc.NumStaticSamplers = ARRAY_COUNT(_staticSamplers);
_desc.pStaticSamplers = _staticSamplers;
_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
}
void RootSignatureDX12::InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc)
{
auto& sampler = _staticSamplers[i];
sampler.Filter = filter;
sampler.AddressU = address;
sampler.AddressV = address;
sampler.AddressW = address;
sampler.MipLODBias = 0.0f;
sampler.MaxAnisotropy = 1;
sampler.ComparisonFunc = comparisonFunc;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
sampler.MinLOD = 0;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = i;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
}
ComPtr<ID3DBlob> RootSignatureDX12::Serialize() const
{
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&_desc, D3D_ROOT_SIGNATURE_VERSION_1_0, &signature, &error));
if (error.Get())
{
LOG(Error, "D3D12SerializeRootSignature failed with error: {}", String((const char*)error->GetBufferPointer()));
}
return signature;
}
#if USE_EDITOR
const Char* GetRootSignatureShaderVisibility(D3D12_SHADER_VISIBILITY visibility)
{
switch (visibility)
{
case D3D12_SHADER_VISIBILITY_VERTEX:
return TEXT(", visibility=SHADER_VISIBILITY_VERTEX");
case D3D12_SHADER_VISIBILITY_HULL:
return TEXT(", visibility=SHADER_VISIBILITY_HULL");
case D3D12_SHADER_VISIBILITY_DOMAIN:
return TEXT(", visibility=SHADER_VISIBILITY_DOMAIN");
case D3D12_SHADER_VISIBILITY_GEOMETRY:
return TEXT(", visibility=SHADER_VISIBILITY_GEOMETRY");
case D3D12_SHADER_VISIBILITY_PIXEL:
return TEXT(", visibility=SHADER_VISIBILITY_PIXEL");
case D3D12_SHADER_VISIBILITY_AMPLIFICATION:
return TEXT(", visibility=SHADER_VISIBILITY_AMPLIFICATION");
case D3D12_SHADER_VISIBILITY_MESH:
return TEXT(", visibility=SHADER_VISIBILITY_MESH");
case D3D12_SHADER_VISIBILITY_ALL:
default:
return TEXT(""); // Default
}
}
const Char* GetRootSignatureSamplerFilter(D3D12_FILTER filter)
{
switch (filter)
{
case D3D12_FILTER_MIN_MAG_MIP_POINT:
return TEXT("FILTER_MIN_MAG_MIP_POINT");
case D3D12_FILTER_MIN_MAG_MIP_LINEAR:
return TEXT("FILTER_MIN_MAG_MIP_LINEAR");
case D3D12_FILTER_ANISOTROPIC:
return TEXT("FILTER_ANISOTROPIC");
case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT:
return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_POINT");
case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR:
return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_LINEAR");
default:
CRASH; // Not implemented
}
}
const Char* GetRootSignatureSamplerAddress(D3D12_TEXTURE_ADDRESS_MODE address)
{
switch (address)
{
case D3D12_TEXTURE_ADDRESS_MODE_WRAP:
return TEXT("TEXTURE_ADDRESS_WRAP");
case D3D12_TEXTURE_ADDRESS_MODE_MIRROR:
return TEXT("TEXTURE_ADDRESS_MIRROR");
case D3D12_TEXTURE_ADDRESS_MODE_CLAMP:
return TEXT("TEXTURE_ADDRESS_CLAMP");
case D3D12_TEXTURE_ADDRESS_MODE_BORDER:
return TEXT("TEXTURE_ADDRESS_BORDER");
case D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE:
return TEXT("TEXTURE_ADDRESS_MIRROR_ONCE");
default:
return TEXT("");
}
}
const Char* GetRootSignatureSamplerComparisonFunc(D3D12_COMPARISON_FUNC func)
{
switch (func)
{
case D3D12_COMPARISON_FUNC_NEVER:
return TEXT("COMPARISON_NEVER");
case D3D12_COMPARISON_FUNC_LESS:
return TEXT("COMPARISON_LESS");
case D3D12_COMPARISON_FUNC_EQUAL:
return TEXT("COMPARISON_EQUAL");
case D3D12_COMPARISON_FUNC_LESS_EQUAL:
return TEXT("COMPARISON_LESS_EQUAL");
case D3D12_COMPARISON_FUNC_GREATER:
return TEXT("COMPARISON_GREATER");
case D3D12_COMPARISON_FUNC_NOT_EQUAL:
return TEXT("COMPARISON_NOT_EQUAL");
case D3D12_COMPARISON_FUNC_GREATER_EQUAL:
return TEXT("COMPARISON_GREATER_EQUAL");
case D3D12_COMPARISON_FUNC_ALWAYS:
default:
return TEXT("COMPARISON_ALWAYS");
}
}
void RootSignatureDX12::ToString(StringBuilder& sb, bool singleLine) const
{
// Flags
sb.Append(TEXT("RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)"));
// Parameters
const Char newLine = singleLine ? ' ' : '\n';
for (const D3D12_ROOT_PARAMETER& param : _parameters)
{
const Char* visibility = GetRootSignatureShaderVisibility(param.ShaderVisibility);
switch (param.ParameterType)
{
case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE:
sb.AppendFormat(TEXT(",{}DescriptorTable("), newLine);
for (uint32 rangeIndex = 0; rangeIndex < param.DescriptorTable.NumDescriptorRanges; rangeIndex++)
{
if (rangeIndex)
sb.Append(TEXT(", "));
const D3D12_DESCRIPTOR_RANGE& range = param.DescriptorTable.pDescriptorRanges[rangeIndex];
switch (range.RangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
sb.AppendFormat(TEXT("SRV(t{}"), range.BaseShaderRegister);
break;
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
sb.AppendFormat(TEXT("UAV(u{}"), range.BaseShaderRegister);
break;
case D3D12_DESCRIPTOR_RANGE_TYPE_CBV:
sb.AppendFormat(TEXT("CBV(b{}"), range.BaseShaderRegister);
break;
case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER:
sb.AppendFormat(TEXT("Sampler(s{}"), range.BaseShaderRegister);
break;
}
if (range.NumDescriptors != 1)
{
if (range.NumDescriptors == UINT_MAX)
sb.Append(TEXT(", numDescriptors=unbounded"));
else
sb.AppendFormat(TEXT(", numDescriptors={}"), range.NumDescriptors);
}
if (range.OffsetInDescriptorsFromTableStart != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
sb.AppendFormat(TEXT(", offset={}"), range.OffsetInDescriptorsFromTableStart);
sb.Append(')');
}
sb.AppendFormat(TEXT("{})"), visibility);
break;
case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS:
sb.AppendFormat(TEXT(",{}RootConstants(num32BitConstants={}, b{}{})"), newLine, param.Constants.Num32BitValues, param.Constants.ShaderRegister, visibility);
break;
case D3D12_ROOT_PARAMETER_TYPE_CBV:
sb.AppendFormat(TEXT(",{}CBV(b{}{})"), newLine, param.Descriptor.ShaderRegister, visibility);
break;
case D3D12_ROOT_PARAMETER_TYPE_SRV:
sb.AppendFormat(TEXT(",{}SRV(t{}{})"), newLine, param.Descriptor.ShaderRegister, visibility);
break;
case D3D12_ROOT_PARAMETER_TYPE_UAV:
sb.AppendFormat(TEXT(",{}UAV(u{}{})"), newLine, param.Descriptor.ShaderRegister, visibility);
break;
}
}
// Static Samplers
for (const D3D12_STATIC_SAMPLER_DESC& sampler : _staticSamplers)
{
const Char* visibility = GetRootSignatureShaderVisibility(sampler.ShaderVisibility);
sb.AppendFormat(TEXT(",{}StaticSampler(s{}"), newLine, sampler.ShaderRegister);
sb.AppendFormat(TEXT(", filter={}"), GetRootSignatureSamplerFilter(sampler.Filter));
sb.AppendFormat(TEXT(", addressU={}"), GetRootSignatureSamplerAddress(sampler.AddressU));
sb.AppendFormat(TEXT(", addressV={}"), GetRootSignatureSamplerAddress(sampler.AddressV));
sb.AppendFormat(TEXT(", addressW={}"), GetRootSignatureSamplerAddress(sampler.AddressW));
sb.AppendFormat(TEXT(", comparisonFunc={}"), GetRootSignatureSamplerComparisonFunc(sampler.ComparisonFunc));
sb.AppendFormat(TEXT(", maxAnisotropy={}"), sampler.MaxAnisotropy);
sb.Append(TEXT(", borderColor=STATIC_BORDER_COLOR_OPAQUE_BLACK"));
sb.AppendFormat(TEXT("{})"), visibility);
}
}
String RootSignatureDX12::ToString() const
{
StringBuilder sb;
ToString(sb);
return sb.ToString();
}
StringAnsi RootSignatureDX12::ToStringAnsi() const
{
StringBuilder sb;
ToString(sb);
return sb.ToStringAnsi();
}
#endif
GPUDevice* GPUDeviceDX12::Create() GPUDevice* GPUDeviceDX12::Create()
{ {
#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE #if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE
@@ -561,170 +871,10 @@ bool GPUDeviceDX12::Init()
} }
// Create root signature // Create root signature
// TODO: maybe create set of different root signatures? for UAVs, for compute, for simple drawing, for post fx?
{ {
// Descriptor tables RootSignatureDX12 signature;
D3D12_DESCRIPTOR_RANGE r[3]; // SRV+UAV+Sampler ComPtr<ID3DBlob> signatureBlob = signature.Serialize();
{ VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&_rootSignature)));
D3D12_DESCRIPTOR_RANGE& range = r[0];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
range.NumDescriptors = GPU_MAX_SR_BINDED;
range.BaseShaderRegister = 0;
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
{
D3D12_DESCRIPTOR_RANGE& range = r[1];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
range.NumDescriptors = GPU_MAX_UA_BINDED;
range.BaseShaderRegister = 0;
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
{
D3D12_DESCRIPTOR_RANGE& range = r[2];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT;
range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT;
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
// Root parameters
D3D12_ROOT_PARAMETER rootParameters[GPU_MAX_CB_BINDED + 3];
for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++)
{
// CB
D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_CB + i];
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParam.Descriptor.ShaderRegister = i;
rootParam.Descriptor.RegisterSpace = 0;
}
{
// SRVs
D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_SR];
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParam.DescriptorTable.NumDescriptorRanges = 1;
rootParam.DescriptorTable.pDescriptorRanges = &r[0];
}
{
// UAVs
D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_UA];
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParam.DescriptorTable.NumDescriptorRanges = 1;
rootParam.DescriptorTable.pDescriptorRanges = &r[1];
}
{
// Samplers
D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_SAMPLER];
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParam.DescriptorTable.NumDescriptorRanges = 1;
rootParam.DescriptorTable.pDescriptorRanges = &r[2];
}
// Static samplers
D3D12_STATIC_SAMPLER_DESC staticSamplers[6];
static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(staticSamplers), "Update static samplers setup.");
// Linear Clamp
staticSamplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
staticSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[0].MipLODBias = 0.0f;
staticSamplers[0].MaxAnisotropy = 1;
staticSamplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
staticSamplers[0].MinLOD = 0;
staticSamplers[0].MaxLOD = D3D12_FLOAT32_MAX;
staticSamplers[0].ShaderRegister = 0;
staticSamplers[0].RegisterSpace = 0;
staticSamplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Point Clamp
staticSamplers[1].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
staticSamplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[1].MipLODBias = 0.0f;
staticSamplers[1].MaxAnisotropy = 1;
staticSamplers[1].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
staticSamplers[1].MinLOD = 0;
staticSamplers[1].MaxLOD = D3D12_FLOAT32_MAX;
staticSamplers[1].ShaderRegister = 1;
staticSamplers[1].RegisterSpace = 0;
staticSamplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Linear Wrap
staticSamplers[2].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
staticSamplers[2].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSamplers[2].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSamplers[2].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSamplers[2].MipLODBias = 0.0f;
staticSamplers[2].MaxAnisotropy = 1;
staticSamplers[2].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
staticSamplers[2].MinLOD = 0;
staticSamplers[2].MaxLOD = D3D12_FLOAT32_MAX;
staticSamplers[2].ShaderRegister = 2;
staticSamplers[2].RegisterSpace = 0;
staticSamplers[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Point Wrap
staticSamplers[3].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
staticSamplers[3].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSamplers[3].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSamplers[3].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSamplers[3].MipLODBias = 0.0f;
staticSamplers[3].MaxAnisotropy = 1;
staticSamplers[3].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
staticSamplers[3].MinLOD = 0;
staticSamplers[3].MaxLOD = D3D12_FLOAT32_MAX;
staticSamplers[3].ShaderRegister = 3;
staticSamplers[3].RegisterSpace = 0;
staticSamplers[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Shadow
staticSamplers[4].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
staticSamplers[4].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[4].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[4].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[4].MipLODBias = 0.0f;
staticSamplers[4].MaxAnisotropy = 1;
staticSamplers[4].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
staticSamplers[4].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
staticSamplers[4].MinLOD = 0;
staticSamplers[4].MaxLOD = D3D12_FLOAT32_MAX;
staticSamplers[4].ShaderRegister = 4;
staticSamplers[4].RegisterSpace = 0;
staticSamplers[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Shadow PCF
staticSamplers[5].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
staticSamplers[5].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[5].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[5].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
staticSamplers[5].MipLODBias = 0.0f;
staticSamplers[5].MaxAnisotropy = 1;
staticSamplers[5].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
staticSamplers[5].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
staticSamplers[5].MinLOD = 0;
staticSamplers[5].MaxLOD = D3D12_FLOAT32_MAX;
staticSamplers[5].ShaderRegister = 5;
staticSamplers[5].RegisterSpace = 0;
staticSamplers[5].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Init
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.NumParameters = ARRAY_COUNT(rootParameters);
rootSignatureDesc.pParameters = rootParameters;
rootSignatureDesc.NumStaticSamplers = ARRAY_COUNT(staticSamplers);
rootSignatureDesc.pStaticSamplers = staticSamplers;
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
// Serialize
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
// Create
VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_rootSignature)));
} }
if (TimestampQueryHeap.Init()) if (TimestampQueryHeap.Init())

View File

@@ -18,11 +18,6 @@
#define DX12_BACK_BUFFER_COUNT 2 #define DX12_BACK_BUFFER_COUNT 2
#endif #endif
#define DX12_ROOT_SIGNATURE_CB 0
#define DX12_ROOT_SIGNATURE_SR (GPU_MAX_CB_BINDED+0)
#define DX12_ROOT_SIGNATURE_UA (GPU_MAX_CB_BINDED+1)
#define DX12_ROOT_SIGNATURE_SAMPLER (GPU_MAX_CB_BINDED+2)
class Engine; class Engine;
class WindowsWindow; class WindowsWindow;
class GPUContextDX12; class GPUContextDX12;

View File

@@ -0,0 +1,33 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Graphics/Config.h"
#include "../IncludeDirectXHeaders.h"
#define DX12_ROOT_SIGNATURE_CB 0
#define DX12_ROOT_SIGNATURE_SR (GPU_MAX_CB_BINDED+0)
#define DX12_ROOT_SIGNATURE_UA (GPU_MAX_CB_BINDED+1)
#define DX12_ROOT_SIGNATURE_SAMPLER (GPU_MAX_CB_BINDED+2)
struct RootSignatureDX12
{
private:
D3D12_ROOT_SIGNATURE_DESC _desc;
D3D12_DESCRIPTOR_RANGE _ranges[3];
D3D12_ROOT_PARAMETER _parameters[GPU_MAX_CB_BINDED + 3];
D3D12_STATIC_SAMPLER_DESC _staticSamplers[6];
public:
RootSignatureDX12();
ComPtr<ID3DBlob> Serialize() const;
#if USE_EDITOR
void ToString(class StringBuilder& sb, bool singleLine = false) const;
String ToString() const;
StringAnsi ToStringAnsi() const;
#endif
private:
void InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL);
};

View File

@@ -518,7 +518,7 @@ namespace
Vector3 nextPos = transform.LocalToWorld(next->Value.Translation); Vector3 nextPos = transform.LocalToWorld(next->Value.Translation);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, NodeSizeByDistance(nextPos, scaleByDistance)), color, 0.0f, depthTest); DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, NodeSizeByDistance(nextPos, scaleByDistance)), color, 0.0f, depthTest);
const float d = (next->Time - prev->Time) / 3.0f; const float d = (next->Time - prev->Time) / 3.0f;
DEBUG_DRAW_BEZIER(prevPos, prevPos + prev->TangentOut.Translation * d, nextPos + next->TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); DEBUG_DRAW_BEZIER(prevPos, transform.LocalToWorld(prev->Value.Translation + prev->TangentOut.Translation * d), transform.LocalToWorld(next->Value.Translation + next->TangentIn.Translation * d), nextPos, color, 0.0f, depthTest);
prev = next; prev = next;
prevPos = nextPos; prevPos = nextPos;
} }

View File

@@ -425,8 +425,8 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va
case 300: case 300:
{ {
// Load function asset // Load function asset
const auto function = Assets.LoadAsync<ParticleEmitterFunction>((Guid)node->Values[0]); const auto function = Assets.Load<ParticleEmitterFunction>((Guid)node->Values[0]);
if (!function || function->WaitForLoaded()) if (!function)
{ {
OnError(node, box, TEXT("Missing or invalid function.")); OnError(node, box, TEXT("Missing or invalid function."));
value = Value::Zero; value = Value::Zero;
@@ -439,7 +439,7 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va
{ {
if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300)) if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300))
{ {
const auto callFunc = Assets.LoadAsync<ParticleEmitterFunction>((Guid)_callStack[i]->Values[0]); const auto callFunc = Assets.Load<ParticleEmitterFunction>((Guid)_callStack[i]->Values[0]);
if (callFunc == function) if (callFunc == function)
{ {
OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString())); OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString()));
@@ -514,7 +514,7 @@ void ParticleEmitterGPUGenerator::ProcessGroupFunction(Box* box, Node* node, Val
value = Value::Zero; value = Value::Zero;
break; break;
} }
const auto function = Assets.LoadAsync<ParticleEmitterFunction>((Guid)functionCallNode->Values[0]); const auto function = Assets.Load<ParticleEmitterFunction>((Guid)functionCallNode->Values[0]);
if (!_functions.TryGet(functionCallNode, graph) || !function) if (!_functions.TryGet(functionCallNode, graph) || !function)
{ {
OnError(node, box, TEXT("Missing calling function graph.")); OnError(node, box, TEXT("Missing calling function graph."));

View File

@@ -156,7 +156,7 @@ void ParticleSystemInstance::Sync(ParticleSystem* system)
if (GPUParticlesCountReadback) if (GPUParticlesCountReadback)
GPUParticlesCountReadback->ReleaseGPU(); GPUParticlesCountReadback->ReleaseGPU();
} }
ASSERT(Emitters.Count() == system->Emitters.Count()); CHECK(Emitters.Count() == system->Emitters.Count());
} }
bool ParticleSystemInstance::ContainsEmitter(ParticleEmitter* emitter) const bool ParticleSystemInstance::ContainsEmitter(ParticleEmitter* emitter) const

View File

@@ -194,7 +194,6 @@ void Collider::UpdateLayerBits()
// Own layer mask // Own layer mask
const uint32 mask1 = Physics::LayerMasks[GetLayer()]; const uint32 mask1 = Physics::LayerMasks[GetLayer()];
ASSERT(_shape);
PhysicsBackend::SetShapeFilterMask(_shape, mask0, mask1); PhysicsBackend::SetShapeFilterMask(_shape, mask0, mask1);
} }

View File

@@ -3,6 +3,7 @@
#if COMPILE_WITH_EMPTY_PHYSICS #if COMPILE_WITH_EMPTY_PHYSICS
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/CollisionData.h" #include "Engine/Physics/CollisionData.h"
#include "Engine/Physics/PhysicalMaterial.h" #include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Physics/PhysicsScene.h" #include "Engine/Physics/PhysicsScene.h"
@@ -226,26 +227,6 @@ bool PhysicsBackend::CheckConvex(void* scene, const Vector3& center, const Colli
return false; return false;
} }
bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return false;
}
bool PhysicsBackend::OverlapSphere(void* scene, const Vector3& center, const float radius, Array<Collider*>& results, uint32 layerMask, bool hitTriggers)
{
return false;
}
bool PhysicsBackend::OverlapCapsule(void* scene, const Vector3& center, const float radius, const float height, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return false;
}
bool PhysicsBackend::OverlapConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return false;
}
bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{ {
return false; return false;
@@ -353,7 +334,7 @@ Vector3 PhysicsBackend::GetRigidDynamicActorCenterOfMass(void* actor)
return Vector3::Zero; return Vector3::Zero;
} }
void PhysicsBackend::SetRigidDynamicActorCenterOfMassOffset(void* actor, const Float3& value) void PhysicsBackend::AddRigidDynamicActorCenterOfMassOffset(void* actor, const Float3& value)
{ {
} }
@@ -698,6 +679,15 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value)
{ {
} }
Vector3 PhysicsBackend::GetControllerBasePosition(void* controller)
{
return Vector3::Zero;
}
void PhysicsBackend::SetControllerBasePosition(void* controller, const Vector3& value)
{
}
Vector3 PhysicsBackend::GetControllerUpDirection(void* controller) Vector3 PhysicsBackend::GetControllerUpDirection(void* controller)
{ {
return Vector3::Up; return Vector3::Up;

View File

@@ -23,6 +23,10 @@ private:
StringAnsiView _fullname; StringAnsiView _fullname;
uint32 _types = 0; uint32 _types = 0;
mutable uint32 _size = 0; mutable uint32 _size = 0;
#else
StringAnsiView _name;
StringAnsiView _namespace;
StringAnsiView _fullname;
#endif #endif
MAssembly* _assembly; MAssembly* _assembly;

View File

@@ -21,6 +21,7 @@
MDomain* MRootDomain = nullptr; MDomain* MRootDomain = nullptr;
MDomain* MActiveDomain = nullptr; MDomain* MActiveDomain = nullptr;
Array<MDomain*, FixedAllocation<4>> MDomains; Array<MDomain*, FixedAllocation<4>> MDomains;
bool MCore::Ready = false;
MClass* MCore::TypeCache::Void = nullptr; MClass* MCore::TypeCache::Void = nullptr;
MClass* MCore::TypeCache::Object = nullptr; MClass* MCore::TypeCache::Object = nullptr;
@@ -301,6 +302,11 @@ bool MProperty::IsStatic() const
return false; return false;
} }
void MCore::OnManagedEventAfterShutdown(const char* eventName)
{
LOG(Error, "Found a binding leak on '{}' event used by C# scripting after shutdown. Ensure to unregister scripting events from objects during disposing.", ::String(eventName));
}
MDomain* MCore::GetRootDomain() MDomain* MCore::GetRootDomain()
{ {
return MRootDomain; return MRootDomain;

View File

@@ -57,6 +57,10 @@ public:
static void UnloadScriptingAssemblyLoadContext(); static void UnloadScriptingAssemblyLoadContext();
#endif #endif
// Utility for guarding against using C# scripting runtime after shutdown (eg. when asset delegate is not properly disposed).
static bool Ready;
static void OnManagedEventAfterShutdown(const char* eventName);
public: public:
/// <summary> /// <summary>
/// Utilities for C# object management. /// Utilities for C# object management.

View File

@@ -19,6 +19,8 @@ protected:
#elif USE_NETCORE #elif USE_NETCORE
void* _handle; void* _handle;
StringAnsiView _name; StringAnsiView _name;
#else
StringAnsiView _name;
#endif #endif
mutable MMethod* _addMethod; mutable MMethod* _addMethod;

View File

@@ -24,6 +24,8 @@ protected:
void* _type; void* _type;
int32 _fieldOffset; int32 _fieldOffset;
StringAnsiView _name; StringAnsiView _name;
#else
StringAnsiView _name;
#endif #endif
MClass* _parentClass; MClass* _parentClass;

View File

@@ -30,6 +30,8 @@ protected:
mutable void* _returnType; mutable void* _returnType;
mutable Array<void*, InlinedAllocation<8>> _parameterTypes; mutable Array<void*, InlinedAllocation<8>> _parameterTypes;
void CacheSignature() const; void CacheSignature() const;
#else
StringAnsiView _name;
#endif #endif
MClass* _parentClass; MClass* _parentClass;
MVisibility _visibility; MVisibility _visibility;

View File

@@ -22,6 +22,8 @@ protected:
#elif USE_NETCORE #elif USE_NETCORE
void* _handle; void* _handle;
StringAnsiView _name; StringAnsiView _name;
#else
StringAnsiView _name;
#endif #endif
mutable MMethod* _getMethod; mutable MMethod* _getMethod;

View File

@@ -316,7 +316,8 @@ bool MCore::LoadEngine()
char* buildInfo = CallStaticMethod<char*>(GetStaticMethodPointer(TEXT("GetRuntimeInformation"))); char* buildInfo = CallStaticMethod<char*>(GetStaticMethodPointer(TEXT("GetRuntimeInformation")));
LOG(Info, ".NET runtime version: {0}", ::String(buildInfo)); LOG(Info, ".NET runtime version: {0}", ::String(buildInfo));
MCore::GC::FreeMemory(buildInfo); GC::FreeMemory(buildInfo);
Ready = true;
return false; return false;
} }
@@ -327,6 +328,7 @@ void MCore::UnloadEngine()
return; return;
PROFILE_CPU(); PROFILE_CPU();
CallStaticMethod<void>(GetStaticMethodPointer(TEXT("Exit"))); CallStaticMethod<void>(GetStaticMethodPointer(TEXT("Exit")));
Ready = false;
MDomains.ClearDelete(); MDomains.ClearDelete();
MRootDomain = nullptr; MRootDomain = nullptr;
ShutdownHostfxr(); ShutdownHostfxr();
@@ -2028,13 +2030,13 @@ static MonoAssembly* OnMonoAssemblyLoad(const char* aname)
String fileName = name; String fileName = name;
if (!name.EndsWith(TEXT(".dll")) && !name.EndsWith(TEXT(".exe"))) if (!name.EndsWith(TEXT(".dll")) && !name.EndsWith(TEXT(".exe")))
fileName += TEXT(".dll"); fileName += TEXT(".dll");
String path = fileName; String path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName;
if (!FileSystem::FileExists(path)) if (!FileSystem::FileExists(path))
{ {
path = Globals::ProjectFolder / String(TEXT("/Dotnet/shared/Microsoft.NETCore.App/")) / fileName; path = Globals::ProjectFolder / String(TEXT("/Dotnet/shared/Microsoft.NETCore.App/")) / fileName;
if (!FileSystem::FileExists(path)) if (!FileSystem::FileExists(path))
{ {
path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName; path = fileName;
} }
} }

View File

@@ -93,6 +93,8 @@ ScriptingObject::ScriptingObject(const SpawnParams& params)
: _gcHandle((MGCHandle)params.Managed) : _gcHandle((MGCHandle)params.Managed)
#elif !COMPILE_WITHOUT_CSHARP #elif !COMPILE_WITHOUT_CSHARP
: _gcHandle(params.Managed ? MCore::GCHandle::New(params.Managed) : 0) : _gcHandle(params.Managed ? MCore::GCHandle::New(params.Managed) : 0)
#else
: _gcHandle(0)
#endif #endif
, _type(params.Type) , _type(params.Type)
, _id(params.ID) , _id(params.ID)

View File

@@ -15,7 +15,6 @@ class MemoryWriteStream;
struct FLAXENGINE_API ShaderCompilationOptions struct FLAXENGINE_API ShaderCompilationOptions
{ {
public: public:
/// <summary> /// <summary>
/// Name of the target object (name of the shader or material for better logging readability) /// Name of the target object (name of the shader or material for better logging readability)
/// </summary> /// </summary>
@@ -37,12 +36,16 @@ public:
uint32 SourceLength = 0; uint32 SourceLength = 0;
public: public:
/// <summary> /// <summary>
/// Target shader profile /// Target shader profile
/// </summary> /// </summary>
ShaderProfile Profile = ShaderProfile::Unknown; ShaderProfile Profile = ShaderProfile::Unknown;
/// <summary>
/// Target platform, set to invalid value of 0 if not used (platform-independent compilation).
/// </summary>
PlatformType Platform = (PlatformType)0;
/// <summary> /// <summary>
/// Disables shaders compiler optimizations. Can be used to debug shaders on a target platform or to speed up the shaders compilation time. /// Disables shaders compiler optimizations. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.
/// </summary> /// </summary>
@@ -64,7 +67,6 @@ public:
Array<ShaderMacro> Macros; Array<ShaderMacro> Macros;
public: public:
/// <summary> /// <summary>
/// Output stream to write compiled shader cache to /// Output stream to write compiled shader cache to
/// </summary> /// </summary>

View File

@@ -196,7 +196,7 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation
default: default:
return true; return true;
} }
if (_profile == ShaderProfile::DirectX_SM5) if (GetProfile() == ShaderProfile::DirectX_SM5)
{ {
profileName += "_5_0"; profileName += "_5_0";
} }

View File

@@ -59,7 +59,8 @@ public:
*ppIncludeSource = nullptr; *ppIncludeSource = nullptr;
const char* source; const char* source;
int32 sourceLength; int32 sourceLength;
const StringAnsi filename(pFilename); StringAnsi filename(pFilename);
filename.Replace('\\', '/');
if (ShaderCompiler::GetIncludedFileSource(_context, "", filename.Get(), source, sourceLength)) if (ShaderCompiler::GetIncludedFileSource(_context, "", filename.Get(), source, sourceLength))
return E_FAIL; return E_FAIL;
IDxcBlobEncoding* textBlob; IDxcBlobEncoding* textBlob;
@@ -70,25 +71,25 @@ public:
} }
}; };
ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile) ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform, void* dxcCreateInstanceProc)
: ShaderCompiler(profile) : ShaderCompiler(profile, platform)
{ {
IDxcCompiler3* compiler = nullptr; IDxcCompiler3* compiler = nullptr;
IDxcLibrary* library = nullptr; IDxcLibrary* library = nullptr;
IDxcContainerReflection* containerReflection = nullptr; IDxcContainerReflection* containerReflection = nullptr;
if (FAILED(DxcCreateInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast<void**>(&compiler))) || DxcCreateInstanceProc createInstance = dxcCreateInstanceProc ? (DxcCreateInstanceProc)dxcCreateInstanceProc : &DxcCreateInstance;
FAILED(DxcCreateInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast<void**>(&library))) || if (FAILED(createInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast<void**>(&compiler))) ||
FAILED(DxcCreateInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast<void**>(&containerReflection)))) FAILED(createInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast<void**>(&library))) ||
FAILED(createInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast<void**>(&containerReflection))))
{ {
LOG(Error, "DxcCreateInstance failed"); LOG(Error, "DxcCreateInstance failed");
} }
_compiler = compiler; _compiler = compiler;
_library = library; _library = library;
_containerReflection = containerReflection; _containerReflection = containerReflection;
static bool PrintVersion = true; static HashSet<void*> PrintVersions;
if (PrintVersion) if (PrintVersions.Add(createInstance))
{ {
PrintVersion = false;
IDxcVersionInfo* version = nullptr; IDxcVersionInfo* version = nullptr;
if (compiler && SUCCEEDED(compiler->QueryInterface(__uuidof(version), reinterpret_cast<void**>(&version)))) if (compiler && SUCCEEDED(compiler->QueryInterface(__uuidof(version), reinterpret_cast<void**>(&version))))
{ {
@@ -221,6 +222,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
argsFull.Add(TEXT("-D")); argsFull.Add(TEXT("-D"));
argsFull.Add(*d); argsFull.Add(*d);
} }
GetArgs(meta, argsFull);
// Compile // Compile
ComPtr<IDxcResult> results; ComPtr<IDxcResult> results;

View File

@@ -22,7 +22,9 @@ public:
/// Initializes a new instance of the <see cref="ShaderCompilerDX"/> class. /// Initializes a new instance of the <see cref="ShaderCompilerDX"/> class.
/// </summary> /// </summary>
/// <param name="profile">The profile.</param> /// <param name="profile">The profile.</param>
ShaderCompilerDX(ShaderProfile profile); /// <param name="platform">The platform.</param>
/// <param name="dxcCreateInstanceProc">The custom DXC Compiler factory function.</param>
ShaderCompilerDX(ShaderProfile profile, PlatformType platform = (PlatformType)0, void* dxcCreateInstanceProc = nullptr);
/// <summary> /// <summary>
/// Finalizes an instance of the <see cref="ShaderCompilerDX"/> class. /// Finalizes an instance of the <see cref="ShaderCompilerDX"/> class.
@@ -30,6 +32,10 @@ public:
~ShaderCompilerDX(); ~ShaderCompilerDX();
protected: protected:
virtual void GetArgs(ShaderFunctionMeta& meta, Array<const Char*, InlinedAllocation<250>>& args)
{
}
// [ShaderCompiler] // [ShaderCompiler]
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override; bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
bool OnCompileBegin() override; bool OnCompileBegin() override;

View File

@@ -23,10 +23,11 @@ public:
}; };
private: private:
ShaderProfile _profile;
PlatformType _platform;
Array<char> _funcNameDefineBuffer; Array<char> _funcNameDefineBuffer;
protected: protected:
ShaderProfile _profile;
ShaderCompilationContext* _context = nullptr; ShaderCompilationContext* _context = nullptr;
Array<ShaderMacro> _globalMacros; Array<ShaderMacro> _globalMacros;
Array<ShaderMacro> _macros; Array<ShaderMacro> _macros;
@@ -37,8 +38,10 @@ public:
/// Initializes a new instance of the <see cref="ShaderCompiler"/> class. /// Initializes a new instance of the <see cref="ShaderCompiler"/> class.
/// </summary> /// </summary>
/// <param name="profile">The profile.</param> /// <param name="profile">The profile.</param>
ShaderCompiler(ShaderProfile profile) /// <param name="platform">The platform.</param>
ShaderCompiler(ShaderProfile profile, PlatformType platform = (PlatformType)0)
: _profile(profile) : _profile(profile)
, _platform(platform)
{ {
} }
@@ -51,12 +54,19 @@ public:
/// <summary> /// <summary>
/// Gets shader profile supported by this compiler. /// Gets shader profile supported by this compiler.
/// </summary> /// </summary>
/// <returns>The shader profile.</returns>
FORCE_INLINE ShaderProfile GetProfile() const FORCE_INLINE ShaderProfile GetProfile() const
{ {
return _profile; return _profile;
} }
/// <summary>
/// Gets target platform supported by this compiler. Returns invalid value of '0' if any platform works.
/// </summary>
FORCE_INLINE PlatformType GetPlatform() const
{
return _platform;
}
/// <summary> /// <summary>
/// Performs the shader compilation. /// Performs the shader compilation.
/// </summary> /// </summary>

View File

@@ -63,6 +63,8 @@ public class ShadersCompilation : EngineModule
options.PrivateDependencies.Add("ShaderCompilerPS4"); options.PrivateDependencies.Add("ShaderCompilerPS4");
if (Sdk.HasValid("PS5Sdk")) if (Sdk.HasValid("PS5Sdk"))
options.PrivateDependencies.Add("ShaderCompilerPS5"); options.PrivateDependencies.Add("ShaderCompilerPS5");
if (Flax.Build.Platform.GetPlatform(TargetPlatform.XboxScarlett, true) != null)
options.PrivateDependencies.Add("ShaderCompilerXboxScarlett");
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -48,6 +48,9 @@
#if COMPILE_WITH_PS5_SHADER_COMPILER #if COMPILE_WITH_PS5_SHADER_COMPILER
#include "Platforms/PS5/Engine/ShaderCompilerPS5/ShaderCompilerPS5.h" #include "Platforms/PS5/Engine/ShaderCompilerPS5/ShaderCompilerPS5.h"
#endif #endif
#if COMPILE_WITH_XBOX_SCARLETT_SHADER_COMPILER
#include "Platforms/XboxScarlett/Engine/ShaderCompilerXboxScarlett/ShaderCompilerXboxScarlett.h"
#endif
namespace ShadersCompilationImpl namespace ShadersCompilationImpl
{ {
@@ -165,20 +168,16 @@ bool ShadersCompilation::Compile(ShaderCompilationOptions& options)
bool result; bool result;
{ {
ShaderCompilationContext context(&options, &meta); ShaderCompilationContext context(&options, &meta);
auto compiler = RequestCompiler(options.Profile, options.Platform);
// Request shaders compiler
auto compiler = RequestCompiler(options.Profile);
if (compiler == nullptr) if (compiler == nullptr)
{ {
LOG(Error, "Shader compiler request failed."); LOG(Error, "Shader compiler request failed.");
return true; return true;
} }
ASSERT(compiler->GetProfile() == options.Profile); ASSERT_LOW_LAYER(compiler->GetProfile() == options.Profile);
// Call compilation process // Call compilation process
result = compiler->Compile(&context); result = compiler->Compile(&context);
// Dismiss compiler
FreeCompiler(compiler); FreeCompiler(compiler);
#if GPU_USE_SHADERS_DEBUG_LAYER #if GPU_USE_SHADERS_DEBUG_LAYER
@@ -210,65 +209,65 @@ bool ShadersCompilation::Compile(ShaderCompilationOptions& options)
return result; return result;
} }
ShaderCompiler* ShadersCompilation::CreateCompiler(ShaderProfile profile) ShaderCompiler* ShadersCompilation::RequestCompiler(ShaderProfile profile, PlatformType platform)
{ {
ShaderCompiler* result = nullptr;
switch (profile)
{
#if COMPILE_WITH_D3D_SHADER_COMPILER
case ShaderProfile::DirectX_SM4:
case ShaderProfile::DirectX_SM5:
result = New<ShaderCompilerD3D>(profile);
break;
#endif
#if COMPILE_WITH_DX_SHADER_COMPILER
case ShaderProfile::DirectX_SM6:
result = New<ShaderCompilerDX>(profile);
break;
#endif
#if COMPILE_WITH_VK_SHADER_COMPILER
case ShaderProfile::Vulkan_SM5:
result = New<ShaderCompilerVulkan>(profile);
break;
#endif
#if COMPILE_WITH_PS4_SHADER_COMPILER
case ShaderProfile::PS4:
result = New<ShaderCompilerPS4>();
break;
#endif
#if COMPILE_WITH_PS5_SHADER_COMPILER
case ShaderProfile::PS5:
result = New<ShaderCompilerPS5>();
break;
#endif
default:
break;
}
ASSERT_LOW_LAYER(result == nullptr || result->GetProfile() == profile);
return result;
}
ShaderCompiler* ShadersCompilation::RequestCompiler(ShaderProfile profile)
{
ShaderCompiler* compiler;
ScopeLock lock(Locker); ScopeLock lock(Locker);
// Try to find ready compiler // Try to find ready compiler
ShaderCompiler* compiler = nullptr;
for (int32 i = 0; i < ReadyCompilers.Count(); i++) for (int32 i = 0; i < ReadyCompilers.Count(); i++)
{ {
compiler = ReadyCompilers[i]; compiler = ReadyCompilers.Get()[i];
if (compiler->GetProfile() == profile) if (compiler->GetProfile() == profile &&
(compiler->GetPlatform() == platform || (int32)compiler->GetPlatform() == 0))
{ {
// Use it
ReadyCompilers.RemoveAt(i); ReadyCompilers.RemoveAt(i);
return compiler; return compiler;
} }
} }
// Create new compiler for a target profile // Create new compiler for a target profile
compiler = CreateCompiler(profile); switch (profile)
{
#if COMPILE_WITH_D3D_SHADER_COMPILER
case ShaderProfile::DirectX_SM4:
case ShaderProfile::DirectX_SM5:
compiler = New<ShaderCompilerD3D>(profile);
break;
#endif
#if COMPILE_WITH_DX_SHADER_COMPILER
case ShaderProfile::DirectX_SM6:
switch (platform)
{
case PlatformType::XboxScarlett:
#if COMPILE_WITH_XBOX_SCARLETT_SHADER_COMPILER
compiler = New<ShaderCompilerXboxScarlett>();
#endif
break;
default:
compiler = New<ShaderCompilerDX>(profile);
break;
}
break;
#endif
#if COMPILE_WITH_VK_SHADER_COMPILER
case ShaderProfile::Vulkan_SM5:
compiler = New<ShaderCompilerVulkan>(profile);
break;
#endif
#if COMPILE_WITH_PS4_SHADER_COMPILER
case ShaderProfile::PS4:
compiler = New<ShaderCompilerPS4>();
break;
#endif
#if COMPILE_WITH_PS5_SHADER_COMPILER
case ShaderProfile::PS5:
compiler = New<ShaderCompilerPS5>();
break;
#endif
default:
break;
}
if (compiler == nullptr) if (compiler == nullptr)
{ {
LOG(Error, "Cannot create Shader Compiler for profile {0}", ::ToString(profile)); LOG(Error, "Cannot create Shader Compiler for profile {0}", ::ToString(profile));
@@ -414,7 +413,8 @@ String ShadersCompilation::ResolveShaderPath(StringView path)
// Skip to the last root start './' but preserve the leading one // Skip to the last root start './' but preserve the leading one
for (int32 i = path.Length() - 2; i >= 2; i--) for (int32 i = path.Length() - 2; i >= 2; i--)
{ {
if (StringUtils::Compare(path.Get() + i, TEXT("./"), 2) == 0) const Char* pos = path.Get() + i;
if (pos[0] == '.' && pos[1] == '/')
{ {
path = path.Substring(i); path = path.Substring(i);
break; break;

View File

@@ -48,9 +48,7 @@ public:
static String CompactShaderPath(StringView path); static String CompactShaderPath(StringView path);
private: private:
static ShaderCompiler* RequestCompiler(ShaderProfile profile, PlatformType platform);
static ShaderCompiler* CreateCompiler(ShaderProfile profile);
static ShaderCompiler* RequestCompiler(ShaderProfile profile);
static void FreeCompiler(ShaderCompiler* compiler); static void FreeCompiler(ShaderCompiler* compiler);
}; };

View File

@@ -226,6 +226,27 @@ void Task::OnEnd()
{ {
ASSERT(!IsRunning()); ASSERT(!IsRunning());
// Add to delete if (_continueWith && !_continueWith->IsEnded())
DeleteObject(30.0f, false); {
// Let next task do the cleanup (to ensure whole tasks chain shares the lifetime)
_continueWith->_rootForRemoval = _rootForRemoval ? _rootForRemoval : this;
}
else
{
constexpr float timeToLive = 30.0f;
// Remove task chain starting from the root
if (_rootForRemoval)
{
auto task = _rootForRemoval;
while (task != this)
{
task->DeleteObject(timeToLive, false);
task = task->_continueWith;
}
}
// Add to delete
DeleteObject(timeToLive, false);
}
} }

View File

@@ -63,6 +63,11 @@ protected:
/// </summary> /// </summary>
Task* _continueWith = nullptr; Task* _continueWith = nullptr;
/// <summary>
/// The task that's starts removal chain, used to sync whole task chain lifetime.
/// </summary>
Task* _rootForRemoval = nullptr;
void SetState(TaskState state) void SetState(TaskState state)
{ {
Platform::AtomicStore((int64 volatile*)&_state, (uint64)state); Platform::AtomicStore((int64 volatile*)&_state, (uint64)state);

View File

@@ -43,32 +43,15 @@ void MaterialGenerator::AddLayer(MaterialLayer* layer)
MaterialLayer* MaterialGenerator::GetLayer(const Guid& id, Node* caller) MaterialLayer* MaterialGenerator::GetLayer(const Guid& id, Node* caller)
{ {
// Find layer first // Find layer first
for (int32 i = 0; i < _layers.Count(); i++) for (MaterialLayer* layer : _layers)
{ {
if (_layers[i]->ID == id) if (layer->ID == id)
{ return layer;
// Found
return _layers[i];
}
} }
// Load asset // Load asset
Asset* asset = Assets.LoadAsync<MaterialBase>(id); Asset* asset = Assets.Load<MaterialBase>(id);
if (asset == nullptr || asset->WaitForLoaded(30000)) if (asset == nullptr)
{
OnError(caller, nullptr, TEXT("Failed to load material asset."));
return nullptr;
}
// Special case for engine exit event
if (Engine::ShouldExit())
{
// End
return nullptr;
}
// Check if load failed
if (!asset->IsLoaded())
{ {
OnError(caller, nullptr, TEXT("Failed to load material asset.")); OnError(caller, nullptr, TEXT("Failed to load material asset."));
return nullptr; return nullptr;
@@ -79,13 +62,11 @@ MaterialLayer* MaterialGenerator::GetLayer(const Guid& id, Node* caller)
Asset* iterator = asset; Asset* iterator = asset;
while (material == nullptr) while (material == nullptr)
{ {
// Wait for material to be loaded
if (iterator->WaitForLoaded()) if (iterator->WaitForLoaded())
{ {
OnError(caller, nullptr, TEXT("Material asset load failed.")); OnError(caller, nullptr, TEXT("Material asset load failed."));
return nullptr; return nullptr;
} }
if (iterator->GetTypeName() == MaterialInstance::TypeName) if (iterator->GetTypeName() == MaterialInstance::TypeName)
{ {
auto instance = ((MaterialInstance*)iterator); auto instance = ((MaterialInstance*)iterator);

View File

@@ -15,12 +15,14 @@ void MaterialGenerator::ProcessGroupLayers(Box* box, Node* node, Value& value)
if (!id.IsValid()) if (!id.IsValid())
{ {
OnError(node, box, TEXT("Missing material.")); OnError(node, box, TEXT("Missing material."));
value = MaterialValue::InitForZero(VariantType::Void);
break; break;
} }
ASSERT(GetRootLayer() != nullptr && GetRootLayer()->ID.IsValid()); ASSERT(GetRootLayer() != nullptr && GetRootLayer()->ID.IsValid());
if (id == GetRootLayer()->ID) if (id == GetRootLayer()->ID)
{ {
OnError(node, box, TEXT("Cannot use current material as layer.")); OnError(node, box, TEXT("Cannot use current material as layer."));
value = MaterialValue::InitForZero(VariantType::Void);
break; break;
} }
@@ -29,6 +31,7 @@ void MaterialGenerator::ProcessGroupLayers(Box* box, Node* node, Value& value)
if (layer == nullptr) if (layer == nullptr)
{ {
OnError(node, box, TEXT("Cannot load material.")); OnError(node, box, TEXT("Cannot load material."));
value = MaterialValue::InitForZero(VariantType::Void);
break; break;
} }
ASSERT(_layers.Contains(layer)); ASSERT(_layers.Contains(layer));

View File

@@ -285,8 +285,8 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
case 24: case 24:
{ {
// Load function asset // Load function asset
const auto function = Assets.LoadAsync<MaterialFunction>((Guid)node->Values[0]); const auto function = Assets.Load<MaterialFunction>((Guid)node->Values[0]);
if (!function || function->WaitForLoaded()) if (!function)
{ {
OnError(node, box, TEXT("Missing or invalid function.")); OnError(node, box, TEXT("Missing or invalid function."));
value = Value::Zero; value = Value::Zero;
@@ -299,7 +299,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
{ {
if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(1, 24)) if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(1, 24))
{ {
const auto callFunc = Assets.LoadAsync<MaterialFunction>((Guid)_callStack[i]->Values[0]); const auto callFunc = Assets.Load<MaterialFunction>((Guid)_callStack[i]->Values[0]);
if (callFunc == function) if (callFunc == function)
{ {
OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString())); OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString()));
@@ -808,7 +808,7 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value)
value = Value::Zero; value = Value::Zero;
break; break;
} }
const auto function = Assets.LoadAsync<MaterialFunction>((Guid)functionCallNode->Values[0]); const auto function = Assets.Load<MaterialFunction>((Guid)functionCallNode->Values[0]);
if (!_functions.TryGet(functionCallNode, graph) || !function) if (!_functions.TryGet(functionCallNode, graph) || !function)
{ {
OnError(node, box, TEXT("Missing calling function graph.")); OnError(node, box, TEXT("Missing calling function graph."));

View File

@@ -3,31 +3,44 @@
#if COMPILE_WITH_MODEL_TOOL #if COMPILE_WITH_MODEL_TOOL
#include "MeshAccelerationStructure.h" #include "MeshAccelerationStructure.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Math.h" #include "Engine/Core/Math/Math.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h" #include "Engine/Content/Assets/Model.h"
#include "Engine/Graphics/GPUBuffer.h"
#include "Engine/Graphics/Models/ModelData.h" #include "Engine/Graphics/Models/ModelData.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
void MeshAccelerationStructure::BuildBVH(int32 node, int32 maxLeafSize, Array<byte>& scratch) PACK_STRUCT(struct GPUBVH {
Float3 BoundsMin;
uint32 Index;
Float3 BoundsMax;
int32 Count; // Negative for non-leaf nodes
});
static_assert(sizeof(GPUBVH) == sizeof(Float4) * 2, "Invalid BVH structure size for GPU.");
void MeshAccelerationStructure::BuildBVH(int32 node, BVHBuild& build)
{ {
auto& root = _bvh[node]; auto* root = &_bvh[node];
ASSERT_LOW_LAYER(root.Leaf.IsLeaf); ASSERT_LOW_LAYER(root->Leaf.IsLeaf);
if (root.Leaf.TriangleCount <= maxLeafSize) if (build.MaxLeafSize > 0 && root->Leaf.TriangleCount <= build.MaxLeafSize)
return;
if (build.MaxDepth > 0 && build.NodeDepth >= build.MaxDepth)
return; return;
// Spawn two leaves // Spawn two leaves
const int32 childIndex = _bvh.Count(); const int32 childIndex = _bvh.Count();
_bvh.AddDefault(2); _bvh.AddDefault(2);
root = &_bvh[node];
auto& left = _bvh.Get()[childIndex]; auto& left = _bvh.Get()[childIndex];
auto& right = _bvh.Get()[childIndex + 1]; auto& right = _bvh.Get()[childIndex + 1];
left.Leaf.IsLeaf = 1; left.Leaf.IsLeaf = 1;
right.Leaf.IsLeaf = 1; right.Leaf.IsLeaf = 1;
left.Leaf.MeshIndex = root.Leaf.MeshIndex; left.Leaf.MeshIndex = root->Leaf.MeshIndex;
right.Leaf.MeshIndex = root.Leaf.MeshIndex; right.Leaf.MeshIndex = root->Leaf.MeshIndex;
// Mid-point splitting based on the largest axis // Mid-point splitting based on the largest axis
const Float3 boundsSize = root.Bounds.GetSize(); const Float3 boundsSize = root->Bounds.GetSize();
int32 axisCount = 0; int32 axisCount = 0;
int32 axis = 0; int32 axis = 0;
RETRY: RETRY:
@@ -51,11 +64,11 @@ RETRY:
// Go to the next axis // Go to the next axis
axis = (axis + 1) % 3; axis = (axis + 1) % 3;
} }
const float midPoint = (float)(root.Bounds.Minimum.Raw[axis] + boundsSize.Raw[axis] * 0.5f); const float midPoint = (float)(root->Bounds.Minimum.Raw[axis] + boundsSize.Raw[axis] * 0.5f);
const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; const Mesh& meshData = _meshes[root->Leaf.MeshIndex];
const Float3* vb = meshData.VertexBuffer.Get<Float3>(); const Float3* vb = meshData.VertexBuffer.Get<Float3>();
int32 indexStart = root.Leaf.TriangleIndex * 3; int32 indexStart = root->Leaf.TriangleIndex * 3;
int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; int32 indexEnd = indexStart + root->Leaf.TriangleCount * 3;
left.Leaf.TriangleCount = 0; left.Leaf.TriangleCount = 0;
right.Leaf.TriangleCount = 0; right.Leaf.TriangleCount = 0;
if (meshData.Use16BitIndexBuffer) if (meshData.Use16BitIndexBuffer)
@@ -64,8 +77,8 @@ RETRY:
{ {
uint16 I0, I1, I2; uint16 I0, I1, I2;
}; };
scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); build.Scratch.Resize(root->Leaf.TriangleCount * sizeof(Tri));
auto dst = (Tri*)scratch.Get(); auto dst = (Tri*)build.Scratch.Get();
auto ib16 = meshData.IndexBuffer.Get<uint16>(); auto ib16 = meshData.IndexBuffer.Get<uint16>();
for (int32 i = indexStart; i < indexEnd;) for (int32 i = indexStart; i < indexEnd;)
{ {
@@ -77,9 +90,9 @@ RETRY:
if (centroid <= midPoint) if (centroid <= midPoint)
dst[left.Leaf.TriangleCount++] = tri; // Left dst[left.Leaf.TriangleCount++] = tri; // Left
else else
dst[root.Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right dst[root->Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right
} }
Platform::MemoryCopy(ib16 + indexStart, dst, root.Leaf.TriangleCount * 3 * sizeof(uint16)); Platform::MemoryCopy(ib16 + indexStart, dst, root->Leaf.TriangleCount * 3 * sizeof(uint16));
if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0) if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0)
{ {
axisCount++; axisCount++;
@@ -90,13 +103,13 @@ RETRY:
indexStart = 0; indexStart = 0;
indexEnd = left.Leaf.TriangleCount * 3; indexEnd = left.Leaf.TriangleCount * 3;
for (int32 i = indexStart; i < indexEnd; i++) for (int32 i = indexStart; i < indexEnd; i++)
left.Bounds.Merge(vb[((uint16*)scratch.Get())[i]]); left.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]);
right.Bounds = BoundingBox(vb[dst[root.Leaf.TriangleCount - 1].I0]); right.Bounds = BoundingBox(vb[dst[root->Leaf.TriangleCount - 1].I0]);
indexStart = left.Leaf.TriangleCount; indexStart = left.Leaf.TriangleCount;
indexEnd = root.Leaf.TriangleCount * 3; indexEnd = root->Leaf.TriangleCount * 3;
for (int32 i = indexStart; i < indexEnd; i++) for (int32 i = indexStart; i < indexEnd; i++)
right.Bounds.Merge(vb[((uint16*)scratch.Get())[i]]); right.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]);
} }
else else
{ {
@@ -104,8 +117,8 @@ RETRY:
{ {
uint32 I0, I1, I2; uint32 I0, I1, I2;
}; };
scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); build.Scratch.Resize(root->Leaf.TriangleCount * sizeof(Tri));
auto dst = (Tri*)scratch.Get(); auto dst = (Tri*)build.Scratch.Get();
auto ib32 = meshData.IndexBuffer.Get<uint32>(); auto ib32 = meshData.IndexBuffer.Get<uint32>();
for (int32 i = indexStart; i < indexEnd;) for (int32 i = indexStart; i < indexEnd;)
{ {
@@ -117,9 +130,9 @@ RETRY:
if (centroid <= midPoint) if (centroid <= midPoint)
dst[left.Leaf.TriangleCount++] = tri; // Left dst[left.Leaf.TriangleCount++] = tri; // Left
else else
dst[root.Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right dst[root->Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right
} }
Platform::MemoryCopy(ib32 + indexStart, dst, root.Leaf.TriangleCount * 3 * sizeof(uint32)); Platform::MemoryCopy(ib32 + indexStart, dst, root->Leaf.TriangleCount * 3 * sizeof(uint32));
if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0) if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0)
{ {
axisCount++; axisCount++;
@@ -130,26 +143,31 @@ RETRY:
indexStart = 0; indexStart = 0;
indexEnd = left.Leaf.TriangleCount * 3; indexEnd = left.Leaf.TriangleCount * 3;
for (int32 i = indexStart; i < indexEnd; i++) for (int32 i = indexStart; i < indexEnd; i++)
left.Bounds.Merge(vb[((uint32*)scratch.Get())[i]]); left.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]);
right.Bounds = BoundingBox(vb[dst[root.Leaf.TriangleCount - 1].I0]); right.Bounds = BoundingBox(vb[dst[root->Leaf.TriangleCount - 1].I0]);
indexStart = left.Leaf.TriangleCount; indexStart = left.Leaf.TriangleCount;
indexEnd = root.Leaf.TriangleCount * 3; indexEnd = root->Leaf.TriangleCount * 3;
for (int32 i = indexStart; i < indexEnd; i++) for (int32 i = indexStart; i < indexEnd; i++)
right.Bounds.Merge(vb[((uint32*)scratch.Get())[i]]); right.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]);
} }
ASSERT_LOW_LAYER(left.Leaf.TriangleCount + right.Leaf.TriangleCount == root.Leaf.TriangleCount); ASSERT_LOW_LAYER(left.Leaf.TriangleCount + right.Leaf.TriangleCount == root->Leaf.TriangleCount);
left.Leaf.TriangleIndex = root.Leaf.TriangleIndex; left.Leaf.TriangleIndex = root->Leaf.TriangleIndex;
right.Leaf.TriangleIndex = left.Leaf.TriangleIndex + left.Leaf.TriangleCount; right.Leaf.TriangleIndex = left.Leaf.TriangleIndex + left.Leaf.TriangleCount;
build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount);
build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount);
// Convert into a node // Convert into a node
root.Node.IsLeaf = 0; root->Node.IsLeaf = 0;
root.Node.ChildIndex = childIndex; root->Node.ChildIndex = childIndex;
root.Node.ChildrenCount = 2; root->Node.ChildrenCount = 2;
// Split children // Split children
BuildBVH(childIndex, maxLeafSize, scratch); build.NodeDepth++;
BuildBVH(childIndex + 1, maxLeafSize, scratch); build.MaxNodeDepth = Math::Max(build.NodeDepth, build.MaxNodeDepth);
BuildBVH(childIndex, build);
BuildBVH(childIndex + 1, build);
build.NodeDepth--;
} }
bool MeshAccelerationStructure::PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const bool MeshAccelerationStructure::PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const
@@ -160,7 +178,7 @@ bool MeshAccelerationStructure::PointQueryBVH(int32 node, const Vector3& point,
{ {
// Find closest triangle // Find closest triangle
Vector3 p; Vector3 p;
const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; const Mesh& meshData = _meshes.Get()[root.Leaf.MeshIndex];
const Float3* vb = meshData.VertexBuffer.Get<Float3>(); const Float3* vb = meshData.VertexBuffer.Get<Float3>();
const int32 indexStart = root.Leaf.TriangleIndex * 3; const int32 indexStart = root.Leaf.TriangleIndex * 3;
const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3;
@@ -229,7 +247,7 @@ bool MeshAccelerationStructure::RayCastBVH(int32 node, const Ray& ray, Real& hit
if (root.Leaf.IsLeaf) if (root.Leaf.IsLeaf)
{ {
// Ray cast along triangles in the leaf // Ray cast along triangles in the leaf
const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; const Mesh& meshData = _meshes.Get()[root.Leaf.MeshIndex];
const Float3* vb = meshData.VertexBuffer.Get<Float3>(); const Float3* vb = meshData.VertexBuffer.Get<Float3>();
const int32 indexStart = root.Leaf.TriangleIndex * 3; const int32 indexStart = root.Leaf.TriangleIndex * 3;
const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3;
@@ -288,6 +306,15 @@ bool MeshAccelerationStructure::RayCastBVH(int32 node, const Ray& ray, Real& hit
return hit; return hit;
} }
MeshAccelerationStructure::~MeshAccelerationStructure()
{
for (auto& e : _meshes)
{
if (e.Asset)
e.Asset->RemoveReference();
}
}
void MeshAccelerationStructure::Add(Model* model, int32 lodIndex) void MeshAccelerationStructure::Add(Model* model, int32 lodIndex)
{ {
PROFILE_CPU(); PROFILE_CPU();
@@ -307,6 +334,8 @@ void MeshAccelerationStructure::Add(Model* model, int32 lodIndex)
} }
auto& meshData = _meshes.AddOne(); auto& meshData = _meshes.AddOne();
meshData.Asset = model;
model->AddReference();
if (model->IsVirtual()) if (model->IsVirtual())
{ {
meshData.Indices = mesh.GetTriangleCount() * 3; meshData.Indices = mesh.GetTriangleCount() * 3;
@@ -350,6 +379,7 @@ void MeshAccelerationStructure::Add(const ModelData* modelData, int32 lodIndex,
} }
auto& meshData = _meshes.AddOne(); auto& meshData = _meshes.AddOne();
meshData.Asset = nullptr;
meshData.Indices = mesh->Indices.Count(); meshData.Indices = mesh->Indices.Count();
meshData.Vertices = mesh->Positions.Count(); meshData.Vertices = mesh->Positions.Count();
if (copy) if (copy)
@@ -369,7 +399,9 @@ void MeshAccelerationStructure::Add(const ModelData* modelData, int32 lodIndex,
void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy) void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy)
{ {
ASSERT(vertices % 3 == 0);
auto& meshData = _meshes.AddOne(); auto& meshData = _meshes.AddOne();
meshData.Asset = nullptr;
if (copy) if (copy)
{ {
meshData.VertexBuffer.Copy((const byte*)vb, vertices * sizeof(Float3)); meshData.VertexBuffer.Copy((const byte*)vb, vertices * sizeof(Float3));
@@ -382,43 +414,122 @@ void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32
meshData.Vertices = vertices; meshData.Vertices = vertices;
meshData.Indices = indices; meshData.Indices = indices;
meshData.Use16BitIndexBuffer = use16BitIndex; meshData.Use16BitIndexBuffer = use16BitIndex;
BoundingBox::FromPoints(meshData.VertexBuffer.Get<Float3>(), vertices, meshData.Bounds);
} }
void MeshAccelerationStructure::BuildBVH(int32 maxLeafSize) void MeshAccelerationStructure::MergeMeshes(bool force16BitIndexBuffer)
{
if (_meshes.Count() == 0)
return;
if (_meshes.Count() == 1 && (!force16BitIndexBuffer || !_meshes[0].Use16BitIndexBuffer))
return;
PROFILE_CPU();
auto meshes = _meshes;
_meshes.Clear();
_meshes.Resize(1);
auto& mesh = _meshes[0];
mesh.Asset = nullptr;
mesh.Use16BitIndexBuffer = true;
mesh.Indices = 0;
mesh.Vertices = 0;
mesh.Bounds = meshes[0].Bounds;
for (auto& e : meshes)
{
if (!e.Use16BitIndexBuffer)
mesh.Use16BitIndexBuffer = false;
mesh.Vertices += e.Vertices;
mesh.Indices += e.Indices;
BoundingBox::Merge(mesh.Bounds, e.Bounds, mesh.Bounds);
}
mesh.Use16BitIndexBuffer &= mesh.Indices <= MAX_uint16 && !force16BitIndexBuffer;
mesh.VertexBuffer.Allocate(mesh.Vertices * sizeof(Float3));
mesh.IndexBuffer.Allocate(mesh.Indices * sizeof(uint32));
int32 vertexCounter = 0, indexCounter = 0;
for (auto& e : meshes)
{
Platform::MemoryCopy(mesh.VertexBuffer.Get() + vertexCounter * sizeof(Float3), e.VertexBuffer.Get(), e.Vertices * sizeof(Float3));
if (e.Use16BitIndexBuffer)
{
for (int32 i = 0; i < e.Indices; i++)
{
uint16 index = ((uint16*)e.IndexBuffer.Get())[i];
((uint32*)mesh.IndexBuffer.Get())[indexCounter + i] = vertexCounter + index;
}
}
else
{
for (int32 i = 0; i < e.Indices; i++)
{
uint16 index = ((uint32*)e.IndexBuffer.Get())[i];
((uint32*)mesh.IndexBuffer.Get())[indexCounter + i] = vertexCounter + index;
}
}
vertexCounter += e.Vertices;
indexCounter += e.Indices;
if (e.Asset)
e.Asset->RemoveReference();
}
}
void MeshAccelerationStructure::BuildBVH(int32 maxLeafSize, int32 maxDepth)
{ {
if (_meshes.Count() == 0) if (_meshes.Count() == 0)
return; return;
PROFILE_CPU(); PROFILE_CPU();
BVHBuild build;
build.MaxLeafSize = maxLeafSize;
build.MaxDepth = maxDepth;
// Estimate memory usage // Estimate memory usage
int32 trianglesCount = 0; int32 trianglesCount = 0;
for (const Mesh& meshData : _meshes) for (const Mesh& meshData : _meshes)
trianglesCount += meshData.Indices / 3; trianglesCount += meshData.Indices / 3;
_bvh.Clear(); _bvh.Clear();
_bvh.EnsureCapacity(trianglesCount / maxLeafSize); _bvh.EnsureCapacity(trianglesCount / Math::Max(maxLeafSize, 16));
// Init with the root node and all meshes as leaves // Skip using root node if BVH contains only one mesh
auto& root = _bvh.AddOne(); if (_meshes.Count() == 1)
root.Node.IsLeaf = 0;
root.Node.ChildIndex = 1;
root.Node.ChildrenCount = _meshes.Count();
root.Bounds = _meshes[0].Bounds;
for (int32 i = 0; i < _meshes.Count(); i++)
{ {
const Mesh& meshData = _meshes[i]; const Mesh& meshData = _meshes.First();
auto& child = _bvh.AddOne(); auto& child = _bvh.AddOne();
child.Leaf.IsLeaf = 1; child.Leaf.IsLeaf = 1;
child.Leaf.MeshIndex = i; child.Leaf.MeshIndex = 0;
child.Leaf.TriangleIndex = 0; child.Leaf.TriangleIndex = 0;
child.Leaf.TriangleCount = meshData.Indices / 3; child.Leaf.TriangleCount = meshData.Indices / 3;
child.Bounds = meshData.Bounds; child.Bounds = meshData.Bounds;
BoundingBox::Merge(root.Bounds, meshData.Bounds, root.Bounds); Array<byte> scratch;
BuildBVH(0, build);
}
else
{
// Init with the root node and all meshes as leaves
auto& root = _bvh.AddOne();
root.Node.IsLeaf = 0;
root.Node.ChildIndex = 1;
root.Node.ChildrenCount = _meshes.Count();
root.Bounds = _meshes[0].Bounds;
for (int32 i = 0; i < _meshes.Count(); i++)
{
const Mesh& meshData = _meshes[i];
auto& child = _bvh.AddOne();
child.Leaf.IsLeaf = 1;
child.Leaf.MeshIndex = i;
child.Leaf.TriangleIndex = 0;
child.Leaf.TriangleCount = meshData.Indices / 3;
child.Bounds = meshData.Bounds;
BoundingBox::Merge(root.Bounds, meshData.Bounds, root.Bounds);
}
// Sub-divide mesh nodes into smaller leaves
build.MaxNodeDepth = build.MaxDepth = 2;
Array<byte> scratch;
for (int32 i = 0; i < _meshes.Count(); i++)
BuildBVH(i + 1, build);
build.NodeDepth = 0;
} }
// Sub-divide mesh nodes into smaller leaves LOG(Info, "BVH nodes: {}, max depth: {}, max triangles: {}", _bvh.Count(), build.MaxNodeDepth, build.MaxNodeTriangles);
Array<byte> scratch;
for (int32 i = 0; i < _meshes.Count(); i++)
BuildBVH(i + 1, maxLeafSize, scratch);
} }
bool MeshAccelerationStructure::PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance) const bool MeshAccelerationStructure::PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance) const
@@ -566,4 +677,80 @@ bool MeshAccelerationStructure::RayCast(const Ray& ray, Real& hitDistance, Vecto
} }
} }
MeshAccelerationStructure::GPU::~GPU()
{
SAFE_DELETE_GPU_RESOURCE(BVHBuffer);
SAFE_DELETE_GPU_RESOURCE(VertexBuffer);
SAFE_DELETE_GPU_RESOURCE(IndexBuffer);
}
MeshAccelerationStructure::GPU::operator bool() const
{
// Index buffer is initialized as last one so all other buffers are fine too
return IndexBuffer && IndexBuffer->GetSize() != 0;
}
MeshAccelerationStructure::GPU MeshAccelerationStructure::ToGPU()
{
PROFILE_CPU();
GPU gpu;
// GPU BVH operates on a single mesh with 32-bit indices
MergeMeshes(true);
// Construct BVH
const int32 BVH_STACK_SIZE = 32; // This must match HLSL shader
BuildBVH(0, BVH_STACK_SIZE);
// Upload BVH
{
Array<GPUBVH> bvhData;
bvhData.Resize(_bvh.Count());
for (int32 i = 0; i < _bvh.Count(); i++)
{
const auto& src = _bvh.Get()[i];
auto& dst = bvhData.Get()[i];
dst.BoundsMin = src.Bounds.Minimum;
dst.BoundsMax = src.Bounds.Maximum;
if (src.Leaf.IsLeaf)
{
dst.Index = src.Leaf.TriangleIndex * 3;
dst.Count = src.Leaf.TriangleCount * 3;
}
else
{
dst.Index = src.Node.ChildIndex;
dst.Count = -(int32)src.Node.ChildrenCount; // Mark as non-leaf
ASSERT(src.Node.ChildrenCount == 2); // GPU shader is hardcoded for 2 children per node
}
}
gpu.BVHBuffer = GPUBuffer::New();
auto desc =GPUBufferDescription::Structured(_bvh.Count(), sizeof(GPUBVH));
desc.InitData = bvhData.Get();
if (gpu.BVHBuffer->Init(desc))
return gpu;
}
// Upload vertex buffer
{
const Mesh& mesh = _meshes[0];
gpu.VertexBuffer = GPUBuffer::New();
auto desc = GPUBufferDescription::Raw(mesh.Vertices * sizeof(Float3), GPUBufferFlags::ShaderResource);
desc.InitData = mesh.VertexBuffer.Get();
if (gpu.VertexBuffer->Init(desc))
return gpu;
}
// Upload index buffer
{
const Mesh& mesh = _meshes[0];
gpu.IndexBuffer = GPUBuffer::New();
auto desc = GPUBufferDescription::Raw(mesh.Indices * sizeof(uint32), GPUBufferFlags::ShaderResource);
desc.InitData = mesh.IndexBuffer.Get();
gpu.IndexBuffer->Init(desc);
}
return gpu;
}
#endif #endif

View File

@@ -11,6 +11,7 @@
class Model; class Model;
class ModelData; class ModelData;
class GPUBuffer;
/// <summary> /// <summary>
/// Acceleration Structure utility for robust ray tracing mesh geometry with optimized data structure. /// Acceleration Structure utility for robust ray tracing mesh geometry with optimized data structure.
@@ -20,6 +21,7 @@ class FLAXENGINE_API MeshAccelerationStructure
private: private:
struct Mesh struct Mesh
{ {
Model* Asset;
BytesContainer IndexBuffer, VertexBuffer; BytesContainer IndexBuffer, VertexBuffer;
int32 Indices, Vertices; int32 Indices, Vertices;
bool Use16BitIndexBuffer; bool Use16BitIndexBuffer;
@@ -49,14 +51,25 @@ private:
}; };
}; };
struct BVHBuild
{
int32 MaxLeafSize, MaxDepth;
int32 NodeDepth = 0;
int32 MaxNodeDepth = 0;
int32 MaxNodeTriangles = 0;
Array<byte> Scratch;
};
Array<Mesh, InlinedAllocation<16>> _meshes; Array<Mesh, InlinedAllocation<16>> _meshes;
Array<BVH> _bvh; Array<BVH> _bvh;
void BuildBVH(int32 node, int32 maxLeafSize, Array<byte>& scratch); void BuildBVH(int32 node, BVHBuild& build);
bool PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const; bool PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const;
bool RayCastBVH(int32 node, const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle) const; bool RayCastBVH(int32 node, const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle) const;
public: public:
~MeshAccelerationStructure();
// Adds the model geometry for the build to the structure. // Adds the model geometry for the build to the structure.
void Add(Model* model, int32 lodIndex); void Add(Model* model, int32 lodIndex);
@@ -66,14 +79,56 @@ public:
// Adds the triangles geometry for the build to the structure. // Adds the triangles geometry for the build to the structure.
void Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy = false); void Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy = false);
// Merges all added meshes into a single mesh (to reduce number of BVH nodes). Required for GPU BVH build.
void MergeMeshes(bool force16BitIndexBuffer = false);
// Builds Bounding Volume Hierarchy (BVH) structure for accelerated geometry queries. // Builds Bounding Volume Hierarchy (BVH) structure for accelerated geometry queries.
void BuildBVH(int32 maxLeafSize = 16); void BuildBVH(int32 maxLeafSize = 16, int32 maxDepth = 0);
// Queries the closest triangle. // Queries the closest triangle.
bool PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance = MAX_Real) const; bool PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance = MAX_Real) const;
// Ray traces the triangles. // Ray traces the triangles.
bool RayCast(const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle, Real maxDistance = MAX_Real) const; bool RayCast(const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle, Real maxDistance = MAX_Real) const;
public:
struct GPU
{
GPUBuffer* BVHBuffer;
GPUBuffer* VertexBuffer;
GPUBuffer* IndexBuffer;
bool Valid;
GPU()
: BVHBuffer(nullptr)
, VertexBuffer(nullptr)
, IndexBuffer(nullptr)
{
}
GPU(GPU&& other) noexcept
: BVHBuffer(other.BVHBuffer)
, VertexBuffer(other.VertexBuffer)
, IndexBuffer(other.IndexBuffer)
{
other.BVHBuffer = nullptr;
other.VertexBuffer = nullptr;
other.IndexBuffer = nullptr;
}
GPU& operator=(GPU other)
{
Swap(*this, other);
return *this;
}
~GPU();
operator bool() const;
};
// Converts the acceleration structure data to GPU format for raytracing inside a shader.
GPU ToGPU();
}; };
#endif #endif

View File

@@ -8,12 +8,14 @@
#include "Engine/Core/RandomStream.h" #include "Engine/Core/RandomStream.h"
#include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Ray.h" #include "Engine/Core/Math/Ray.h"
#include "Engine/Core/Utilities.h"
#include "Engine/Platform/ConditionVariable.h" #include "Engine/Platform/ConditionVariable.h"
#include "Engine/Profiler/Profiler.h" #include "Engine/Profiler/Profiler.h"
#include "Engine/Threading/JobSystem.h" #include "Engine/Threading/JobSystem.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/GPUBuffer.h" #include "Engine/Graphics/GPUBuffer.h"
#include "Engine/Graphics/GPUTimerQuery.h"
#include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/Async/GPUTask.h" #include "Engine/Graphics/Async/GPUTask.h"
#include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Shaders/GPUShader.h"
@@ -26,7 +28,6 @@
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Engine/Units.h" #include "Engine/Engine/Units.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Core/Utilities.h"
#include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/DateTime.h"
#include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Types/TimeSpan.h"
@@ -81,14 +82,18 @@ class GPUModelSDFTask : public GPUTask
{ {
ConditionVariable* _signal; ConditionVariable* _signal;
AssetReference<Shader> _shader; AssetReference<Shader> _shader;
MeshAccelerationStructure* _scene;
Model* _inputModel; Model* _inputModel;
const ModelData* _modelData; const ModelData* _modelData;
int32 _lodIndex; int32 _lodIndex;
float _backfacesThreshold;
Int3 _resolution; Int3 _resolution;
ModelBase::SDFData* _sdf; ModelBase::SDFData* _sdf;
GPUBuffer *_sdfSrc, *_sdfDst;
GPUTexture* _sdfResult; GPUTexture* _sdfResult;
Float3 _xyzToLocalMul, _xyzToLocalAdd; Float3 _xyzToLocalMul, _xyzToLocalAdd;
#if GPU_ALLOW_PROFILE_EVENTS
GPUTimerQuery* _timerQuery;
#endif
const uint32 ThreadGroupSize = 64; const uint32 ThreadGroupSize = 64;
GPU_CB_STRUCT(Data { GPU_CB_STRUCT(Data {
@@ -96,7 +101,7 @@ class GPUModelSDFTask : public GPUTask
uint32 ResolutionSize; uint32 ResolutionSize;
float MaxDistance; float MaxDistance;
uint32 VertexStride; uint32 VertexStride;
int32 Index16bit; float BackfacesThreshold;
uint32 TriangleCount; uint32 TriangleCount;
Float3 VoxelToPosMul; Float3 VoxelToPosMul;
float WorldUnitsPerVoxel; float WorldUnitsPerVoxel;
@@ -105,47 +110,46 @@ class GPUModelSDFTask : public GPUTask
}); });
public: public:
GPUModelSDFTask(ConditionVariable& signal, Model* inputModel, const ModelData* modelData, int32 lodIndex, const Int3& resolution, ModelBase::SDFData* sdf, GPUTexture* sdfResult, const Float3& xyzToLocalMul, const Float3& xyzToLocalAdd) GPUModelSDFTask(ConditionVariable& signal, MeshAccelerationStructure* scene, Model* inputModel, const ModelData* modelData, int32 lodIndex, const Int3& resolution, ModelBase::SDFData* sdf, GPUTexture* sdfResult, const Float3& xyzToLocalMul, const Float3& xyzToLocalAdd, float backfacesThreshold)
: GPUTask(Type::Custom) : GPUTask(Type::Custom, GPU_ALLOW_PROFILE_EVENTS ? 4 : GPU_ASYNC_LATENCY) // Fix timer query result reading with some more latency
, _signal(&signal) , _signal(&signal)
, _shader(Content::LoadAsyncInternal<Shader>(TEXT("Shaders/SDF"))) , _shader(Content::LoadAsyncInternal<Shader>(TEXT("Shaders/SDF")))
, _scene(scene)
, _inputModel(inputModel) , _inputModel(inputModel)
, _modelData(modelData) , _modelData(modelData)
, _lodIndex(lodIndex) , _lodIndex(lodIndex)
, _backfacesThreshold(backfacesThreshold)
, _resolution(resolution) , _resolution(resolution)
, _sdf(sdf) , _sdf(sdf)
, _sdfSrc(GPUBuffer::New())
, _sdfDst(GPUBuffer::New())
, _sdfResult(sdfResult) , _sdfResult(sdfResult)
, _xyzToLocalMul(xyzToLocalMul) , _xyzToLocalMul(xyzToLocalMul)
, _xyzToLocalAdd(xyzToLocalAdd) , _xyzToLocalAdd(xyzToLocalAdd)
{ #if GPU_ALLOW_PROFILE_EVENTS
#if GPU_ENABLE_RESOURCE_NAMING , _timerQuery(GPUDevice::Instance->CreateTimerQuery())
_sdfSrc->SetName(TEXT("SDFSrc"));
_sdfDst->SetName(TEXT("SDFDst"));
#endif #endif
{
} }
~GPUModelSDFTask() ~GPUModelSDFTask()
{ {
SAFE_DELETE_GPU_RESOURCE(_sdfSrc); #if GPU_ALLOW_PROFILE_EVENTS
SAFE_DELETE_GPU_RESOURCE(_sdfDst); SAFE_DELETE_GPU_RESOURCE(_timerQuery);
#endif
} }
Result run(GPUTasksContext* tasksContext) override Result run(GPUTasksContext* tasksContext) override
{ {
PROFILE_GPU_CPU("GPUModelSDFTask"); PROFILE_GPU_CPU("GPUModelSDFTask");
GPUContext* context = tasksContext->GPU; GPUContext* context = tasksContext->GPU;
#if GPU_ALLOW_PROFILE_EVENTS
_timerQuery->Begin();
#endif
// Allocate resources // Allocate resources
if (_shader == nullptr || _shader->WaitForLoaded()) if (_shader == nullptr || _shader->WaitForLoaded())
return Result::Failed; return Result::Failed;
GPUShader* shader = _shader->GetShader(); GPUShader* shader = _shader->GetShader();
const uint32 resolutionSize = _resolution.X * _resolution.Y * _resolution.Z; const uint32 resolutionSize = _resolution.X * _resolution.Y * _resolution.Z;
auto desc = GPUBufferDescription::Typed(resolutionSize, PixelFormat::R32_UInt, true);
// TODO: use transient texture (single frame)
if (_sdfSrc->Init(desc) || _sdfDst->Init(desc))
return Result::Failed;
auto cb = shader->GetCB(0); auto cb = shader->GetCB(0);
Data data; Data data;
data.Resolution = _resolution; data.Resolution = _resolution;
@@ -154,6 +158,13 @@ public:
data.WorldUnitsPerVoxel = _sdf->WorldUnitsPerVoxel; data.WorldUnitsPerVoxel = _sdf->WorldUnitsPerVoxel;
data.VoxelToPosMul = _xyzToLocalMul; data.VoxelToPosMul = _xyzToLocalMul;
data.VoxelToPosAdd = _xyzToLocalAdd; data.VoxelToPosAdd = _xyzToLocalAdd;
data.BackfacesThreshold = _backfacesThreshold - 0.05f; // Bias a bit
// Send BVH to the GPU
auto bvh = _scene->ToGPU();
CHECK_RETURN(bvh.BVHBuffer && bvh.VertexBuffer && bvh.IndexBuffer, Result::Failed);
data.VertexStride = sizeof(Float3);
data.TriangleCount = bvh.IndexBuffer->GetElementsCount() / 3;
// Dispatch in 1D and fallback to 2D when using large resolution // Dispatch in 1D and fallback to 2D when using large resolution
Int3 threadGroups(Math::CeilToInt((float)resolutionSize / ThreadGroupSize), 1, 1); Int3 threadGroups(Math::CeilToInt((float)resolutionSize / ThreadGroupSize), 1, 1);
@@ -165,159 +176,34 @@ public:
} }
data.ThreadGroupsX = threadGroups.X; data.ThreadGroupsX = threadGroups.X;
// Init SDF volume // Init constants
context->BindCB(0, cb); context->BindCB(0, cb);
context->UpdateCB(cb, &data); context->UpdateCB(cb, &data);
context->BindUA(0, _sdfSrc->View());
context->Dispatch(shader->GetCS("CS_Init"), threadGroups.X, threadGroups.Y, threadGroups.Z);
// Rendering input triangles into the SDF volume // Allocate output texture
if (_inputModel) auto sdfTextureDesc = GPUTextureDescription::New3D(_resolution.X, _resolution.Y, _resolution.Z, PixelFormat::R16_UNorm, GPUTextureFlags::UnorderedAccess);
{
PROFILE_GPU_CPU_NAMED("Rasterize");
const ModelLOD& lod = _inputModel->LODs[Math::Clamp(_lodIndex, _inputModel->HighestResidentLODIndex(), _inputModel->LODs.Count() - 1)];
GPUBuffer *vbTemp = nullptr, *ibTemp = nullptr;
for (int32 i = 0; i < lod.Meshes.Count(); i++)
{
const Mesh& mesh = lod.Meshes[i];
const MaterialSlot& materialSlot = _inputModel->MaterialSlots[mesh.GetMaterialSlotIndex()];
if (materialSlot.Material && !materialSlot.Material->WaitForLoaded())
{
// Skip transparent materials
if (materialSlot.Material->GetInfo().BlendMode != MaterialBlendMode::Opaque)
continue;
}
GPUBuffer* vb = mesh.GetVertexBuffer(0);
GPUBuffer* ib = mesh.GetIndexBuffer();
data.Index16bit = mesh.Use16BitIndexBuffer() ? 1 : 0;
data.VertexStride = vb->GetStride();
data.TriangleCount = mesh.GetTriangleCount();
const uint32 groups = Math::CeilToInt((float)data.TriangleCount / ThreadGroupSize);
if (groups > GPU_MAX_CS_DISPATCH_THREAD_GROUPS)
{
// TODO: support larger meshes via 2D dispatch
LOG(Error, "Not supported mesh with {} triangles.", data.TriangleCount);
continue;
}
context->UpdateCB(cb, &data);
if (!EnumHasAllFlags(vb->GetDescription().Flags, GPUBufferFlags::RawBuffer | GPUBufferFlags::ShaderResource))
{
desc = GPUBufferDescription::Raw(vb->GetSize(), GPUBufferFlags::ShaderResource);
// TODO: use transient buffer (single frame)
if (!vbTemp)
{
vbTemp = GPUBuffer::New();
#if GPU_ENABLE_RESOURCE_NAMING
vbTemp->SetName(TEXT("SDFvb"));
#endif
}
vbTemp->Init(desc);
context->CopyBuffer(vbTemp, vb, desc.Size);
vb = vbTemp;
}
if (!EnumHasAllFlags(ib->GetDescription().Flags, GPUBufferFlags::RawBuffer | GPUBufferFlags::ShaderResource))
{
desc = GPUBufferDescription::Raw(ib->GetSize(), GPUBufferFlags::ShaderResource);
// TODO: use transient buffer (single frame)
if (!ibTemp)
{
ibTemp = GPUBuffer::New();
#if GPU_ENABLE_RESOURCE_NAMING
ibTemp->SetName(TEXT("SDFib"));
#endif
}
ibTemp->Init(desc);
context->CopyBuffer(ibTemp, ib, desc.Size);
ib = ibTemp;
}
context->BindSR(0, vb->View());
context->BindSR(1, ib->View());
context->Dispatch(shader->GetCS("CS_RasterizeTriangle"), groups, 1, 1);
}
SAFE_DELETE_GPU_RESOURCE(vbTemp);
SAFE_DELETE_GPU_RESOURCE(ibTemp);
}
else if (_modelData)
{
PROFILE_GPU_CPU_NAMED("Rasterize");
const ModelLodData& lod = _modelData->LODs[Math::Clamp(_lodIndex, 0, _modelData->LODs.Count() - 1)];
auto vb = GPUBuffer::New();
auto ib = GPUBuffer::New();
#if GPU_ENABLE_RESOURCE_NAMING
vb->SetName(TEXT("SDFvb"));
ib->SetName(TEXT("SDFib"));
#endif
for (int32 i = 0; i < lod.Meshes.Count(); i++)
{
const MeshData* mesh = lod.Meshes[i];
const MaterialSlotEntry& materialSlot = _modelData->Materials[mesh->MaterialSlotIndex];
auto material = Content::LoadAsync<MaterialBase>(materialSlot.AssetID);
if (material && !material->WaitForLoaded())
{
// Skip transparent materials
if (material->GetInfo().BlendMode != MaterialBlendMode::Opaque)
continue;
}
data.Index16bit = 0;
data.VertexStride = sizeof(Float3);
data.TriangleCount = mesh->Indices.Count() / 3;
const uint32 groups = Math::CeilToInt((float)data.TriangleCount / ThreadGroupSize);
if (groups > GPU_MAX_CS_DISPATCH_THREAD_GROUPS)
{
// TODO: support larger meshes via 2D dispatch
LOG(Error, "Not supported mesh with {} triangles.", data.TriangleCount);
continue;
}
context->UpdateCB(cb, &data);
desc = GPUBufferDescription::Raw(mesh->Positions.Count() * sizeof(Float3), GPUBufferFlags::ShaderResource);
desc.InitData = mesh->Positions.Get();
// TODO: use transient buffer (single frame)
vb->Init(desc);
desc = GPUBufferDescription::Raw(mesh->Indices.Count() * sizeof(uint32), GPUBufferFlags::ShaderResource);
desc.InitData = mesh->Indices.Get();
// TODO: use transient buffer (single frame)
ib->Init(desc);
context->BindSR(0, vb->View());
context->BindSR(1, ib->View());
context->Dispatch(shader->GetCS("CS_RasterizeTriangle"), groups, 1, 1);
}
SAFE_DELETE_GPU_RESOURCE(vb);
SAFE_DELETE_GPU_RESOURCE(ib);
}
// Convert SDF volume data back to floats
context->Dispatch(shader->GetCS("CS_Resolve"), threadGroups.X, threadGroups.Y, threadGroups.Z);
// Run linear flood-fill loop to populate all voxels with valid distances (spreads the initial values from triangles rasterization)
{
PROFILE_GPU_CPU_NAMED("FloodFill");
auto csFloodFill = shader->GetCS("CS_FloodFill");
const int32 floodFillIterations = Math::Max(_resolution.MaxValue() / 2 + 1, 8);
for (int32 floodFill = 0; floodFill < floodFillIterations; floodFill++)
{
context->ResetUA();
context->BindUA(0, _sdfDst->View());
context->BindSR(0, _sdfSrc->View());
context->Dispatch(csFloodFill, threadGroups.X, threadGroups.Y, threadGroups.Z);
Swap(_sdfSrc, _sdfDst);
}
}
// Encode SDF values into output storage
context->ResetUA();
context->BindSR(0, _sdfSrc->View());
// TODO: update GPU SDF texture within this task to skip additional CPU->GPU copy
auto sdfTextureDesc = GPUTextureDescription::New3D(_resolution.X, _resolution.Y, _resolution.Z, PixelFormat::R16_UNorm, GPUTextureFlags::UnorderedAccess | GPUTextureFlags::RenderTarget);
// TODO: use transient texture (single frame) // TODO: use transient texture (single frame)
auto sdfTexture = GPUTexture::New(); auto sdfTexture = GPUTexture::New();
#if GPU_ENABLE_RESOURCE_NAMING #if GPU_ENABLE_RESOURCE_NAMING
sdfTexture->SetName(TEXT("SDFTexture")); sdfTexture->SetName(TEXT("SDFTexture"));
#endif #endif
sdfTexture->Init(sdfTextureDesc); sdfTexture->Init(sdfTextureDesc);
context->BindUA(1, sdfTexture->ViewVolume());
context->Dispatch(shader->GetCS("CS_Encode"), threadGroups.X, threadGroups.Y, threadGroups.Z); // Renders directly to the output texture
context->BindUA(0, sdfTexture->ViewVolume());
// Init the volume (rasterization mixes with existing contents)
context->Dispatch(shader->GetCS("CS_Init"), threadGroups.X, threadGroups.Y, threadGroups.Z);
// Render input triangles into the SDF volume
{
PROFILE_GPU("Rasterize");
context->BindSR(0, bvh.VertexBuffer->View());
context->BindSR(1, bvh.IndexBuffer->View());
context->BindSR(2, bvh.BVHBuffer->View());
auto* rasterizeCS = shader->GetCS("CS_RasterizeTriangles");
context->Dispatch(rasterizeCS, threadGroups.X, threadGroups.Y, threadGroups.Z);
}
// Copy result data into readback buffer // Copy result data into readback buffer
if (_sdfResult) if (_sdfResult)
@@ -329,6 +215,9 @@ public:
SAFE_DELETE_GPU_RESOURCE(sdfTexture); SAFE_DELETE_GPU_RESOURCE(sdfTexture);
#if GPU_ALLOW_PROFILE_EVENTS
_timerQuery->End();
#endif
return Result::Ok; return Result::Ok;
} }
@@ -336,6 +225,10 @@ public:
{ {
GPUTask::OnSync(); GPUTask::OnSync();
_signal->NotifyOne(); _signal->NotifyOne();
#if GPU_ALLOW_PROFILE_EVENTS
if (_timerQuery->HasResult())
LOG(Info, "GPU SDF generation took {} ms", Utilities::RoundTo1DecimalPlace(_timerQuery->GetResult()));
#endif
} }
void OnFail() override void OnFail() override
@@ -366,9 +259,11 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData,
return true; return true;
ModelBase::SDFData sdf; ModelBase::SDFData sdf;
sdf.WorldUnitsPerVoxel = METERS_TO_UNITS(0.1f) / Math::Max(resolutionScale, 0.0001f); // 1 voxel per 10 centimeters sdf.WorldUnitsPerVoxel = METERS_TO_UNITS(0.1f) / Math::Max(resolutionScale, 0.0001f); // 1 voxel per 10 centimeters
#if 0
const float boundsMargin = sdf.WorldUnitsPerVoxel * 0.5f; // Add half-texel margin around the mesh const float boundsMargin = sdf.WorldUnitsPerVoxel * 0.5f; // Add half-texel margin around the mesh
bounds.Minimum -= boundsMargin; bounds.Minimum -= boundsMargin;
bounds.Maximum += boundsMargin; bounds.Maximum += boundsMargin;
#endif
const Float3 size = bounds.GetSize(); const Float3 size = bounds.GetSize();
Int3 resolution(Float3::Ceil(Float3::Clamp(size / sdf.WorldUnitsPerVoxel, 4, 256))); Int3 resolution(Float3::Ceil(Float3::Clamp(size / sdf.WorldUnitsPerVoxel, 4, 256)));
Float3 uvwToLocalMul = size; Float3 uvwToLocalMul = size;
@@ -443,6 +338,13 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData,
// http://ramakarl.com/pdfs/2016_Hoetzlein_GVDB.pdf // http://ramakarl.com/pdfs/2016_Hoetzlein_GVDB.pdf
// https://www.cse.chalmers.se/~uffe/HighResolutionSparseVoxelDAGs.pdf // https://www.cse.chalmers.se/~uffe/HighResolutionSparseVoxelDAGs.pdf
// Setup acceleration structure for fast ray tracing the mesh triangles
MeshAccelerationStructure scene;
if (inputModel)
scene.Add(inputModel, lodIndex);
else if (modelData)
scene.Add(modelData, lodIndex);
// Check if run SDF generation on a GPU via Compute Shader or on a Job System // Check if run SDF generation on a GPU via Compute Shader or on a Job System
useGPU &= GPUDevice::Instance useGPU &= GPUDevice::Instance
&& GPUDevice::Instance->GetState() == GPUDevice::DeviceState::Ready && GPUDevice::Instance->GetState() == GPUDevice::DeviceState::Ready
@@ -463,7 +365,7 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData,
// Run SDF generation via GPU async task // Run SDF generation via GPU async task
ConditionVariable signal; ConditionVariable signal;
CriticalSection mutex; CriticalSection mutex;
Task* task = New<GPUModelSDFTask>(signal, inputModel, modelData, lodIndex, resolution, &sdf, sdfResult, xyzToLocalMul, xyzToLocalAdd); Task* task = New<GPUModelSDFTask>(signal, &scene, inputModel, modelData, lodIndex, resolution, &sdf, sdfResult, xyzToLocalMul, xyzToLocalAdd, backfacesThreshold);
task->Start(); task->Start();
mutex.Lock(); mutex.Lock();
signal.Wait(mutex); signal.Wait(mutex);
@@ -487,16 +389,10 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData,
} }
else else
{ {
// Setup acceleration structure for fast ray tracing the mesh triangles
MeshAccelerationStructure scene;
if (inputModel)
scene.Add(inputModel, lodIndex);
else if (modelData)
scene.Add(modelData, lodIndex);
scene.BuildBVH(); scene.BuildBVH();
// Brute-force for each voxel to calculate distance to the closest triangle with point query and distance sign by raycasting around the voxel // Brute-force for each voxel to calculate distance to the closest triangle with point query and distance sign by raycasting around the voxel
constexpr int32 sampleCount = 12; constexpr int32 sampleCount = BUILD_DEBUG ? 6 : 12;
Float3 sampleDirections[sampleCount]; Float3 sampleDirections[sampleCount];
{ {
RandomStream rand; RandomStream rand;
@@ -524,36 +420,30 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData,
Real minDistance = sdf.MaxDistance; Real minDistance = sdf.MaxDistance;
Vector3 voxelPos = Float3((float)x, (float)y, (float)z) * xyzToLocalMul + xyzToLocalAdd; Vector3 voxelPos = Float3((float)x, (float)y, (float)z) * xyzToLocalMul + xyzToLocalAdd;
// Point query to find the distance to the closest surface
scene.PointQuery(voxelPos, minDistance, hitPoint, hitTriangle);
// Raycast samples around voxel to count triangle backfaces hit // Raycast samples around voxel to count triangle backfaces hit
int32 hitBackCount = 0, hitCount = 0; int32 hitBackCount = 0, minBackfaceHitCount = (int32)(sampleCount * backfacesThreshold);
for (int32 sample = 0; sample < sampleCount; sample++) for (int32 sample = 0; sample < sampleCount; sample++)
{ {
Ray sampleRay(voxelPos, sampleDirections[sample]); Ray sampleRay(voxelPos, sampleDirections[sample]);
sampleRay.Position -= sampleRay.Direction * 0.0001f; // Apply small margin sampleRay.Position -= sampleRay.Direction * 0.0001f; // Apply small margin
if (scene.RayCast(sampleRay, hitDistance, hitNormal, hitTriangle)) if (scene.RayCast(sampleRay, hitDistance, hitNormal, hitTriangle))
{ {
if (hitDistance < minDistance) minDistance = Math::Min(hitDistance, minDistance);
minDistance = hitDistance; if (Float3::Dot(sampleRay.Direction, hitTriangle.GetNormal()) > 0)
hitCount++; {
const bool backHit = Float3::Dot(sampleRay.Direction, hitTriangle.GetNormal()) > 0; if (++hitBackCount >= minBackfaceHitCount)
if (backHit) break;
hitBackCount++; }
} }
} }
float distance = (float)minDistance; // Point query to find the distance to the closest surface
// TODO: surface thickness threshold? shift reduce distance for all voxels by something like 0.01 to enlarge thin geometry scene.PointQuery(voxelPos, minDistance, hitPoint, hitTriangle, minDistance);
// if ((float)hitBackCount > (float)hitCount * 0.3f && hitCount != 0) if (hitBackCount >= minBackfaceHitCount)
if ((float)hitBackCount > (float)sampleCount * backfacesThreshold && hitCount != 0) minDistance *= -1; // Voxel is inside the geometry so turn it into negative distance to the surface
{
// Voxel is inside the geometry so turn it into negative distance to the surface
distance *= -1;
}
const int32 xAddress = x + yAddress; const int32 xAddress = x + yAddress;
formatWrite(voxels.Get() + xAddress * formatStride, distance * encodeMAD.X + encodeMAD.Y); formatWrite(voxels.Get() + xAddress * formatStride, minDistance * encodeMAD.X + encodeMAD.Y);
} }
} }
}; };

View File

@@ -12,6 +12,12 @@
#if USE_EDITOR #if USE_EDITOR
#include "Editor/Editor.h" #include "Editor/Editor.h"
#endif #endif
#if PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#define USE_STOCKD3D 0
#include <mf_x/mfapi.h>
#include <mf_x/mfidl.h>
#include <mf_x/mfreadwrite.h>
#else
#include <sdkddkver.h> #include <sdkddkver.h>
#if WINVER >= _WIN32_WINNT_WINBLUE && WINVER < _WIN32_WINNT_WIN10 #if WINVER >= _WIN32_WINNT_WINBLUE && WINVER < _WIN32_WINNT_WIN10
// Fix compilation for Windows 8.1 on the latest Windows SDK // Fix compilation for Windows 8.1 on the latest Windows SDK
@@ -24,6 +30,7 @@ typedef enum _MFVideoSphericalFormat { } MFVideoSphericalFormat;
#include <mfapi.h> #include <mfapi.h>
#include <mfidl.h> #include <mfidl.h>
#include <mfreadwrite.h> #include <mfreadwrite.h>
#endif
#define VIDEO_API_MF_ERROR(api, err) LOG(Warning, "[VideoBackendMF] {} failed with error 0x{:x}", TEXT(#api), (uint64)err) #define VIDEO_API_MF_ERROR(api, err) LOG(Warning, "[VideoBackendMF] {} failed with error 0x{:x}", TEXT(#api), (uint64)err)
@@ -111,7 +118,7 @@ namespace MF
player.Format = PixelFormat::NV12; player.Format = PixelFormat::NV12;
else if (subtype == MFVideoFormat_YUY2) else if (subtype == MFVideoFormat_YUY2)
player.Format = PixelFormat::YUY2; player.Format = PixelFormat::YUY2;
#if (WDK_NTDDI_VERSION >= NTDDI_WIN10) #if (WDK_NTDDI_VERSION >= NTDDI_WIN10) && PLATFORM_WINDOWS
else if (subtype == MFVideoFormat_A2R10G10B10) else if (subtype == MFVideoFormat_A2R10G10B10)
player.Format = PixelFormat::R10G10B10A2_UNorm; player.Format = PixelFormat::R10G10B10A2_UNorm;
else if (subtype == MFVideoFormat_A16B16G16R16F) else if (subtype == MFVideoFormat_A16B16G16R16F)
@@ -120,6 +127,14 @@ namespace MF
else else
{ {
// Reconfigure decoder to output supported format by force // Reconfigure decoder to output supported format by force
#if PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
// Xbox supports NV12 via HV decoder
auto fallbackFormat = PixelFormat::NV12;
GUID fallbackFormatGuid = MFVideoFormat_NV12;
#else
auto fallbackFormat = PixelFormat::YUY2;
GUID fallbackFormatGuid = MFVideoFormat_YUY2;
#endif
IMFMediaType* customType = nullptr; IMFMediaType* customType = nullptr;
hr = MFCreateMediaType(&customType); hr = MFCreateMediaType(&customType);
if (FAILED(hr)) if (FAILED(hr))
@@ -128,7 +143,7 @@ namespace MF
goto END; goto END;
} }
customType->SetGUID(MF_MT_MAJOR_TYPE, majorType); customType->SetGUID(MF_MT_MAJOR_TYPE, majorType);
customType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2); customType->SetGUID(MF_MT_SUBTYPE, fallbackFormatGuid);
MFSetAttributeSize(customType, MF_MT_FRAME_SIZE, width, height); MFSetAttributeSize(customType, MF_MT_FRAME_SIZE, width, height);
hr = playerMF.SourceReader->SetCurrentMediaType(streamIndex, nullptr, customType); hr = playerMF.SourceReader->SetCurrentMediaType(streamIndex, nullptr, customType);
if (FAILED(hr)) if (FAILED(hr))
@@ -136,12 +151,13 @@ namespace MF
VIDEO_API_MF_ERROR(SetCurrentMediaType, hr); VIDEO_API_MF_ERROR(SetCurrentMediaType, hr);
goto END; goto END;
} }
player.Format = PixelFormat::YUY2; player.Format = fallbackFormat;
customType->Release(); customType->Release();
} }
} }
else if (majorType == MFMediaType_Audio) else if (majorType == MFMediaType_Audio)
{ {
#if !(PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT) // TODO: fix missing MFAudioFormat_PCM/MFAudioFormat_Float convertion on Xbox (bug?)
player.AudioInfo.SampleRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0); player.AudioInfo.SampleRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0);
player.AudioInfo.NumChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0); player.AudioInfo.NumChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0);
player.AudioInfo.BitDepth = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16); player.AudioInfo.BitDepth = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16);
@@ -165,6 +181,7 @@ namespace MF
} }
customType->Release(); customType->Release();
} }
#endif
} }
result = false; result = false;

View File

@@ -22,8 +22,6 @@ public class Video : EngineModule
{ {
case TargetPlatform.Windows: case TargetPlatform.Windows:
case TargetPlatform.UWP: case TargetPlatform.UWP:
case TargetPlatform.XboxOne:
case TargetPlatform.XboxScarlett:
// Media Foundation // Media Foundation
options.SourcePaths.Add(Path.Combine(FolderPath, "MF")); options.SourcePaths.Add(Path.Combine(FolderPath, "MF"));
options.CompileEnv.PreprocessorDefinitions.Add("VIDEO_API_MF"); options.CompileEnv.PreprocessorDefinitions.Add("VIDEO_API_MF");
@@ -34,6 +32,15 @@ public class Video : EngineModule
options.OutputFiles.Add("mfreadwrite.lib"); options.OutputFiles.Add("mfreadwrite.lib");
options.OutputFiles.Add("mfuuid.lib"); options.OutputFiles.Add("mfuuid.lib");
break; break;
case TargetPlatform.XboxOne:
case TargetPlatform.XboxScarlett:
// Media Foundation
options.SourcePaths.Add(Path.Combine(FolderPath, "MF"));
options.CompileEnv.PreprocessorDefinitions.Add("VIDEO_API_MF");
options.OutputFiles.Add("mfplat.lib");
options.OutputFiles.Add("mfreadwrite.lib");
options.OutputFiles.Add("mfuuid.lib");
break;
case TargetPlatform.Mac: case TargetPlatform.Mac:
case TargetPlatform.iOS: case TargetPlatform.iOS:
// AVFoundation // AVFoundation

View File

@@ -704,8 +704,8 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value)
case 16: case 16:
{ {
// Get the variable type // Get the variable type
auto asset = Assets.LoadAsync<GameplayGlobals>((Guid)node->Values[0]); auto asset = Assets.Load<GameplayGlobals>((Guid)node->Values[0]);
if (!asset || asset->WaitForLoaded()) if (!asset)
{ {
OnError(node, box, TEXT("Failed to load Gameplay Global asset.")); OnError(node, box, TEXT("Failed to load Gameplay Global asset."));
value = Value::Zero; value = Value::Zero;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -18,6 +18,26 @@ bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3
return inExtentX && inExtentY; return inExtentX && inExtentY;
} }
// Determines whether there is an intersection between a ray (rPos and rDir) and a triangle (v0, v1, v2).
// Returns true on intersection and outputs the distance along the ray to the intersection point.
// This method tests if the ray intersects either the front or back of the triangle.
bool RayIntersectsTriangle(float3 rPos, float3 rDir, float3 v0, float3 v1, float3 v2, out float distance)
{
// [https://stackoverflow.com/a/42752998]
float3 edgeAB = v1 - v0;
float3 edgeAC = v2 - v0;
float3 triFaceVector = cross(edgeAB, edgeAC);
float3 vertRayOffset = rPos - v0;
float3 rayOffsetPerp = cross(vertRayOffset, rDir);
float determinant = -dot(rDir, triFaceVector);
float invDet = 1.0f / determinant;
distance = dot(vertRayOffset, triFaceVector) * invDet;
float u = dot(edgeAC, rayOffsetPerp) * invDet;
float v = -dot(edgeAB, rayOffsetPerp) * invDet;
float w = 1.0f - u - v;
return abs(determinant) >= 1E-8 && distance > 0 && u >= 0 && v >= 0 && w >= 0;
}
// Hits axis-aligned box (boxMin, boxMax) with a line (lineStart, lineEnd). // Hits axis-aligned box (boxMin, boxMax) with a line (lineStart, lineEnd).
// Returns the intersections on the line (x - closest, y - furthest). // Returns the intersections on the line (x - closest, y - furthest).
// Line hits the box if: intersections.x < intersections.y. // Line hits the box if: intersections.x < intersections.y.
@@ -42,4 +62,39 @@ bool BoxIntersectsSphere(float3 boxMin, float3 boxMax, float3 sphereCenter, floa
return distance(sphereCenter, clampedCenter) <= sphereRadius; return distance(sphereCenter, clampedCenter) <= sphereRadius;
} }
// Calculates unsigned distance from point to the AABB. If point is inside it, returns 0.
float PointDistanceBox(float3 boxMin, float3 boxMax, float3 pos)
{
float3 clampedPos = clamp(pos, boxMin, boxMax);
return length(clampedPos - pos);
}
float dot2(float3 v)
{
return dot(v, v);
}
// Calculates squared distance from point to the triangle.
float DistancePointToTriangle2(float3 p, float3 v1, float3 v2, float3 v3)
{
// [Inigo Quilez, https://iquilezles.org/articles/triangledistance/]
float3 v21 = v2 - v1; float3 p1 = p - v1;
float3 v32 = v3 - v2; float3 p2 = p - v2;
float3 v13 = v1 - v3; float3 p3 = p - v3;
float3 nor = cross(v21, v13);
return // inside/outside test
(sign(dot(cross(v21, nor), p1)) +
sign(dot(cross(v32, nor), p2)) +
sign(dot(cross(v13, nor), p3)) < 2.0)
?
// 3 edges
min(min(
dot2(v21 * saturate(dot(v21, p1) / dot2(v21)) - p1),
dot2(v32 * saturate(dot(v32, p2) / dot2(v32)) - p2)),
dot2(v13 * saturate(dot(v13, p3) / dot2(v13)) - p3))
:
// 1 face
dot(nor, p1) * dot(nor, p1) / dot2(nor);
}
#endif #endif

View File

@@ -93,6 +93,40 @@
#endif #endif
// Compiler support for HLSL 2021 that is stricter (need to use or/and/select for vector-based logical operators)
#if !defined(__DXC_VERSION_MAJOR) || (__DXC_VERSION_MAJOR <= 1 && __DXC_VERSION_MINOR < 7)
bool InternalAnd(bool a, bool b) { return bool(a && b); }
bool2 InternalAnd(bool2 a, bool2 b) { return bool2(a.x && b.x, a.y && b.y); }
bool3 InternalAnd(bool3 a, bool3 b) { return bool3(a.x && b.x, a.y && b.y, a.z && b.z); }
bool4 InternalAnd(bool4 a, bool4 b) { return bool4(a.x && b.x, a.y && b.y, a.z && b.z, a.w && b.w); }
bool InternalOr(bool a, bool b) { return bool(a || b); }
bool2 InternalOr(bool2 a, bool2 b) { return bool2(a.x || b.x, a.y || b.y); }
bool3 InternalOr(bool3 a, bool3 b) { return bool3(a.x || b.x, a.y || b.y, a.z || b.z); }
bool4 InternalOr(bool4 a, bool4 b) { return bool4(a.x || b.x, a.y || b.y, a.z || b.z, a.w || b.w); }
#define SELECT_INTERNAL(type) \
type InternalSelect(bool c, type a, type b) { return type (c ? a.x : b.x); } \
type##2 InternalSelect(bool c, type##2 a, type##2 b) { return type##2(c ? a.x : b.x, c ? a.y : b.y); } \
type##2 InternalSelect(bool2 c, type a, type b) { return type##2(c.x ? a : b, c.y ? a : b); } \
type##2 InternalSelect(bool2 c, type##2 a, type##2 b) { return type##2(c.x ? a.x : b.x, c.y ? a.y : b.y); } \
type##3 InternalSelect(bool c, type##3 a, type##3 b) { return type##3(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z); } \
type##3 InternalSelect(bool3 c, type a, type b) { return type##3(c.x ? a : b, c.y ? a : b, c.z ? a : b); } \
type##3 InternalSelect(bool3 c, type##3 a, type##3 b) { return type##3(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z); } \
type##4 InternalSelect(bool c, type##4 a, type##4 b) { return type##4(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z, c ? a.w : b.w); } \
type##4 InternalSelect(bool4 c, type a, type b) { return type##4(c.x ? a : b, c.y ? a : b, c.z ? a : b, c.w ? a : b); } \
type##4 InternalSelect(bool4 c, type##4 a, type##4 b) { return type##4(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z, c.w ? a.w : b.w); }
SELECT_INTERNAL(uint)
SELECT_INTERNAL(float)
#undef SELECT_INTERNAL
#define and(a, b) InternalAnd(a, b)
#define or(a, b) InternalOr(a, b)
#define select(c, a, b) InternalSelect(c, a, b)
#endif
// Compiler attribute fallback // Compiler attribute fallback
#ifndef UNROLL #ifndef UNROLL
#define UNROLL #define UNROLL

View File

@@ -165,11 +165,19 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target
BRANCH BRANCH
if (NoL > 0) if (NoL > 0)
{ {
#if RADIAL_LIGHT
// Shot a ray from light to the texel to see if there is any occluder
GlobalSDFTrace trace;
trace.Init(Light.Position, -L, bias, toLightDst);
GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 1.0f);
shadowMask = hit.IsHit() && hit.HitTime < toLightDst - bias * 3 ? LightShadowsStrength : 1;
#else
// Shot a ray from texel into the light to see if there is any occluder // Shot a ray from texel into the light to see if there is any occluder
GlobalSDFTrace trace; GlobalSDFTrace trace;
trace.Init(gBuffer.WorldPos + gBuffer.Normal * shadowBias, L, bias, toLightDst - bias); trace.Init(gBuffer.WorldPos + gBuffer.Normal * shadowBias, L, bias, toLightDst - bias);
GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 2.0f); GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 2.0f);
shadowMask = hit.IsHit() ? LightShadowsStrength : 1; shadowMask = hit.IsHit() ? LightShadowsStrength : 1;
#endif
} }
else else
{ {
@@ -234,7 +242,7 @@ void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupId
if (BoxIntersectsSphere(groupMin, groupMax, objectBounds.xyz, objectBounds.w)) if (BoxIntersectsSphere(groupMin, groupMax, objectBounds.xyz, objectBounds.w))
{ {
uint sharedIndex; uint sharedIndex;
InterlockedAdd(SharedCulledObjectsCount, 1, sharedIndex); InterlockedAdd(SharedCulledObjectsCount, 1u, sharedIndex);
if (sharedIndex < GLOBAL_SURFACE_ATLAS_SHARED_CULL_SIZE) if (sharedIndex < GLOBAL_SURFACE_ATLAS_SHARED_CULL_SIZE)
SharedCulledObjects[sharedIndex] = objectAddress; SharedCulledObjects[sharedIndex] = objectAddress;
} }
@@ -263,7 +271,7 @@ void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupId
// Allocate object data size in the buffer // Allocate object data size in the buffer
uint objectsStart; uint objectsStart;
uint objectsSize = objectsCount + 1; // Include objects count before actual objects data uint objectsSize = objectsCount + 1; // Include objects count before actual objects data
RWGlobalSurfaceAtlasCulledObjects.InterlockedAdd(0, objectsSize, objectsStart); // Counter at 0 RWGlobalSurfaceAtlasCulledObjects.InterlockedAdd(0u, objectsSize, objectsStart); // Counter at 0
if (objectsStart + objectsSize > CulledObjectsCapacity) if (objectsStart + objectsSize > CulledObjectsCapacity)
{ {
// Not enough space in the buffer // Not enough space in the buffer

View File

@@ -52,7 +52,7 @@ float3 LinearToSrgb(float3 linearColor)
float3 sRGBToLinear(float3 color) float3 sRGBToLinear(float3 color)
{ {
color = max(6.10352e-5, color); color = max(6.10352e-5, color);
return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92); return select(color > 0.04045, pow(color * (1.0 / 1.055) + 0.0521327, 2.4), color * (1.0 / 12.92));
} }
float3 LogToLinear(float3 logColor) float3 LogToLinear(float3 logColor)

View File

@@ -11,6 +11,7 @@
#define GLOBAL_SDF_WORLD_SIZE 60000.0f #define GLOBAL_SDF_WORLD_SIZE 60000.0f
#define GLOBAL_SDF_MIN_VALID 0.9f #define GLOBAL_SDF_MIN_VALID 0.9f
#define GLOBAL_SDF_CHUNK_MARGIN_SCALE 4.0f #define GLOBAL_SDF_CHUNK_MARGIN_SCALE 4.0f
#define GLOBAL_SDF_SAMPLER SamplerLinearClamp
// Global SDF data for a constant buffer // Global SDF data for a constant buffer
struct GlobalSDFData struct GlobalSDFData
@@ -74,6 +75,15 @@ void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldP
textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // Cascades are placed next to each other on X axis textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // Cascades are placed next to each other on X axis
} }
// Clamps Global SDF cascade UV to ensure it can be sued for gradient sampling (clamps first and last pixels).
void ClampGlobalSDFTextureGradientUV(const GlobalSDFData data, uint cascade, float texelOffset, inout float3 textureUV)
{
float cascadeSizeUV = 1.0f / data.CascadesCount;
float cascadeUVStart = cascadeSizeUV * cascade + texelOffset;
float cascadeUVEnd = cascadeUVStart + cascadeSizeUV - texelOffset * 3;
textureUV.x = clamp(textureUV.x, cascadeUVStart, cascadeUVEnd);
}
// Gets the Global SDF cascade index for the given world location. // Gets the Global SDF cascade index for the given world location.
uint GetGlobalSDFCascade(const GlobalSDFData data, float3 worldPosition) uint GetGlobalSDFCascade(const GlobalSDFData data, float3 worldPosition)
{ {
@@ -96,7 +106,7 @@ float SampleGlobalSDFCascade(const GlobalSDFData data, Texture3D<snorm float> te
float voxelSize = data.CascadeVoxelSize[cascade]; float voxelSize = data.CascadeVoxelSize[cascade];
float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN);
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex;
if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1))
distance = distanceTex; distance = distanceTex;
return distance; return distance;
@@ -115,7 +125,7 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D<snorm float> tex, floa
float voxelSize = data.CascadeVoxelSize[cascade]; float voxelSize = data.CascadeVoxelSize[cascade];
float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN);
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0);
if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1))
{ {
distance = distanceTex * maxDistanceTex; distance = distanceTex * maxDistanceTex;
@@ -140,12 +150,12 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D<snorm float> tex, Text
float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN);
float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; float maxDistanceMip = data.CascadeMaxDistanceMip[cascade];
float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0);
if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1))
{ {
distance = distanceMip * maxDistanceMip; distance = distanceMip * maxDistanceMip;
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex;
if (distanceTex < chunkMargin) if (distanceTex < chunkMargin)
distance = distanceTex; distance = distanceTex;
break; break;
@@ -169,16 +179,17 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<snorm float>
float voxelSize = data.CascadeVoxelSize[cascade]; float voxelSize = data.CascadeVoxelSize[cascade];
float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN);
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0);
if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1))
{ {
float texelOffset = 1.0f / data.Resolution; float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV);
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex;
distance = distanceTex * maxDistanceTex; distance = distanceTex * maxDistanceTex;
break; break;
@@ -203,33 +214,35 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<snorm float>
float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN);
float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; float maxDistanceMip = data.CascadeMaxDistanceMip[cascade];
float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip; float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceMip;
if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1))
{ {
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex;
if (distanceTex < chunkMargin) if (distanceTex < chunkMargin)
{ {
distance = distanceTex; distance = distanceTex;
float texelOffset = 1.0f / data.Resolution; float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV);
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex;
} }
else else
{ {
distance = distanceMip; distance = distanceMip;
float texelOffset = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR / data.Resolution; float texelOffset = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR / data.Resolution;
float xp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV);
float xn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; float xp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; float xn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; float yp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float zp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; float yn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; float zp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceMip; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceMip;
} }
break; break;
@@ -253,15 +266,14 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<snorm float>
float4 cascadePosDistance = data.CascadePosDistance[cascade]; float4 cascadePosDistance = data.CascadePosDistance[cascade];
float voxelSize = data.CascadeVoxelSize[cascade]; float voxelSize = data.CascadeVoxelSize[cascade];
float voxelExtent = voxelSize * 0.5f; float voxelExtent = voxelSize * 0.5f;
float3 worldPosition = trace.WorldPosition;
// Skip until cascade that contains the start location // Skip until cascade that contains the start location
if (any(abs(worldPosition - cascadePosDistance.xyz) > cascadePosDistance.w)) if (any(abs(trace.WorldPosition - cascadePosDistance.xyz) > cascadePosDistance.w))
continue; continue;
// Hit the cascade bounds to find the intersection points // Hit the cascade bounds to find the intersection points
float traceStartBias = voxelSize * cascadeTraceStartBias; float traceStartBias = voxelSize * cascadeTraceStartBias;
float2 intersections = LineHitBox(worldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www); float2 intersections = LineHitBox(trace.WorldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www);
intersections.xy *= traceMaxDistance; intersections.xy *= traceMaxDistance;
intersections.x = max(intersections.x, traceStartBias); intersections.x = max(intersections.x, traceStartBias);
intersections.x = max(intersections.x, nextIntersectionStart); intersections.x = max(intersections.x, nextIntersectionStart);
@@ -280,18 +292,18 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<snorm float>
LOOP LOOP
for (; step < 250 && stepTime < intersections.y && hit.HitTime < 0.0f; step++) for (; step < 250 && stepTime < intersections.y && hit.HitTime < 0.0f; step++)
{ {
float3 stepPosition = worldPosition + trace.WorldDirection * stepTime; float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime;
float stepScale = trace.StepScale; float stepScale = trace.StepScale;
// Sample SDF // Sample SDF
float stepDistance, voxelSizeScale = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR; float stepDistance, voxelSizeScale = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR;
float3 cascadeUV, textureUV; float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeUV, textureUV); GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeUV, textureUV);
float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip; float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceMip;
if (distanceMip < chunkSize) if (distanceMip < chunkSize)
{ {
stepDistance = distanceMip; stepDistance = distanceMip;
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex;
if (distanceTex < chunkMargin) if (distanceTex < chunkMargin)
{ {
stepDistance = distanceTex; stepDistance = distanceTex;
@@ -301,7 +313,7 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<snorm float>
} }
else else
{ {
// Assume no SDF nearby so perform a jump tto the next chunk // Assume no SDF nearby so perform a jump to the next chunk
stepDistance = chunkSize; stepDistance = chunkSize;
voxelSizeScale = 1.0f; voxelSizeScale = 1.0f;
} }
@@ -318,12 +330,13 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<snorm float>
{ {
// Calculate hit normal from SDF gradient // Calculate hit normal from SDF gradient
float texelOffset = 1.0f / data.Resolution; float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV);
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn)); hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn));
} }
} }

View File

@@ -311,6 +311,7 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target
float3 viewRay = lerp(lerp(ViewFrustumWorldRays[3], ViewFrustumWorldRays[0], input.TexCoord.x), lerp(ViewFrustumWorldRays[2], ViewFrustumWorldRays[1], input.TexCoord.x), 1 - input.TexCoord.y).xyz; float3 viewRay = lerp(lerp(ViewFrustumWorldRays[3], ViewFrustumWorldRays[0], input.TexCoord.x), lerp(ViewFrustumWorldRays[2], ViewFrustumWorldRays[1], input.TexCoord.x), 1 - input.TexCoord.y).xyz;
viewRay = normalize(viewRay - ViewWorldPos); viewRay = normalize(viewRay - ViewWorldPos);
trace.Init(ViewWorldPos, viewRay, ViewNearPlane, ViewFarPlane); trace.Init(ViewWorldPos, viewRay, ViewNearPlane, ViewFarPlane);
trace.NeedsHitNormal = true;
GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace);
// Debug draw // Debug draw
@@ -321,9 +322,14 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target
else else
{ {
// Debug draw SDF normals // Debug draw SDF normals
float dst; color.rgb = normalize(hit.HitNormal) * 0.5f + 0.5f;
color.rgb = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, hit.GetHitPosition(trace), dst)) * 0.5f + 0.5f;
} }
#elif 1
else
{
// Composite with SDF normals
color.rgb *= saturate(normalize(hit.HitNormal) * 0.5f + 0.7f) + 0.1f;
}
#endif #endif
return float4(color, 1); return float4(color, 1);
} }

View File

@@ -8,7 +8,7 @@
uint NextPow2(uint value) uint NextPow2(uint value)
{ {
uint mask = (1 << firstbithigh(value)) - 1; uint mask = (1u << firstbithigh(value)) - 1u;
return (value + mask) & ~mask; return (value + mask) & ~mask;
} }

View File

@@ -0,0 +1,164 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#ifndef __MESH_ACCELERATION_STRUCTURE__
#define __MESH_ACCELERATION_STRUCTURE__
#include "./Flax/Collisions.hlsl"
// This must match MeshAccelerationStructure::ToGPU
#define BVH_STACK_SIZE 32
struct BVHNode
{
float3 BoundsMin;
uint Index;
float3 BoundsMax;
int Count; // Negative for non-leaf nodes
};
struct BVHBuffers
{
StructuredBuffer<BVHNode> BVHBuffer;
ByteAddressBuffer VertexBuffer;
ByteAddressBuffer IndexBuffer;
uint VertexStride;
};
struct BVHHit
{
float Distance;
bool IsBackface;
};
float3 LoadVertexBVH(BVHBuffers bvh, uint index)
{
int addr = index << 2u;
uint vertexIndex = bvh.IndexBuffer.Load(addr);
return asfloat(bvh.VertexBuffer.Load3(vertexIndex * bvh.VertexStride));
}
// [https://tavianator.com/2011/ray_box.html]
float RayTestBoxBVH(float3 rayPos, float3 rayDir, float3 boxMin, float3 boxMax)
{
float3 rayInvDir = rcp(rayDir);
float3 tMin = (boxMin - rayPos) * rayInvDir;
float3 tMax = (boxMax - rayPos) * rayInvDir;
float3 t1 = min(tMin, tMax);
float tNear = max(max(t1.x, t1.y), t1.z);
float3 t2 = max(tMin, tMax);
float tFar = min(min(t2.x, t2.y), t2.z);
bool hit = tFar >= tNear && tFar > 0;
return hit ? max(tNear, 0) : -1;
}
// Performs raytracing against the BVH acceleration structure to find the closest intersection with a triangle.
bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f)
{
hit = (BVHHit)0;
hit.Distance = maxDistance;
// Stack-based recursion, starts from root node
uint stack[BVH_STACK_SIZE];
uint stackCount = 1;
stack[0] = 0;
bool result = false;
LOOP
while (stackCount > 0)
{
BVHNode node = bvh.BVHBuffer[stack[--stackCount]];
// Raytrace bounds
float boundsHit = RayTestBoxBVH(rayPos, rayDir, node.BoundsMin, node.BoundsMax);
BRANCH
if (boundsHit >= 0 && boundsHit < hit.Distance)
{
BRANCH
if (node.Count > 0) // Is leaf?
{
// Ray cast along all triangles in the leaf
uint indexStart = node.Index;
uint indexEnd = indexStart + node.Count;
for (uint i = indexStart; i < indexEnd;)
{
// Load triangle
float3 v0 = LoadVertexBVH(bvh, i++);
float3 v1 = LoadVertexBVH(bvh, i++);
float3 v2 = LoadVertexBVH(bvh, i++);
// Raytrace triangle
float distance;
if (RayIntersectsTriangle(rayPos, rayDir, v0, v1, v2, distance) && distance < hit.Distance)
{
float3 n = normalize(cross(v1 - v0, v2 - v0));
hit.Distance = distance;
hit.IsBackface = dot(rayDir, n) > 0;
result = true;
}
}
}
else
{
// Push children onto the stack to be tested
stack[stackCount++] = node.Index + 0;
stack[stackCount++] = node.Index + 1;
}
}
}
return result;
}
// Performs a query against the BVH acceleration structure to find the closest distance to a triangle from a given point.
bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f)
{
hit = (BVHHit)0;
hit.Distance = maxDistance;
// Stack-based recursion, starts from root node
uint stack[BVH_STACK_SIZE];
uint stackCount = 1;
stack[0] = 0;
bool result = false;
LOOP
while (stackCount > 0)
{
BVHNode node = bvh.BVHBuffer[stack[--stackCount]];
// Skip too far nodes
if (PointDistanceBox(node.BoundsMin, node.BoundsMax, pos) >= hit.Distance)
continue;
BRANCH
if (node.Count > 0) // Is leaf?
{
// Find the closest triangles in the leaf
uint indexStart = node.Index;
uint indexEnd = indexStart + node.Count;
for (uint i = indexStart; i < indexEnd;)
{
// Load triangle
float3 v0 = LoadVertexBVH(bvh, i++);
float3 v1 = LoadVertexBVH(bvh, i++);
float3 v2 = LoadVertexBVH(bvh, i++);
// Check triangle
float distance = sqrt(DistancePointToTriangle2(pos, v0, v1, v2));
if (distance < hit.Distance)
{
hit.Distance = distance;
result = true;
}
}
}
else
{
// Push children onto the stack to be tested
stack[stackCount++] = node.Index + 0;
stack[stackCount++] = node.Index + 1;
}
}
return result;
}
#endif

View File

@@ -1,18 +1,14 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
// Mesh SDF generation based on https://github.com/GPUOpen-Effects/TressFX
#include "./Flax/Common.hlsl" #include "./Flax/Common.hlsl"
#include "./Flax/ThirdParty/TressFX/TressFXSDF.hlsl" #include "./Flax/MeshAccelerationStructure.hlsl"
#define THREAD_GROUP_SIZE 64
META_CB_BEGIN(0, Data) META_CB_BEGIN(0, Data)
int3 Resolution; int3 Resolution;
uint ResolutionSize; uint ResolutionSize;
float MaxDistance; float MaxDistance;
uint VertexStride; uint VertexStride;
bool Index16bit; float BackfacesThreshold;
uint TriangleCount; uint TriangleCount;
float3 VoxelToPosMul; float3 VoxelToPosMul;
float WorldUnitsPerVoxel; float WorldUnitsPerVoxel;
@@ -20,21 +16,9 @@ float3 VoxelToPosAdd;
uint ThreadGroupsX; uint ThreadGroupsX;
META_CB_END META_CB_END
RWBuffer<uint> SDF : register(u0); uint GetVoxelIndex(uint3 groupId, uint groupIndex, uint groupSize)
uint GetVoxelIndex(uint3 groupId, uint groupIndex)
{ {
return groupIndex + (groupId.x + groupId.y * ThreadGroupsX) * THREAD_GROUP_SIZE; return groupIndex + (groupId.x + groupId.y * ThreadGroupsX) * groupSize;
}
int3 ClampVoxelCoord(int3 coord)
{
return clamp(coord, 0, Resolution - 1);
}
int GetVoxelIndex(int3 coord)
{
return Resolution.x * Resolution.y * coord.z + Resolution.x * coord.y + coord.x;
} }
float3 GetVoxelPos(int3 coord) float3 GetVoxelPos(int3 coord)
@@ -42,12 +26,6 @@ float3 GetVoxelPos(int3 coord)
return float3((float)coord.x, (float)coord.y, (float)coord.z) * VoxelToPosMul + VoxelToPosAdd; return float3((float)coord.x, (float)coord.y, (float)coord.z) * VoxelToPosMul + VoxelToPosAdd;
} }
int3 GetVoxelCoord(float3 pos)
{
pos = (pos - VoxelToPosAdd) / VoxelToPosMul;
return int3((int)pos.x, (int)pos.y, (int)pos.z);
}
int3 GetVoxelCoord(uint index) int3 GetVoxelCoord(uint index)
{ {
uint sizeX = (uint)Resolution.x; uint sizeX = (uint)Resolution.x;
@@ -59,189 +37,90 @@ int3 GetVoxelCoord(uint index)
return int3((int)coordX, (int)coordY, (int)coordZ); return int3((int)coordX, (int)coordY, (int)coordZ);
} }
// Clears SDF texture with the initial distance. #ifdef _CS_Init
#define THREAD_GROUP_SIZE 64
RWTexture3D<unorm half> SDFtex : register(u0);
// Clears SDF texture with the maximum distance.
META_CS(true, FEATURE_LEVEL_SM5) META_CS(true, FEATURE_LEVEL_SM5)
[numthreads(THREAD_GROUP_SIZE, 1, 1)] [numthreads(THREAD_GROUP_SIZE, 1, 1)]
void CS_Init(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) void CS_Init(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex)
{ {
uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex, THREAD_GROUP_SIZE);
if (voxelIndex >= ResolutionSize) if (voxelIndex >= ResolutionSize)
return; return;
float distance = MaxDistance * 10.0f; // Start with a very large value int3 voxelCoord = GetVoxelCoord(voxelIndex);
SDF[voxelIndex] = FloatFlip3(distance); SDFtex[voxelCoord] = 1.0f;
} }
// Unpacks SDF texture into distances stores as normal float value (FloatFlip3 is used for interlocked operations on uint). #endif
META_CS(true, FEATURE_LEVEL_SM5)
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
void CS_Resolve(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex)
{
uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex);
if (voxelIndex >= ResolutionSize)
return;
SDF[voxelIndex] = IFloatFlip3(SDF[voxelIndex]);
}
#ifdef _CS_RasterizeTriangle #ifdef _CS_RasterizeTriangles
#define THREAD_GROUP_SIZE 64
RWTexture3D<unorm half> SDFtex : register(u0);
ByteAddressBuffer VertexBuffer : register(t0); ByteAddressBuffer VertexBuffer : register(t0);
ByteAddressBuffer IndexBuffer : register(t1); ByteAddressBuffer IndexBuffer : register(t1);
StructuredBuffer<BVHNode> BVHBuffer : register(t2);
uint LoadIndex(uint i)
{
if (Index16bit)
{
uint index = IndexBuffer.Load((i >> 1u) << 2u);
index = (i & 1u) == 1u ? (index >> 16) : index;
return index & 0xffff;
}
return IndexBuffer.Load(i << 2u);
}
float3 LoadVertex(uint i)
{
return asfloat(VertexBuffer.Load3(i * VertexStride));
}
// Renders triangle mesh into the SDF texture by writing minimum distance to the triangle into all intersecting voxels. // Renders triangle mesh into the SDF texture by writing minimum distance to the triangle into all intersecting voxels.
META_CS(true, FEATURE_LEVEL_SM5) META_CS(true, FEATURE_LEVEL_SM5)
[numthreads(THREAD_GROUP_SIZE, 1, 1)] [numthreads(THREAD_GROUP_SIZE, 1, 1)]
void CS_RasterizeTriangle(uint3 DispatchThreadId : SV_DispatchThreadID) void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID, uint GroupIndex : SV_GroupIndex)
{ {
uint triangleIndex = DispatchThreadId.x; uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex, THREAD_GROUP_SIZE);
if (triangleIndex >= TriangleCount) if (voxelIndex >= ResolutionSize)
return; return;
int3 voxelCoord = GetVoxelCoord(voxelIndex);
float3 voxelPos = GetVoxelPos(voxelCoord);
// Load triangle BVHBuffers bvh;
triangleIndex *= 3; bvh.BVHBuffer = BVHBuffer;
uint i0 = LoadIndex(triangleIndex + 0); bvh.VertexBuffer = VertexBuffer;
uint i1 = LoadIndex(triangleIndex + 1); bvh.IndexBuffer = IndexBuffer;
uint i2 = LoadIndex(triangleIndex + 2); bvh.VertexStride = VertexStride;
float3 v0 = LoadVertex(i0);
float3 v1 = LoadVertex(i1);
float3 v2 = LoadVertex(i2);
// Project triangle into SDF voxels // Point query to find the distance to the closest surface
float3 vMargin = float3(WorldUnitsPerVoxel, WorldUnitsPerVoxel, WorldUnitsPerVoxel); BVHHit hit;
float3 vMin = min(min(v0, v1), v2) - vMargin; PointQueryBVH(bvh, voxelPos, hit, MaxDistance);
float3 vMax = max(max(v0, v1), v2) + vMargin; float sdf = hit.Distance;
int3 voxelMargin = int3(1, 1, 1);
int3 voxelMin = GetVoxelCoord(vMin) - voxelMargin;
int3 voxelMax = GetVoxelCoord(vMax) + voxelMargin;
voxelMin = ClampVoxelCoord(voxelMin);
voxelMax = ClampVoxelCoord(voxelMax);
// Rasterize into SDF voxels // Raycast triangles around voxel to count triangle backfaces hit
for (int z = voxelMin.z; z <= voxelMax.z; z++) #define CLOSEST_CACHE_SIZE 6
float3 closestDirections[CLOSEST_CACHE_SIZE] =
{ {
for (int y = voxelMin.y; y <= voxelMax.y; y++) float3(+1, 0, 0),
float3(-1, 0, 0),
float3(0, +1, 0),
float3(0, -1, 0),
float3(0, 0, +1),
float3(0, 0, -1),
};
uint hitBackCount = 0;
uint minBackfaceHitCount = (uint)(CLOSEST_CACHE_SIZE * BackfacesThreshold);
for (uint i = 0; i < CLOSEST_CACHE_SIZE; i++)
{
float3 rayDir = closestDirections[i];
if (RayCastBVH(bvh, voxelPos, rayDir, hit, MaxDistance))
{ {
for (int x = voxelMin.x; x <= voxelMax.x; x++) sdf = min(sdf, hit.Distance);
{ if (hit.IsBackface)
int3 voxelCoord = int3(x, y, z); hitBackCount++;
int voxelIndex = GetVoxelIndex(voxelCoord);
float3 voxelPos = GetVoxelPos(voxelCoord);
float distance = SignedDistancePointToTriangle(voxelPos, v0, v1, v2);
if (distance < -10.0f) // TODO: find a better way to reject negative distance from degenerate triangles that break SDF shape
distance = abs(distance);
InterlockedMin(SDF[voxelIndex], FloatFlip3(distance));
}
} }
} }
} if (hitBackCount >= minBackfaceHitCount)
#endif
#if defined(_CS_FloodFill) || defined(_CS_Encode)
Buffer<uint> InSDF : register(t0);
float GetVoxel(int voxelIndex)
{
return asfloat(InSDF[voxelIndex]);
}
float GetVoxel(int3 coord)
{
coord = ClampVoxelCoord(coord);
int voxelIndex = GetVoxelIndex(coord);
return GetVoxel(voxelIndex);
}
float CombineSDF(float sdf, int3 nearbyCoord, float nearbyDistance)
{
// Sample nearby voxel
float sdfNearby = GetVoxel(nearbyCoord);
// Include distance to that nearby voxel
if (sdfNearby < 0.0f)
nearbyDistance *= -1;
sdfNearby += nearbyDistance;
if (sdfNearby > MaxDistance)
{ {
// Ignore if nearby sample is invalid (see CS_Init) // Voxel is inside the geometry so turn it into negative distance to the surface
sdf *= -1;
} }
else if (sdf > MaxDistance)
{
// Use nearby sample if current one is invalid (see CS_Init)
sdf = sdfNearby;
}
else
{
// Use distance closer to 0
sdf = sdf >= 0 ? min(sdf, sdfNearby) : max(sdf, sdfNearby);
}
return sdf;
}
// Fills the voxels with minimum distances to the triangles.
META_CS(true, FEATURE_LEVEL_SM5)
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
void CS_FloodFill(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex)
{
uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex);
if (voxelIndex >= ResolutionSize)
return;
float sdf = GetVoxel(voxelIndex);
// Skip if the distance is already so small that we know that triangle is nearby
if (abs(sdf) > WorldUnitsPerVoxel * 1.2f)
{
int3 voxelCoord = GetVoxelCoord(voxelIndex);
int3 offset = int3(-1, 0, 1);
// Sample nearby voxels
float nearbyDistance = WorldUnitsPerVoxel;
sdf = CombineSDF(sdf, voxelCoord + offset.zyy, nearbyDistance);
sdf = CombineSDF(sdf, voxelCoord + offset.yzy, nearbyDistance);
sdf = CombineSDF(sdf, voxelCoord + offset.yyz, nearbyDistance);
sdf = CombineSDF(sdf, voxelCoord + offset.xyy, nearbyDistance);
sdf = CombineSDF(sdf, voxelCoord + offset.yxy, nearbyDistance);
sdf = CombineSDF(sdf, voxelCoord + offset.yyx, nearbyDistance);
}
SDF[voxelIndex] = asuint(sdf);
}
RWTexture3D<half> SDFtex : register(u1);
// Encodes SDF values into the packed format with normalized distances.
META_CS(true, FEATURE_LEVEL_SM5)
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
void CS_Encode(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex)
{
uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex);
if (voxelIndex >= ResolutionSize)
return;
float sdf = GetVoxel(voxelIndex);
sdf = min(sdf, MaxDistance);
// Pack from range [-MaxDistance; +MaxDistance] to [0; 1] // Pack from range [-MaxDistance; +MaxDistance] to [0; 1]
sdf = clamp(sdf, -MaxDistance, MaxDistance);
sdf = (sdf / MaxDistance) * 0.5f + 0.5f; sdf = (sdf / MaxDistance) * 0.5f + 0.5f;
int3 voxelCoord = GetVoxelCoord(voxelIndex);
SDFtex[voxelCoord] = sdf; SDFtex[voxelCoord] = sdf;
} }

View File

@@ -1,121 +0,0 @@
// Source: https://github.com/GPUOpen-Effects/TressFX
// License: MIT
//
// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//When building the SDF we want to find the lowest distance at each SDF cell. In order to allow multiple threads to write to the same
//cells, it is necessary to use atomics. However, there is no support for atomics with 32-bit floats so we convert the float into unsigned int
//and use atomic_min() / InterlockedMin() as a workaround.
//
//When used with atomic_min, both FloatFlip2() and FloatFlip3() will store the float with the lowest magnitude.
//The difference is that FloatFlip2() will preper negative values ( InterlockedMin( FloatFlip2(1.0), FloatFlip2(-1.0) ) == -1.0 ),
//while FloatFlip3() prefers positive values ( InterlockedMin( FloatFlip3(1.0), FloatFlip3(-1.0) ) == 1.0 ).
//Using FloatFlip3() seems to result in a SDF with higher quality compared to FloatFlip2().
uint FloatFlip2(float fl)
{
uint f = asuint(fl);
return (f << 1) | (f >> 31 ^ 0x00000001); //Rotate sign bit to least significant and Flip sign bit so that (0 == negative)
}
uint IFloatFlip2(uint f2)
{
return (f2 >> 1) | (f2 << 31 ^ 0x80000000);
}
uint FloatFlip3(float fl)
{
uint f = asuint(fl);
return (f << 1) | (f >> 31); //Rotate sign bit to least significant
}
uint IFloatFlip3(uint f2)
{
return (f2 >> 1) | (f2 << 31);
}
float DistancePointToEdge(float3 p, float3 x0, float3 x1, out float3 n)
{
float3 x10 = x1 - x0;
float t = dot(x1 - p, x10) / dot(x10, x10);
t = max(0.0f, min(t, 1.0f));
float3 a = p - (t*x0 + (1.0f - t)*x1);
float d = length(a);
n = a / (d + 1e-30f);
return d;
}
// Check if p is in the positive or negative side of triangle (x0, x1, x2)
// Positive side is where the normal vector of triangle ( (x1-x0) x (x2-x0) ) is pointing to.
float SignedDistancePointToTriangle(float3 p, float3 x0, float3 x1, float3 x2)
{
float d = 0;
float3 x02 = x0 - x2;
float l0 = length(x02) + 1e-30f;
x02 = x02 / l0;
float3 x12 = x1 - x2;
float l1 = dot(x12, x02);
x12 = x12 - l1*x02;
float l2 = length(x12) + 1e-30f;
x12 = x12 / l2;
float3 px2 = p - x2;
float b = dot(x12, px2) / l2;
float a = (dot(x02, px2) - l1*b) / l0;
float c = 1 - a - b;
// normal vector of triangle. Don't need to normalize this yet.
float3 nTri = cross((x1 - x0), (x2 - x0));
float3 n;
float tol = 1e-8f;
if (a >= -tol && b >= -tol && c >= -tol)
{
n = p - (a*x0 + b*x1 + c*x2);
d = length(n);
float3 n1 = n / d;
float3 n2 = nTri / (length(nTri) + 1e-30f); // if d == 0
n = (d > 0) ? n1 : n2;
}
else
{
float3 n_12;
float3 n_02;
d = DistancePointToEdge(p, x0, x1, n);
float d12 = DistancePointToEdge(p, x1, x2, n_12);
float d02 = DistancePointToEdge(p, x0, x2, n_02);
d = min(d, d12);
d = min(d, d02);
n = (d == d12) ? n_12 : n;
n = (d == d02) ? n_02 : n;
}
d = (dot(p - x0, nTri) < 0.f) ? -d : d;
return d;
}

View File

@@ -341,7 +341,7 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_
if (all(gridCoordinate < GridSizeInt)) if (all(gridCoordinate < GridSizeInt))
{ {
scatteringAndExtinction = isnan(scatteringAndExtinction) || isinf(scatteringAndExtinction) ? 0 : scatteringAndExtinction; scatteringAndExtinction = select(or(isnan(scatteringAndExtinction), isinf(scatteringAndExtinction)), 0, scatteringAndExtinction);
RWLightScattering[gridCoordinate] = max(scatteringAndExtinction, 0); RWLightScattering[gridCoordinate] = max(scatteringAndExtinction, 0);
} }
} }

View File

@@ -48,9 +48,6 @@ public class nethost : ThirdPartyModule
switch (options.Platform.Target) switch (options.Platform.Target)
{ {
case TargetPlatform.Windows: case TargetPlatform.Windows:
case TargetPlatform.XboxOne:
case TargetPlatform.XboxScarlett:
case TargetPlatform.UWP:
if (hostRuntime.Type == DotNetSdk.HostType.CoreCLR) if (hostRuntime.Type == DotNetSdk.HostType.CoreCLR)
{ {
options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "nethost.lib")); options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "nethost.lib"));
@@ -63,6 +60,12 @@ public class nethost : ThirdPartyModule
options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "coreclr.dll")); options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "coreclr.dll"));
} }
break; break;
case TargetPlatform.XboxOne:
case TargetPlatform.XboxScarlett:
options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "monosgen-2.0.lib"));
options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "mono-profiler-aot.lib"));
options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "System.Globalization.Native-Static.lib"));
break;
case TargetPlatform.Linux: case TargetPlatform.Linux:
options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.a")); options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.a"));
options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.so")); options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.so"));

View File

@@ -2043,6 +2043,7 @@ namespace Flax.Build.Bindings
contents.Append(')').AppendLine(); contents.Append(')').AppendLine();
contents.Append(" {").AppendLine(); contents.Append(" {").AppendLine();
contents.Append(" static MMethod* method = nullptr;").AppendLine(); contents.Append(" static MMethod* method = nullptr;").AppendLine();
contents.AppendFormat(" if (!MCore::Ready) {{ MCore::OnManagedEventAfterShutdown(\"{0}.{1}\"); return; }}", classTypeNameManaged, eventInfo.Name).AppendLine();
contents.AppendFormat(" if (!method) {{ method = {1}::TypeInitializer.GetClass()->GetMethod(\"Internal_{0}_Invoke\", {2}); CHECK(method); }}", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine(); contents.AppendFormat(" if (!method) {{ method = {1}::TypeInitializer.GetClass()->GetMethod(\"Internal_{0}_Invoke\", {2}); CHECK(method); }}", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine();
contents.Append(" MObject* exception = nullptr;").AppendLine(); contents.Append(" MObject* exception = nullptr;").AppendLine();
if (paramsCount == 0) if (paramsCount == 0)

View File

@@ -217,9 +217,17 @@ namespace Flax.Build
using RegistryKey sdkVersionsKey = baseKey.OpenSubKey($@"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sdk"); using RegistryKey sdkVersionsKey = baseKey.OpenSubKey($@"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sdk");
using RegistryKey runtimeKey = baseKey.OpenSubKey(@$"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App"); using RegistryKey runtimeKey = baseKey.OpenSubKey(@$"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App");
using RegistryKey hostKey = baseKey.OpenSubKey(@$"SOFTWARE\dotnet\Setup\InstalledVersions\{arch}\sharedhost"); using RegistryKey hostKey = baseKey.OpenSubKey(@$"SOFTWARE\dotnet\Setup\InstalledVersions\{arch}\sharedhost");
dotnetPath = (string)hostKey.GetValue("Path"); dotnetPath = (string)hostKey?.GetValue("Path");
dotnetSdkVersions = sdkVersionsKey?.GetValueNames() ?? Enumerable.Empty<string>(); dotnetSdkVersions = sdkVersionsKey?.GetValueNames() ?? Enumerable.Empty<string>();
dotnetRuntimeVersions = runtimeKey?.GetValueNames() ?? Enumerable.Empty<string>(); dotnetRuntimeVersions = runtimeKey?.GetValueNames() ?? Enumerable.Empty<string>();
if (string.IsNullOrEmpty(dotnetPath))
{
// The sharedhost registry key seems to be deprecated, assume the default installation location instead
var defaultPath = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Program Files", "dotnet");
if (File.Exists(Path.Combine(defaultPath, "dotnet.exe")))
dotnetPath = defaultPath;
}
} }
#pragma warning restore CA1416 #pragma warning restore CA1416
break; break;

View File

@@ -89,7 +89,11 @@ namespace Flax.Deps.Dependencies
os = "windows"; os = "windows";
runtimeFlavor = "Mono"; runtimeFlavor = "Mono";
buildMonoAotCross = true; buildMonoAotCross = true;
buildArgs = $" -subset mono+libs -cmakeargs \"-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1\" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; var defines = "-D_GAMING_XBOX=1-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1";
defines += targetPlatform == TargetPlatform.XboxScarlett ? "-D_GAMING_XBOX_SCARLETT=1" : "-D_GAMING_XBOX_XBOXONE=1";
defines += "-DDISABLE_EXECUTABLES=1-DDISABLE_SHARED_LIBS=1";
buildArgs = $" -subset mono+libs -cmakeargs \"{defines}\" /p:FeaturePerfTracing=false /p:FeatureWin32Registry=false /p:FeatureCominteropApartmentSupport=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false";
envVars.Add("_GAMING_XBOX", "1");
break; break;
case TargetPlatform.Linux: case TargetPlatform.Linux:
os = "linux"; os = "linux";
@@ -237,15 +241,28 @@ namespace Flax.Deps.Dependencies
switch (targetPlatform) switch (targetPlatform)
{ {
case TargetPlatform.Windows: case TargetPlatform.Windows:
case TargetPlatform.XboxOne:
case TargetPlatform.XboxScarlett:
libs1 = new[] libs1 = new[]
{ {
"lib/coreclr.dll", "lib/coreclr.dll",
"lib/coreclr.import.lib", "lib/coreclr.import.lib",
}; };
libs2 = new string[] libs2 = new[]
{ {
"System.Globalization.Native.dll",
"System.IO.Compression.Native.dll",
};
break;
case TargetPlatform.XboxOne:
case TargetPlatform.XboxScarlett:
libs1 = new[]
{
"lib/monosgen-2.0.lib",
"lib/mono-profiler-aot.lib",
};
libs2 = new[]
{
"lib/System.Globalization.Native-Static.lib",
"lib/System.IO.Compression.Native-Static.lib",
}; };
break; break;
default: default:

View File

@@ -13,6 +13,11 @@ namespace Flax.Build.Platforms
/// <seealso cref="Flax.Build.Platforms.WindowsToolchainBase" /> /// <seealso cref="Flax.Build.Platforms.WindowsToolchainBase" />
public abstract class GDKToolchain : WindowsToolchainBase public abstract class GDKToolchain : WindowsToolchainBase
{ {
/// <summary>
/// Enables OpenMP library as dynamic dependency.
/// </summary>
protected bool OpenMP = false;
/// <summary> /// <summary>
/// Gets the version of Xbox Services toolset. /// Gets the version of Xbox Services toolset.
/// </summary> /// </summary>
@@ -74,6 +79,12 @@ namespace Flax.Build.Platforms
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vccorlib140.dll")); options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vccorlib140.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140.dll")); options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140_1.dll")); options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140_1.dll"));
if (OpenMP)
{
redistToolsPath = Path.Combine(paths[0], "x64", "Microsoft.VC" + (int)crtToolset + ".OpenMP");
redistToolsPath = Utilities.RemovePathRelativeParts(redistToolsPath);
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcomp140.dll"));
}
} }
} }
} }