using T3.Core.Rendering; using T3.Core.Rendering.Material; namespace Lib.render.shading._; /// /// 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. /// /// [Guid("6e32756e-4267-47f1-bad0-56ee8f58b070")] internal sealed class _DispatchSceneDraws : Instance<_DispatchSceneDraws> { [Output(Guid = "bda1583a-f5ea-4c3b-ae7d-0bac614d29ec")] public readonly Slot 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 srvInput, int index) { var srv = srvInput.GetValue(context); _shaderResourceViews[index] = srv; return srv == null; } private bool TryGetAndApplyBuffer(EvaluationContext context, InputSlot 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(); private SharpDX.Direct3D11.PixelShader _prevPixelShader; private SharpDX.Direct3D11.VertexShader _prevVertexShader; private SamplerState[] _prevSamplerStates = Array.Empty(); private Buffer[] _prevConstantBuffers; private ShaderResourceView[] _prevShaderResourceViews; private bool _useSceneMaterials; [Input(Guid = "DAD22148-B87F-439A-9219-785BEE63991C")] public readonly InputSlot SceneSetup = new(); [Input(Guid = "7a9ae929-7001-42ef-b7f2-f2e03bbb7206")] public readonly InputSlot VertexShader = new(); [Input(Guid = "59864DA4-3658-4D7D-830E-6EF0D3CBB505")] public readonly InputSlot PixelShader = new(); [Input(Guid = "27405ECF-4994-4E67-8604-D86426345344")] public readonly InputSlot PrefilteredSpecular = new(); [Input(Guid = "3C19533B-C59A-470B-8712-073E71813C0C")] public readonly InputSlot BrdfLookup = new(); [Input(Guid = "60bae25c-64fe-40df-a2e6-a99297a92e0b")] public readonly MultiInputSlot SamplerStates = new(); [Input(Guid = "AF6C8349-F48E-4C5A-87FC-33421E255117")] public readonly InputSlot TransformBuffer = new(); [Input(Guid = "883AEABC-19C8-4754-BB3B-9152B491FFA7")] public readonly InputSlot FloatParameterBuffer = new(); [Input(Guid = "8DC3E6E7-0EEF-4082-9DC8-4379A901E56B")] public readonly InputSlot FogParameterBuffer = new(); [Input(Guid = "DB81BA63-8B7B-425C-9D6A-8F3CAF0BE70F")] public readonly InputSlot PointLightBuffer = new(); [Input(Guid = "46AEE96F-A74E-491C-AE02-843EF62124F7")] public readonly InputSlot UseSceneMaterials = new(); }