From 164d420a25aa30465779a2f9fff04be4b54e0fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Thu, 26 May 2022 00:34:16 +0200 Subject: [PATCH] DX11: Automatically validate input layouts against vertex shaders --- GlitchyEditor/src/EditorLayer.bf | 4 +- GlitchyEngine/src/Content/ModelLoader.bf | 16 +++--- .../DX11/Renderer/Dx11GeometryBinding.bf | 8 +-- .../DX11/Renderer/Dx11GraphicsContext.bf | 45 ++++++++++++++++- .../Platform/DX11/Renderer/Dx11RendererAPI.bf | 2 +- .../DX11/Renderer/Dx11VertexLayout.bf | 50 ++++++++++++------- GlitchyEngine/src/Renderer/GraphicsContext.bf | 2 + GlitchyEngine/src/Renderer/Renderer.bf | 4 +- GlitchyEngine/src/Renderer/Renderer2D.bf | 6 +-- GlitchyEngine/src/Renderer/VertexLayout.bf | 9 ++-- Sandbox/src/ExampleLayer.bf | 4 +- 11 files changed, 101 insertions(+), 49 deletions(-) diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index 6a2a61e..389d8c9 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -295,7 +295,7 @@ namespace GlitchyEditor mat.SetVariable("MetallicFactor", 1.0f); mat.SetVariable("RoughnessFactor", 1.0f); - EcsEntity e = ModelLoader.LoadModel("content/Models/sphere.glb", myEffect, mat, _scene.[Friend]_ecsWorld, clips, "Sphere 1"); + EcsEntity e = ModelLoader.LoadModel("content/Models/sphere.glb", mat, _scene.[Friend]_ecsWorld, clips, "Sphere 1"); Entity entity = .(e, _scene); var transform = entity.GetComponent(); @@ -342,7 +342,7 @@ namespace GlitchyEditor mat.SetVariable("RoughnessFactor", (x + 1) / 10.0f); mat.SetVariable("MetallicFactor", y / 9.0f); - EcsEntity e = ModelLoader.LoadModel("content/Models/sphere.glb", myEffect, mat, _scene.[Friend]_ecsWorld, clips, scope $"Sphere {x} {y}"); + EcsEntity e = ModelLoader.LoadModel("content/Models/sphere.glb", mat, _scene.[Friend]_ecsWorld, clips, scope $"Sphere {x} {y}"); Entity entity = .(e, _scene); var transform = entity.GetComponent(); diff --git a/GlitchyEngine/src/Content/ModelLoader.bf b/GlitchyEngine/src/Content/ModelLoader.bf index 9f5bfc3..6244e10 100644 --- a/GlitchyEngine/src/Content/ModelLoader.bf +++ b/GlitchyEngine/src/Content/ModelLoader.bf @@ -12,7 +12,7 @@ namespace GlitchyEngine.Content { static readonly Matrix RightToLeftHand = .Scaling(1, 1, -1); - public static EcsEntity LoadModel(String filename, Effect validationEffect, Material material, EcsWorld world, + public static EcsEntity LoadModel(String filename, Material material, EcsWorld world, List outClips, StringView entityName = StringView()) { CGLTF.Options options = .(); @@ -29,7 +29,7 @@ namespace GlitchyEngine.Content for(var node in data.Scenes[0].Nodes) { - NodesToEntities(data, node, entity, world, validationEffect, material, outClips); + NodesToEntities(data, node, entity, world, material, outClips); } CGLTF.Free(data); @@ -63,7 +63,7 @@ namespace GlitchyEngine.Content return (entity, childTransform); } - private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, EcsEntity parentEntity, EcsWorld world, Effect validationEffect, Material material, List clips) + private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, EcsEntity parentEntity, EcsWorld world, Material material, List clips) { (EcsEntity entity, TransformComponent* childTransform) = CreateEntity(world, node.Name == null ? null : StringView(node.Name), parentEntity); @@ -109,7 +109,7 @@ namespace GlitchyEngine.Content { var mesh = world.AssignComponent(entity); - using (var geo = PrimitiveToGeoBinding(node.Mesh.Primitives[0], validationEffect)) + using (var geo = PrimitiveToGeoBinding(node.Mesh.Primitives[0])) { mesh.Mesh = geo; } @@ -137,7 +137,7 @@ namespace GlitchyEngine.Content meshParent.Entity = entity; var mesh = world.AssignComponent(meshEntity); - mesh.Mesh = PrimitiveToGeoBinding(primitive, validationEffect); + mesh.Mesh = PrimitiveToGeoBinding(primitive); if(skeleton == null) { @@ -158,11 +158,11 @@ namespace GlitchyEngine.Content for(var child in node.Children) { - NodesToEntities(data, child, entity, world, validationEffect, material, clips); + NodesToEntities(data, child, entity, world, material, clips); } } - public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive, Effect validationEffect) + public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive) { GeometryBinding binding = new GeometryBinding(); @@ -327,7 +327,7 @@ namespace GlitchyEngine.Content vertexElements[i] = elements[i]; } - VertexLayout layout = new VertexLayout(vertexElements, true, validationEffect.VertexShader); + VertexLayout layout = new VertexLayout(vertexElements, true); binding.SetVertexLayout(layout..ReleaseRefNoDelete()); } diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GeometryBinding.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GeometryBinding.bf index ceae99e..ffd373a 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GeometryBinding.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GeometryBinding.bf @@ -15,7 +15,6 @@ namespace GlitchyEngine.Renderer internal uint32[DirectX.D3D11.D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT] bufferStrides; internal uint32[DirectX.D3D11.D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT] bufferOffsets; - internal ID3D11InputLayout* nativeVertexLayout; internal ID3D11Buffer* nativeIndexBuffer; public ~this() @@ -25,7 +24,6 @@ namespace GlitchyEngine.Renderer buffer?.Release(); } - nativeVertexLayout?.Release(); nativeIndexBuffer?.Release(); } @@ -61,10 +59,6 @@ namespace GlitchyEngine.Renderer protected override void PlatformSetVertexLayout(VertexLayout vertexLayout) { - Debug.Profiler.ProfileResourceFunction!(); - - nativeVertexLayout?.Release(); - nativeVertexLayout = vertexLayout?.nativeLayout..AddRef(); } protected override void PlatformSetIndexBuffer(IndexBuffer indexBuffer) @@ -88,7 +82,7 @@ namespace GlitchyEngine.Renderer Debug.Profiler.ProfileRendererFunction!(); NativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nativeBuffers, &bufferStrides, &bufferOffsets); - NativeContext.InputAssembler.SetInputLayout(_vertexLayout.nativeLayout); + GraphicsContext.Get().SetVertexLayout(_vertexLayout); NativeContext.InputAssembler.SetPrimitiveTopology((.)_primitiveTopology); if(_indexBuffer != null) diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf index 7b9bc2a..8850c39 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf @@ -34,6 +34,11 @@ namespace GlitchyEngine.Renderer private const uint32 MaxRTVCount = DirectX.D3D11.D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; + // current vertex layout and vertex shader needed for validation. + ID3D11InputLayout* _currentInputLayout ~ _?.Release(); + VertexLayout _currentVertexLayout ~ _?.ReleaseRef(); + VertexShader _currentVertexShader ~ _?.ReleaseRef(); + //public static override uint32 MaxRenderTargetCount() => MaxRTVCount; public this(Windows.HWnd windowHandle) @@ -168,16 +173,39 @@ namespace GlitchyEngine.Renderer NativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset); } + [Inline] + private void BindInputLayout() + { + if (_currentInputLayout == null) + { + _currentInputLayout = _currentVertexLayout.GetNativeVertexLayout(_currentVertexShader.nativeCode); + _currentInputLayout.AddRef(); + + NativeContext.InputAssembler.SetInputLayout(_currentInputLayout); + } + } + public override void Draw(uint32 vertexCount, uint32 startVertexIndex = 0) { + BindInputLayout(); + NativeContext.Draw(vertexCount, startVertexIndex); } public override void DrawIndexed(uint32 indexCount, uint32 startIndexLocation = 0, int32 vertexOffset = 0) { + BindInputLayout(); + NativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset); } + public override void DrawIndexedInstanced(uint32 indexCountPerInstance, uint32 instanceCount, uint32 startIndexLocation, int32 baseVertexLocation, uint32 startInstanceLocation) + { + BindInputLayout(); + + NativeContext.DrawIndexedInstanced(indexCountPerInstance, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation); + } + public override void SetIndexBuffer(Buffer buffer, IndexFormat indexFormat = .Index16Bit, uint32 byteOffset = 0) { NativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset); @@ -190,7 +218,12 @@ namespace GlitchyEngine.Renderer public override void SetVertexLayout(VertexLayout vertexLayout) { - NativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout); + if (_currentVertexLayout != vertexLayout) + { + SetReference!(_currentVertexLayout, vertexLayout); + _currentInputLayout?.Release(); + _currentInputLayout = null; + } } public override void SetPrimitiveTopology(GlitchyEngine.Renderer.PrimitiveTopology primitiveTopology) @@ -252,7 +285,7 @@ namespace GlitchyEngine.Renderer } shader.Buffers.PlatformFetchNativeBuffers(); - + switch(typeof(TShader)) { // TODO: Add remaining shader stages @@ -262,6 +295,14 @@ namespace GlitchyEngine.Renderer case typeof(VertexShader): NativeContext.VertexShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers); NativeContext.VertexShader.SetShader((ID3D11VertexShader*)shader.nativeShader); + + if (VertexShader vs = shader as VertexShader) + { + SetReference!(_currentVertexShader, vs); + _currentInputLayout?.Release(); + _currentInputLayout = null; + } + default: Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented."); } diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf index c1cc715..498867c 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf @@ -216,7 +216,7 @@ namespace GlitchyEngine.Renderer { Debug.Profiler.ProfileRendererFunction!(); - NativeContext.DrawIndexedInstanced(geometry.IndexCount, geometry.InstanceCount, geometry.IndexByteOffset, 0, 0); + _context.DrawIndexedInstanced(geometry.IndexCount, geometry.InstanceCount, geometry.IndexByteOffset, 0, 0); } public override void SetViewport(Viewport viewport) diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11VertexLayout.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11VertexLayout.bf index d439a91..3f717fc 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11VertexLayout.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11VertexLayout.bf @@ -5,6 +5,7 @@ using System.Diagnostics; using DirectX.Common; using DirectX.D3D11; using GlitchyEngine.Platform.DX11; +using System.Collections; using internal GlitchyEngine.Renderer; using internal GlitchyEngine.Platform.DX11; @@ -13,19 +14,19 @@ namespace GlitchyEngine.Renderer { public extension VertexLayout { - internal ID3D11InputLayout* nativeLayout ~ _?.Release(); + private Dictionary _validatedShaders = new .() ~ + { + if (_ != null) + { + for (let entry in _) + { + entry.key.Release(); + entry.value.Release(); + } - public ID3DBlob* nativeShaderCode ~ _?.Release(); - - public this(VertexElement[] elements, bool ownsElements, VertexShader vertexShader) - { - nativeShaderCode = vertexShader.nativeCode..AddRef(); - - _elements = elements; - _ownsElements = ownsElements; - - CreateNativeLayout(); - } + delete _; + } + }; private void ToNativeLayout(VertexElement[] input, InputElementDescription[] output) { @@ -35,19 +36,30 @@ namespace GlitchyEngine.Renderer output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass, input[i].InstanceDataStepRate); } - protected override void CreateNativeLayout() + /// Validates or gets the validated input layout for the given vertexshader. + internal ID3D11InputLayout* GetNativeVertexLayout(ID3DBlob* vertexShaderCode) { Debug.Profiler.ProfileResourceFunction!(); - var nativeElements = scope InputElementDescription[_elements.Count]; + ID3D11InputLayout* layout = null; - ToNativeLayout(_elements, nativeElements); - - var result = NativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout); - if(result.Failed) + if (!_validatedShaders.TryGetValue(vertexShaderCode, out layout)) { - Log.EngineLogger.Error($"Failed to create D3D11 input layout: Message({(int)result}): {result}"); + var nativeElements = scope InputElementDescription[_elements.Count]; + + ToNativeLayout(_elements, nativeElements); + + var result = NativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, vertexShaderCode.GetBufferPointer(), vertexShaderCode.GetBufferSize(), &layout); + if(result.Failed) + { + Log.EngineLogger.Error($"Failed to create D3D11 input layout: Message({(int)result}): {result}"); + Debug.FatalError(); + } + + _validatedShaders[vertexShaderCode..AddRef()] = layout; } + + return layout; } } } diff --git a/GlitchyEngine/src/Renderer/GraphicsContext.bf b/GlitchyEngine/src/Renderer/GraphicsContext.bf index d9c08dd..5225707 100644 --- a/GlitchyEngine/src/Renderer/GraphicsContext.bf +++ b/GlitchyEngine/src/Renderer/GraphicsContext.bf @@ -79,6 +79,8 @@ namespace GlitchyEngine.Renderer public extern void Draw(uint32 vertexCount, uint32 startVertexIndex = 0); public extern void DrawIndexed(uint32 indexCount, uint32 startIndexLocation = 0, int32 vertexOffset = 0); + + public extern void DrawIndexedInstanced(uint32 indexCountPerInstance, uint32 instanceCount, uint32 startIndexLocation, int32 baseVertexLocation, uint32 startInstanceLocation); public void SetIndexBuffer(IndexBuffer indexBuffer, uint32 byteOffset = 0) { diff --git a/GlitchyEngine/src/Renderer/Renderer.bf b/GlitchyEngine/src/Renderer/Renderer.bf index 716aa36..6fc67d8 100644 --- a/GlitchyEngine/src/Renderer/Renderer.bf +++ b/GlitchyEngine/src/Renderer/Renderer.bf @@ -210,7 +210,7 @@ namespace GlitchyEngine.Renderer VertexElement[] vertexElements = new VertexElement[1]; vertexElements[0] = .(.R32G32B32_Float, "POSITION"); - VertexLayout layout = new VertexLayout(vertexElements, true, LineEffect.VertexShader); + VertexLayout layout = new VertexLayout(vertexElements, true); LineGeometry.SetVertexLayout(layout..ReleaseRefNoDelete()); } @@ -258,7 +258,7 @@ namespace GlitchyEngine.Renderer VertexElement(.R32G32_Float, "TEXCOORD") ); - using (var quadBatchLayout = new VertexLayout(vertexElements, true, TestFullscreenEffect.VertexShader)) + using (var quadBatchLayout = new VertexLayout(vertexElements, true)) { s_fullscreenQuadGeometry.SetVertexLayout(quadBatchLayout); } diff --git a/GlitchyEngine/src/Renderer/Renderer2D.bf b/GlitchyEngine/src/Renderer/Renderer2D.bf index 1fccfc6..e18477d 100644 --- a/GlitchyEngine/src/Renderer/Renderer2D.bf +++ b/GlitchyEngine/src/Renderer/Renderer2D.bf @@ -184,7 +184,7 @@ namespace GlitchyEngine.Renderer 0, 1, 2, 2, 3, 0 ); - + quadIndices.SetData(indices); s_quadGeometry.SetIndexBuffer(quadIndices); } @@ -211,7 +211,7 @@ namespace GlitchyEngine.Renderer s_quadBatchBinding = new GeometryBinding(); s_quadBatchBinding.SetPrimitiveTopology(.TriangleList); - using (var quadBatchLayout = new VertexLayout(vertexElements, true, s_batchEffect.VertexShader)) + using (var quadBatchLayout = new VertexLayout(vertexElements, true)) { s_quadBatchBinding.SetVertexLayout(quadBatchLayout); } @@ -238,7 +238,7 @@ namespace GlitchyEngine.Renderer s_circleBatchBinding = new GeometryBinding(); s_circleBatchBinding.SetPrimitiveTopology(.TriangleList); - using (var circleBatchLayout = new VertexLayout(vertexElements, true, s_circleBatchEffect.VertexShader)) + using (var circleBatchLayout = new VertexLayout(vertexElements, true)) { s_circleBatchBinding.SetVertexLayout(circleBatchLayout); } diff --git a/GlitchyEngine/src/Renderer/VertexLayout.bf b/GlitchyEngine/src/Renderer/VertexLayout.bf index 9f83d7e..5568284 100644 --- a/GlitchyEngine/src/Renderer/VertexLayout.bf +++ b/GlitchyEngine/src/Renderer/VertexLayout.bf @@ -3,7 +3,6 @@ using GlitchyEngine.Core; namespace GlitchyEngine.Renderer { - /** * Type of data contained in an input slot. */ @@ -86,6 +85,12 @@ namespace GlitchyEngine.Renderer public VertexElement[] Elements => _elements; + public this(VertexElement[] elements, bool ownsElements) + { + _elements = elements; + _ownsElements = ownsElements; + } + public ~this() { if(_ownsElements) @@ -99,8 +104,6 @@ namespace GlitchyEngine.Renderer delete _elements; } } - - protected extern void CreateNativeLayout(); } public interface IVertexData diff --git a/Sandbox/src/ExampleLayer.bf b/Sandbox/src/ExampleLayer.bf index 064a780..4311572 100644 --- a/Sandbox/src/ExampleLayer.bf +++ b/Sandbox/src/ExampleLayer.bf @@ -97,7 +97,7 @@ namespace Sandbox // Create Input Layout - VertexLayout vertexLayout = new VertexLayout(VertexColorTexture.VertexElements, false, textureEffect.VertexShader); + VertexLayout vertexLayout = new VertexLayout(VertexColorTexture.VertexElements, false); textureEffect.ReleaseRef(); @@ -233,7 +233,7 @@ namespace Sandbox materialTestMaterial.SetVariable("BaseColor", Color.White); materialTestMaterial.SetVariable("LightDir", Vector3(1, 1, -0.5f).Normalized()); - ModelLoader.LoadModel("content\\Models\\RiggedFigure\\RiggedFigure.glb", testEffect, materialTestMaterial, _world, Clips); + ModelLoader.LoadModel("content\\Models\\RiggedFigure\\RiggedFigure.glb", materialTestMaterial, _world, Clips); materialTestMaterial.ReleaseRef(); testEffect.ReleaseRef();