Skip to content

Commit

Permalink
[Rendering] WIP: Started rework to move instancing from ModelComponen…
Browse files Browse the repository at this point in the history
…t to it's own render feature
  • Loading branch information
tebjan committed Jul 16, 2020
1 parent c54b22e commit d11eaea
Show file tree
Hide file tree
Showing 11 changed files with 279 additions and 59 deletions.
91 changes: 91 additions & 0 deletions sources/engine/Stride.Engine/Engine/InstancingComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Text;
using Stride.Core;
using Stride.Core.Mathematics;
using Stride.Engine.Design;
using Stride.Engine.Processors;

namespace Stride.Engine
{
[DefaultEntityComponentProcessor(typeof(InstancingProcessor))]
public class InstancingComponent : ActivableEntityComponent
{
/// <summary>
/// The instance count
/// </summary>
public int InstanceCount;

/// <summary>
/// The instance transformation matrices.
/// </summary>
[DataMemberIgnore]
public Matrix[] WorldMatrices = new Matrix[0];

/// <summary>
/// The inverse instance transformation matrices, updated automatically by the <see cref="InstancingProcessor"/>.
/// </summary>
[DataMemberIgnore]
public Matrix[] WorldInverseMatrices = new Matrix[0];

/// <summary>
/// The bounding box of the world matrices, updated automatically by the <see cref="InstancingProcessor"/>.
/// </summary>
public BoundingBox BoundingBox = BoundingBox.Empty;

bool matricesUpdated;

/// <summary>
/// Updates the world matrices.
/// </summary>
/// <param name="matrices">The matrices.</param>
/// <param name="instanceCount">The instance count. When set to -1 the lenght if the matrices array is used</param>
public void UpdateWorldMatrices(Matrix[] matrices, int instanceCount = -1)
{
WorldMatrices = matrices;

if (WorldMatrices != null)
{
InstanceCount = instanceCount < 0 ? WorldMatrices.Length : Math.Min(WorldMatrices.Length, instanceCount);
}
else
{
InstanceCount = 0;
}

matricesUpdated = true;
}

// Should this be here or in processor or render feature?
public void Process()
{
if (matricesUpdated)
{
if (WorldMatrices != null)
{
// Make sure inverse matrices are big enough
if (WorldInverseMatrices.Length < InstanceCount)
{
WorldInverseMatrices = new Matrix[InstanceCount];
}

// Invert matrices and update bounding box
var bb = BoundingBox.Empty;
for (int i = 0; i < InstanceCount; i++)
{
Matrix.Invert(ref WorldMatrices[i], out WorldInverseMatrices[i]);
var pos = WorldMatrices[i].TranslationVector;
BoundingBox.Merge(ref bb, ref pos, out bb);
}
BoundingBox = bb;
}
else
{
BoundingBox = BoundingBox.Empty;
}
}

matricesUpdated = false;
}
}
}
28 changes: 4 additions & 24 deletions sources/engine/Stride.Engine/Engine/ModelComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,6 @@ private void CheckSkeleton()
[Display("Cast shadows")]
public bool IsShadowCaster { get; set; }

/// <summary>
/// Gets or sets a boolean indicating if this model component is instanced.
/// </summary>
/// <value>A boolean indicating if this model component is is instanced.</value>
/// <userdoc>Tells the material whether instancing is used.</userdoc>
[DataMember(25)]
public bool IsInstanced { get; set; }

/// <summary>
/// The render group for this component.
/// </summary>
Expand All @@ -172,22 +164,6 @@ private void CheckSkeleton()
[DataMemberIgnore]
public BoundingSphere BoundingSphere;

/// <summary>
/// The instance transformation matrices.
/// </summary>
[DataMemberIgnore]
public Matrix[] InstanceWorldMatrices = new Matrix[0];

/// <summary>
/// The inverse instance transformation matrices, updated automatically.
/// </summary>
[DataMemberIgnore]
public Matrix[] InstanceWorldInverseMatrices = new Matrix[0];

// TODO: Manage buffers, where to transfer the data to the buffers?
public Buffer<Matrix> InstanceWorldBuffer;
public Buffer<Matrix> InstanceWorldInverseBuffer;

/// <summary>
/// Gets the material at the specified index. If the material is not overriden by this component, it will try to get it from <see cref="Stride.Rendering.Model.Materials"/>
/// </summary>
Expand Down Expand Up @@ -250,6 +226,10 @@ private void ModelUpdated()
}
}

