Files
2026-07-13 13:13:17 +08:00

224 lines
9.7 KiB
C#

using T3.Core.Rendering;
using T3.Core.Rendering.Material;
namespace Lib.render.shading._;
/// <summary>
/// Draws the nodes defined in the scene setup by dispatching a series of draw calls.
/// To improve performance we:
/// - set shaders once
/// - set shared constant buffers (transform, fog, etc.) only ones
/// - only update material related SRVs
/// - only clean-up once
///
/// LATER optimizations could include:
/// - Using point references for subcomponent manipulation of color and transform
/// - Using point references for instancing
/// - Sorting draw order to draw batches of matching materials, meshes etc points.
///
/// </summary>
[Guid("6e32756e-4267-47f1-bad0-56ee8f58b070")]
internal sealed class _DispatchSceneDraws : Instance<_DispatchSceneDraws>
{
[Output(Guid = "bda1583a-f5ea-4c3b-ae7d-0bac614d29ec")]
public readonly Slot<Command> Output = new();
public _DispatchSceneDraws()
{
Output.UpdateAction += Update;
}
private void Update(EvaluationContext context)
{
var device = ResourceManager.Device;
var deviceContext = device.ImmediateContext;
var vsStage = deviceContext.VertexShader;
var psStage = deviceContext.PixelShader;
_useSceneMaterials = UseSceneMaterials.GetValue(context);
// Keep current state
_prevConstantBuffers = vsStage.GetConstantBuffers(0, ConstantBufferIndexCount);
_prevShaderResourceViews = vsStage.GetShaderResources(0, _shaderResourceViews.Length);
_prevSamplerStates = vsStage.GetSamplers(0, _samplerStates.Length);
_prevVertexShader = vsStage.Get();
_prevPixelShader = psStage.Get();
// Shared resources shared by all draw calls
var resourcesMissing = false;
SamplerStates.GetValues(ref _samplerStates, context);
var vs = VertexShader.GetValue(context);
var ps = PixelShader.GetValue(context);
resourcesMissing |= (vs == null || ps == null);
resourcesMissing |= TryGetAndApplySrv(context, PrefilteredSpecular, PrefilteredSpecularIndex);
resourcesMissing |= TryGetAndApplySrv(context, BrdfLookup, BrdfLookupIndex);
resourcesMissing |= TryGetAndApplyBuffer(context, TransformBuffer, TransformBufferIndex);
resourcesMissing |= TryGetAndApplyBuffer(context, FloatParameterBuffer, FloatParameterBufferIndex);
resourcesMissing |= TryGetAndApplyBuffer(context, FogParameterBuffer, FogParameterBufferIndex);
resourcesMissing |= TryGetAndApplyBuffer(context, PointLightBuffer, PointLightBufferIndex);
// Draw...
if (resourcesMissing)
{
Log.Warning("Resources missing", this);
}
else
{
vsStage.Set(vs);
vsStage.SetSamplers(0, _samplerStates.Length, _samplerStates);
psStage.Set(ps);
psStage.SetSamplers(0, _samplerStates.Length, _samplerStates);
TryDrawNodes(context, psStage, vsStage);
}
// Restore
vsStage.Set(_prevVertexShader);
vsStage.SetConstantBuffers(0, _prevConstantBuffers.Length, _prevConstantBuffers);
vsStage.SetShaderResources(0, _prevShaderResourceViews.Length, _prevShaderResourceViews);
psStage.Set(_prevPixelShader);
psStage.SetConstantBuffers(0, _prevConstantBuffers.Length, _prevConstantBuffers);
psStage.SetShaderResources(0, _prevShaderResourceViews.Length, _prevShaderResourceViews);
psStage.SetSamplers(0, _prevSamplerStates.Length, _prevSamplerStates);
}
[SuppressMessage("Performance", "CA1822:Mark members as static")]
private void TryDrawNodes(EvaluationContext context, SharpDX.Direct3D11.PixelShaderStage psStage, SharpDX.Direct3D11.VertexShaderStage vsStage)
{
var sceneSetup = SceneSetup.GetValue(context);
if (sceneSetup?.Dispatches == null)
{
Log.Warning("undefined or empty scene setup");
return;
}
PbrMaterial lastMaterial = null;
foreach (var dispatch in sceneSetup.Dispatches)
{
// Resources changing with each draw call
var resourcesMissing = false;
var material = (dispatch.Material == null || !_useSceneMaterials) ? context.PbrMaterial : dispatch.Material;
//var material = context.PbrMaterial;
if (material != lastMaterial)
{
_constantBuffers[PbrParameterBufferIndex] = material.ParameterBuffer;
resourcesMissing |= TestAndSet(material?.NormalSrv, NormalMapIndex);
resourcesMissing |= TestAndSet(material?.AlbedoMapSrv, AlbedoMapIndex);
resourcesMissing |= TestAndSet(material?.EmissiveMapSrv, EmissiveMapIndex);
resourcesMissing |= TestAndSet(material?.RoughnessMetallicOcclusionSrv, RmoColorMapIndex);
lastMaterial = material;
}
// Compute local transform matrix
var m = Matrix4x4.Multiply(dispatch.CombinedTransform,context.ObjectToWorld);
var transformBufferContent = new TransformBufferLayout(context.CameraToClipSpace, context.WorldToCamera, m);
//var transformBufferContent = new TransformBufferLayout(context.CameraToClipSpace, context.WorldToCamera, context.ObjectToWorld);
ResourceManager.SetupConstBuffer(transformBufferContent, ref _constantBuffers[TransformBufferIndex]);
psStage.SetConstantBuffers(0, ConstantBufferIndexCount, _constantBuffers);
vsStage.SetConstantBuffers(0, ConstantBufferIndexCount, _constantBuffers);
resourcesMissing |= TestAndSet(dispatch.MeshBuffers?.VertexBuffer?.Srv, MeshVerticesIndex);
resourcesMissing |= TestAndSet(dispatch.MeshBuffers?.IndicesBuffer?.Srv, MeshIndicesIndex);
if (resourcesMissing)
{
Log.Debug($"Skipping draw call for {dispatch.Material.Name} because we're missing some resources",this);
continue;
}
vsStage.SetShaderResources(0, _shaderResourceViews.Length, _shaderResourceViews);
psStage.SetShaderResources(0, _shaderResourceViews.Length, _shaderResourceViews);
// Log.Debug($"Dispatching draw for {dispatch.VertexCount} vertices...");
ResourceManager.Device.ImmediateContext.Draw(dispatch.VertexCount, dispatch.VertexStartIndex);
}
}
private bool TryGetAndApplySrv(EvaluationContext context, InputSlot<ShaderResourceView> srvInput, int index)
{
var srv = srvInput.GetValue(context);
_shaderResourceViews[index] = srv;
return srv == null;
}
private bool TryGetAndApplyBuffer(EvaluationContext context, InputSlot<Buffer> bufferInput, int index)
{
var buffer = bufferInput.GetValue(context);
_constantBuffers[index] = buffer;
return buffer == null;
}
private bool TestAndSet(ShaderResourceView srv, int index)
{
_shaderResourceViews[index] = srv;
return srv == null;
}
private const int MeshVerticesIndex = 0;
private const int MeshIndicesIndex = 1;
private const int AlbedoMapIndex = 2;
private const int EmissiveMapIndex = 3;
private const int RmoColorMapIndex = 4;
private const int NormalMapIndex = 5;
private const int PrefilteredSpecularIndex = 6;
private const int BrdfLookupIndex = 7;
private const int SrvIndexCount = 8;
private const int TransformBufferIndex = 0;
private const int FloatParameterBufferIndex = 1;
private const int FogParameterBufferIndex = 2;
private const int PointLightBufferIndex = 3;
private const int PbrParameterBufferIndex = 4; // Coming from PbrMaterial
private const int ConstantBufferIndexCount = 5;
private readonly ShaderResourceView[] _shaderResourceViews = new ShaderResourceView[SrvIndexCount];
private readonly Buffer[] _constantBuffers = new Buffer[ConstantBufferIndexCount];
private SamplerState[] _samplerStates = Array.Empty<SamplerState>();
private SharpDX.Direct3D11.PixelShader _prevPixelShader;
private SharpDX.Direct3D11.VertexShader _prevVertexShader;
private SamplerState[] _prevSamplerStates = Array.Empty<SamplerState>();
private Buffer[] _prevConstantBuffers;
private ShaderResourceView[] _prevShaderResourceViews;
private bool _useSceneMaterials;
[Input(Guid = "DAD22148-B87F-439A-9219-785BEE63991C")]
public readonly InputSlot<SceneSetup> SceneSetup = new();
[Input(Guid = "7a9ae929-7001-42ef-b7f2-f2e03bbb7206")]
public readonly InputSlot<T3.Core.DataTypes.VertexShader> VertexShader = new();
[Input(Guid = "59864DA4-3658-4D7D-830E-6EF0D3CBB505")]
public readonly InputSlot<T3.Core.DataTypes.PixelShader> PixelShader = new();
[Input(Guid = "27405ECF-4994-4E67-8604-D86426345344")]
public readonly InputSlot<ShaderResourceView> PrefilteredSpecular = new();
[Input(Guid = "3C19533B-C59A-470B-8712-073E71813C0C")]
public readonly InputSlot<ShaderResourceView> BrdfLookup = new();
[Input(Guid = "60bae25c-64fe-40df-a2e6-a99297a92e0b")]
public readonly MultiInputSlot<SamplerState> SamplerStates = new();
[Input(Guid = "AF6C8349-F48E-4C5A-87FC-33421E255117")]
public readonly InputSlot<Buffer> TransformBuffer = new();
[Input(Guid = "883AEABC-19C8-4754-BB3B-9152B491FFA7")]
public readonly InputSlot<Buffer> FloatParameterBuffer = new();
[Input(Guid = "8DC3E6E7-0EEF-4082-9DC8-4379A901E56B")]
public readonly InputSlot<Buffer> FogParameterBuffer = new();
[Input(Guid = "DB81BA63-8B7B-425C-9D6A-8F3CAF0BE70F")]
public readonly InputSlot<Buffer> PointLightBuffer = new();
[Input(Guid = "46AEE96F-A74E-491C-AE02-843EF62124F7")]
public readonly InputSlot<bool> UseSceneMaterials = new();
}