/// <summary>
/// Updates the skeleton, skinning and bounding box with the associated transform component.
/// </summary>
/// <param name="transformComponent">The transform component.</param>
internal void Update(TransformComponent transformComponent)
{
if (!Enabled || model == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Stride.Core.Annotations;
using Stride.Core.Threading;
using Stride.Games;
using Stride.Rendering;

namespace Stride.Engine.Processors
{
public class InstancingProcessor : EntityProcessor<InstancingComponent, InstancingProcessor.InstancingData>
{
public class InstancingData
{
public ModelComponent ModelComponent;
}

public InstancingProcessor()
: base (typeof(ModelComponent)) // Requires a ModelComponent
{

}

public override void Draw(RenderContext context)
{
Dispatcher.ForEach(ComponentDatas, entity =>
{
var instancingComponent = entity.Key;
var instancingData = entity.Value;

UpdateInstancingData(instancingComponent, instancingData);
});
}

private void UpdateInstancingData(InstancingComponent instancingComponent, InstancingData instancingData)
{
if (instancingComponent.Enabled && instancingComponent.InstanceCount > 0)
{
instancingComponent.Process();
}
}

protected override void OnEntityComponentAdding(Entity entity, [NotNull] InstancingComponent component, [NotNull] InstancingData data)
{
base.OnEntityComponentAdding(entity, component, data);
}

// Instancing data per InstancingComponent
protected override InstancingData GenerateComponentData([NotNull] Entity entity, [NotNull] InstancingComponent component)
{
return new InstancingData();
}

protected override bool IsAssociatedDataValid([NotNull] Entity entity, [NotNull] InstancingComponent component, [NotNull] InstancingData associatedData)
{
return true;
}
}
}
95 changes: 95 additions & 0 deletions sources/engine/Stride.Engine/Rendering/InstancingRenderFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Text;
using Stride.Core.Mathematics;
using Stride.Graphics;
using Stride.Rendering;

namespace Stride.Engine.Rendering
{
public struct InstancingData
{
public Matrix[] WorldMatrices;
public Matrix[] WorldInverseMatrices;

// GPU buffers, managed by the render feature
public Buffer<Matrix> InstanceWorldBuffer;
public Buffer<Matrix> InstanceWorldInverseBuffer;
}

public class InstancingRenderFeature : SubRenderFeature
{
private StaticObjectPropertyKey<InstancingData> renderObjectInstancingDataInfoKey;

private LogicalGroupReference instancingResourceGroupKey;

/// <inheritdoc/>
protected override void InitializeCore()
{
renderObjectInstancingDataInfoKey = RootRenderFeature.RenderData.CreateStaticObjectKey<InstancingData>();
instancingResourceGroupKey = ((RootEffectRenderFeature)RootRenderFeature).CreateDrawLogicalGroup("Instancing");
}

/// <inheritdoc/>
public override void Extract()
{
var renderObjectInstancingData = RootRenderFeature.RenderData.GetData(renderObjectInstancingDataInfoKey);

foreach (var objectNodeReference in RootRenderFeature.ObjectNodeReferences)
{
var objectNode = RootRenderFeature.GetObjectNode(objectNodeReference);
var renderMesh = objectNode.RenderObject as RenderMesh;
if (renderMesh == null)
continue;

var modelComponent = renderMesh.Source as ModelComponent;
if (modelComponent == null)
continue;

var instancingComponent = modelComponent.Entity.Get<InstancingComponent>();
if (instancingComponent == null)
continue;

var data = renderObjectInstancingData[renderMesh.StaticObjectNode];
data.WorldMatrices = instancingComponent.WorldMatrices;
data.WorldInverseMatrices = instancingComponent.WorldInverseMatrices;
renderMesh.ActiveMeshDraw.InstanceCount = instancingComponent.InstanceCount;

renderMesh.BoundingBox.Center += instancingComponent.BoundingBox.Center;
renderMesh.BoundingBox.Extent += instancingComponent.BoundingBox.Extent;
}
}

/// <inheritdoc/>
public unsafe override void Prepare(RenderDrawContext context)
{
var renderObjectInstancingData = RootRenderFeature.RenderData.GetData(renderObjectInstancingDataInfoKey);

foreach (var renderNode in ((RootEffectRenderFeature)RootRenderFeature).RenderNodes)
{
var perDrawLayout = renderNode.RenderEffect.Reflection?.PerDrawLayout;
if (perDrawLayout == null)
continue;

if (instancingResourceGroupKey.Index < 0)
continue;

var group = perDrawLayout.GetLogicalGroup(instancingResourceGroupKey);
if (group.DescriptorEntryStart == -1)
continue;

var renderMesh = renderNode.RenderObject as RenderMesh;
if (renderMesh == null)
continue;

var instancingData = renderObjectInstancingData[renderMesh.StaticObjectNode];

instancingData.InstanceWorldBuffer.SetData(context.CommandList, instancingData.WorldMatrices);
instancingData.InstanceWorldInverseBuffer.SetData(context.CommandList, instancingData.WorldInverseMatrices);

renderNode.Resources.DescriptorSet.SetShaderResourceView(group.DescriptorEntryStart, instancingData.InstanceWorldBuffer);
renderNode.Resources.DescriptorSet.SetShaderResourceView(group.DescriptorEntryStart + 1, instancingData.InstanceWorldInverseBuffer);
}
}
}
}
23 changes: 0 additions & 23 deletions sources/engine/Stride.Engine/Rendering/ModelRenderProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,34 +89,11 @@ private void UpdateRenderModel(ModelComponent modelComponent, RenderModel render

var modelViewHierarchy = modelComponent.Skeleton;
var nodeTransformations = modelViewHierarchy.NodeTransformations;

// Check instancing
var isInstanced = modelComponent.IsInstanced
&& modelComponent.InstanceWorldMatrices != null
&& modelComponent.InstanceWorldMatrices.Length > 0
&& modelComponent.InstanceWorldBuffer != null
&& modelComponent.InstanceWorldInverseBuffer != null;

renderModel.IsInstanced = isInstanced;

if (isInstanced)
{
renderModel.InstanceCount = modelComponent.InstanceWorldMatrices.Length;
}

for (int sourceMeshIndex = 0; sourceMeshIndex < renderModel.Materials.Length; sourceMeshIndex++)
{
var materialInfo = renderModel.Materials[sourceMeshIndex];

if (renderModel.IsInstanced)
{
foreach (var materialPass in materialInfo.Material.Passes)
{
materialPass.Parameters.Set(TransformationWAndVPInstancedKeys.InstanceWorld, modelComponent.InstanceWorldBuffer);
materialPass.Parameters.Set(TransformationWAndVPInstancedKeys.InstanceWorldInverse, modelComponent.InstanceWorldInverseBuffer);
}
}

var passes = materialInfo.MeshCount;
// Note: indices in RenderModel.Meshes and Model.Meshes are different (due to multipass materials)
var meshIndex = materialInfo.MeshStartIndex;
Expand Down
19 changes: 19 additions & 0 deletions sources/engine/Stride.Rendering/Rendering/Instancing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using Stride.Core.Mathematics;
using Stride.Graphics;

namespace Stride.Rendering
{
public class Instancing
{



public void UpdateTransforms(Matrix[] worldMatrices)
{

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public override void PrepareEffectPermutations(RenderDrawContext context)
var materialInfo = renderMesh.MaterialInfo;

// Add instanced shaders
material.Parameters.Set(MaterialKeys.HasInstancing, renderMesh.RenderModel.IsInstanced);
material.Parameters.Set(MaterialKeys.HasInstancing, renderMesh.ActiveMeshDraw.InstanceCount > 0);

// Material use first 16 bits
var materialHashCode = material != null ? ((uint)material.GetHashCode() & 0x0FFF) | ((uint)material.PassIndex << 12) : 0;
Expand Down
2 changes: 2 additions & 0 deletions sources/engine/Stride.Rendering/Rendering/MeshDraw.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public class MeshDraw

public int DrawCount;

public int InstanceCount;

public int StartLocation;

public VertexBufferBinding[] VertexBuffers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,15 @@ public override void Draw(RenderDrawContext context, RenderView renderView, Rend
// Draw
if (drawData.IndexBuffer == null)
{
if (renderMesh.RenderModel.IsInstanced)
commandList.DrawInstanced(drawData.DrawCount, renderMesh.RenderModel.InstanceCount, drawData.StartLocation);
if (drawData.InstanceCount > 0)
commandList.DrawInstanced(drawData.DrawCount, drawData.InstanceCount, drawData.StartLocation);
else
commandList.Draw(drawData.DrawCount, drawData.StartLocation);
}
else
{
if (renderMesh.RenderModel.IsInstanced)
commandList.DrawIndexedInstanced(drawData.DrawCount, renderMesh.RenderModel.InstanceCount, drawData.StartLocation);
if (drawData.InstanceCount > 0)
commandList.DrawIndexedInstanced(drawData.DrawCount, drawData.InstanceCount, drawData.StartLocation);
else
commandList.DrawIndexed(drawData.DrawCount, drawData.StartLocation);
}
Expand Down
Loading

0 comments on commit d11eaea

Please sign in to comment.