From 76087d6ba472593a121da47c9574c682c138f82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sun, 9 Feb 2025 01:39:35 +0100 Subject: [PATCH] Materials kind of working and used in some places - Also Texture viewer fixes --- BeefSpace.toml | 2 +- GlitchyEditor/BeefProj.toml | 3 + .../Assets/Processors/MaterialProcessor.bf | 2 +- GlitchyEditor/src/EditWindows/AssetViewer.bf | 145 +++++---- .../src/Content/Loaders/ShaderLoader.bf | 2 +- GlitchyEngine/src/ImGui/ImGuiLayer.bf | 2 + .../DX11/Renderer/Dx11BufferCollection.bf | 2 +- .../DX11/Renderer/Dx11GraphicsContext.bf | 100 ++++-- .../Platform/DX11/Renderer/Dx11RendererAPI.bf | 10 + .../src/Renderer/BufferCollection.bf | 2 + GlitchyEngine/src/Renderer/BufferVariable.bf | 79 ++++- GlitchyEngine/src/Renderer/ConstantBuffer.bf | 32 +- GlitchyEngine/src/Renderer/Effect.bf | 2 +- GlitchyEngine/src/Renderer/GraphicsContext.bf | 4 + GlitchyEngine/src/Renderer/Material.bf | 302 +++++++----------- .../src/Renderer/OverridingConstantBuffer.bf | 106 ++++++ GlitchyEngine/src/Renderer/RenderCommand.bf | 10 + GlitchyEngine/src/Renderer/Renderer2D.bf | 79 +++-- GlitchyEngine/src/Renderer/RendererAPI.bf | 4 + .../src/Renderer/Text/FontRenderer.bf | 4 +- GlitchyEngine/src/World/SceneRenderer.bf | 2 + 21 files changed, 572 insertions(+), 322 deletions(-) create mode 100644 GlitchyEngine/src/Renderer/OverridingConstantBuffer.bf diff --git a/BeefSpace.toml b/BeefSpace.toml index b696ccc..8b72823 100644 --- a/BeefSpace.toml +++ b/BeefSpace.toml @@ -1,6 +1,6 @@ FileVersion = 1 Projects = {GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}, FreeType = {Path = "GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "GlitchyEngine/vendor/gltf/cgltf-beef"}, GlitchyEditor = {Path = "GlitchyEditor"}, msdfgen-beef = {Path = "GlitchyEngine/vendor/msdfgen/msdfgen-beef"}, ImGui = {Path = "GlitchyEngine/vendor/imgui/ImGui"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplDX11"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplWin32"}, ImGuizmo = {Path = "GlitchyEngine/vendor/imgui/ImGuizmo"}, GlitchyEngineHelper = {Path = "GlitchyEngineHelper"}, bon = {Path = "GlitchyEngine/vendor/bon"}, box2d-beef = {Path = "GlitchyEngine/vendor/box2D"}, "Beef.Linq" = {Path = "GlitchyEngine/vendor/Beef.Linq/src"}, ScriptCore = {Path = "ScriptCore"}} -WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper", "ScriptCore"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq"]} +WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper", "ScriptCore"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq"], Beef = ["Beefy2D", "corlib", "MiniZ"]} [Workspace] StartupProject = "GlitchyEditor" diff --git a/GlitchyEditor/BeefProj.toml b/GlitchyEditor/BeefProj.toml index 2cd40b7..7363e10 100644 --- a/GlitchyEditor/BeefProj.toml +++ b/GlitchyEditor/BeefProj.toml @@ -7,6 +7,9 @@ TargetType = "BeefGUIApplication" StartupObject = "GlitchyEngine.Program" ProcessorMacros = ["GE_EDITOR_IMGUI_DEMO"] +[Platform.Windows] +IconFile = "$(ProjectDir)/Resources/Textures/GlitchyEngineIcon.ico" + [Configs.Debug.Win64] PostBuildCmds = ["CopyFilesIfNewer(\"$(WorkspaceDir)/bin/vswhere.exe\", \"$(TargetDir)\")"] DebugCommandArguments = "\"D:\\Development\\Git\\SingleStateToOrbit\"" diff --git a/GlitchyEditor/src/Assets/Processors/MaterialProcessor.bf b/GlitchyEditor/src/Assets/Processors/MaterialProcessor.bf index 47422e2..853c0f5 100644 --- a/GlitchyEditor/src/Assets/Processors/MaterialProcessor.bf +++ b/GlitchyEditor/src/Assets/Processors/MaterialProcessor.bf @@ -84,7 +84,7 @@ class MaterialProcessor : IAssetProcessor { Log.EngineLogger.Error("The buffers of a material must be ConstantBuffers."); continue; - // TODO: Why even allow any kind of Buffer for Materials? Will this make sense later if we can create and bind Buffers from C#? + // TODO: Why even allow any other kind of Buffer for Materials? Will this make sense later if we can create and bind Buffers from C#? } Span data = processedMaterial.CloneBuffer(buffer.Name, cBuffer); diff --git a/GlitchyEditor/src/EditWindows/AssetViewer.bf b/GlitchyEditor/src/EditWindows/AssetViewer.bf index 07d3c19..137b398 100644 --- a/GlitchyEditor/src/EditWindows/AssetViewer.bf +++ b/GlitchyEditor/src/EditWindows/AssetViewer.bf @@ -14,7 +14,7 @@ class AssetViewer : EditorWindow private AssetHandle _selectedAsset; - append TexturererViewerer _textureViewer = .(); + append TextureViewer _textureViewer = .(); public this(EditorContentManager contentManager) { @@ -133,7 +133,7 @@ class AssetViewer : EditorWindow } } -class TexturererViewerer +class TextureViewer { enum BackgroundMode : int32 { @@ -160,7 +160,8 @@ class TexturererViewerer } AssetHandle _effect; - AssetHandle _renderTargetEffect; + //AssetHandle _renderTargetEffect; + Material _renderTargetMaterial ~ _.ReleaseRef(); float _zoom = 1.0f; @@ -169,14 +170,28 @@ class TexturererViewerer SampleMode _sampleMode = .Linear; - RenderTarget2D _target ~ _?.ReleaseRef(); + RenderTargetGroup _targets ~ _?.ReleaseRef(); + /*RenderTarget2D _target ~ _?.ReleaseRef(); + RenderTarget2D _idTarget ~ _?.ReleaseRef(); // TODO: we don't need depth! - DepthStencilTarget _depth ~ _?.ReleaseRef(); + DepthStencilTarget _depth ~ _?.ReleaseRef();*/ public this() { _effect = Content.LoadAsset("Resources/Shaders/textureViewerShader.hlsl", null, true); - _renderTargetEffect = Content.LoadAsset("Resources/Shaders/RenderTargetGroupViewer.hlsl", null, true); + AssetHandle effect = Content.LoadAsset("Resources/Shaders/RenderTargetGroupViewer.hlsl", null, true); + + _renderTargetMaterial = new Material(effect); + + _targets = new RenderTargetGroup(.(){ + Width = 100, + Height = 100, + ColorTargetDescriptions = TargetDescription[]( + .(.R8G8B8A8_UNorm, clearColor: .Color(ColorRGBA.Pink)), + .(.R32_UInt) + ), + DepthTargetDescription = .(.D24_UNorm_S8_UInt) + }); } float2 _position; @@ -215,7 +230,8 @@ class TexturererViewerer if (_backgroundMode == .CustomColor) { - ImGui.ColorPicker4("Color", ref _backgroundColor); + ImGui.ColorPicker3("Color", ref _backgroundColor); + _backgroundColor.A = 1.0f; } } @@ -365,21 +381,17 @@ class TexturererViewerer viewportSize.x = Math.Max(viewportSize.x, 1); viewportSize.y = Math.Max(viewportSize.y, 1); - - if(_target == null || viewportSize.x != _target.Width || viewportSize.y != _target.Height) + + if (_targets.Width != viewportSize.x || _targets.Height != viewportSize.y) { - _target?.ReleaseRef(); - _target = new RenderTarget2D(.(.R8G8B8A8_UNorm_SRGB, (.)viewportSize.x, (.)viewportSize.y)); - _target.SamplerState = SamplerStateManager.PointClamp; - _depth?.ReleaseRef(); - _depth = new DepthStencilTarget((.)viewportSize.x, (.)viewportSize.y, .D16_UNorm); + _targets.Resize((.)viewportSize.x, (.)viewportSize.y); } - + RenderBackground(); RenderTexture(texture); - - ImGui.Image(_target, viewportSize); + + ImGui.Image(_targets.GetViewBinding(0), viewportSize); ImGui.EndChild(); } @@ -398,20 +410,19 @@ class TexturererViewerer viewportSize.x = Math.Max(viewportSize.x, 1); viewportSize.y = Math.Max(viewportSize.y, 1); - if(_target == null || viewportSize.x != _target.Width || viewportSize.y != _target.Height) + if (_targets.Width != viewportSize.x || _targets.Height != viewportSize.y) { - _target?.ReleaseRef(); - _target = new RenderTarget2D(.(.R8G8B8A8_UNorm_SRGB, (.)viewportSize.x, (.)viewportSize.y)); - _target.SamplerState = SamplerStateManager.PointClamp; - _depth?.ReleaseRef(); - _depth = new DepthStencilTarget((.)viewportSize.x, (.)viewportSize.y, .D16_UNorm); + _targets.Resize((.)viewportSize.x, (.)viewportSize.y); } RenderBackground(); RenderTexture(texture); - - ImGui.Image(_target, viewportSize); + + using (let vb = _targets.GetViewBinding(0)) + { + ImGui.Image(vb, viewportSize); + } ImGui.EndChild(); } @@ -468,19 +479,21 @@ class TexturererViewerer private void RenderBackground() { - Viewport vp = .(0, 0, _target.Width, _target.Height); + Viewport vp = .(0, 0, _targets.Width, _targets.Height); RenderCommand.SetViewport(vp); + RenderCommand.Clear(_targets, .ColorDepth); // TODO: don't clear pink! - RenderCommand.Clear(_target, .Pink); - RenderCommand.Clear(_depth, .Depth, 1.0f, 0); + //RenderCommand.Clear(_target, .Pink); + //RenderCommand.Clear(_depth, .Depth, 1.0f, 0); //_target.Bind(); - RenderCommand.SetRenderTarget(_target); - RenderCommand.SetDepthStencilTarget(_depth); + RenderCommand.SetRenderTargetGroup(_targets); + //RenderCommand.SetRenderTarget(_idTarget, 1); + //RenderCommand.SetDepthStencilTarget(_depth); RenderCommand.BindRenderTargets(); - float2 targetSize = float2(_target.Width, _target.Height); + float2 targetSize = float2(_targets.Width, _targets.Height); _camera.Left = 0; _camera.Top = 0; @@ -520,7 +533,7 @@ class TexturererViewerer Renderer2D.EndScene(); } - private void RenderTexture(TextureViewBinding viewedTexture, float2 textureSize, Format format) + private void RenderTexture(Asset textureAsset, float2 textureSize, Format format, int32? groupTargetIndex = null) { float2 mippedTextureSize = float2((int)textureSize.X >> _mipLevel, (int)textureSize.X >> _mipLevel); @@ -544,52 +557,70 @@ class TexturererViewerer //Renderer2D.BeginScene(_camera, .SortByTexture, _effect); // float3(_position * .(1, -1), 0) - RenderCommand.Clear(_depth, .Depth, 1.0f, 0); + //RenderCommand.Clear(_depth, .Depth, 1.0f, 0); + RenderCommand.Clear(_targets, .Depth); Matrix matrix = .Translation(float3(_position * .(1, -1), 0)) * .Scaling(float3(zoomedTextureSize, 1)); // TODO: ViewProjection kommt nicht korrekt an? - _renderTargetEffect.Variables["WorldViewProjection"].SetData(_camera.ViewProjection * matrix); + _renderTargetMaterial.SetVariable("WorldViewProjection", _camera.ViewProjection * matrix); + _renderTargetMaterial.SetVariable("ColorOffset", _colorOffset); + _renderTargetMaterial.SetVariable("ColorScale", _colorScale); + _renderTargetMaterial.SetVariable("AlphaOffset", _alphaOffset); + _renderTargetMaterial.SetVariable("AlphaScale", _alphaScale); + /*_renderTargetEffect.Variables["WorldViewProjection"].SetData(_camera.ViewProjection * matrix); _renderTargetEffect.Variables["ColorOffset"].SetData(_colorOffset); _renderTargetEffect.Variables["ColorScale"].SetData(_colorScale); _renderTargetEffect.Variables["AlphaOffset"].SetData(_alphaOffset); - _renderTargetEffect.Variables["AlphaScale"].SetData(_alphaScale); + _renderTargetEffect.Variables["AlphaScale"].SetData(_alphaScale);*/ - _renderTargetEffect.Variables["Texels"].SetData(mippedTextureSize); + //_renderTargetEffect.Variables["Texels"].SetData(mippedTextureSize); + _renderTargetMaterial.SetVariable("Texels", mippedTextureSize); - _renderTargetEffect.Variables["MipLevel"].SetData((float)_mipLevel); + //_renderTargetEffect.Variables["MipLevel"].SetData((float)_mipLevel); + _renderTargetMaterial.SetVariable("MipLevel", (float)_mipLevel); - _renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA)); + //_renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA)); + _renderTargetMaterial.SetVariable("Swizzle", int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA)); - if (((DirectX.DXGI.Format)format).IsInt()) + int textureSlot = -1; + + if (format.IsInt()) { - // Int Texture - _renderTargetEffect.Variables["Mode"].SetData(1); - _renderTargetEffect.SetTexture("IntTexture", viewedTexture); - _renderTargetEffect.SetTexture("UIntTexture", (Texture)null); - _renderTargetEffect.SetTexture("Texture", (Texture)null); + // Int Texture + _renderTargetMaterial.SetVariable("Mode", 1); + textureSlot = _renderTargetMaterial.Effect.Textures["IntTexture"].PsSlot.Index; } else if (format.IsUInt()) { // UInt Texture - _renderTargetEffect.Variables["Mode"].SetData(2); - _renderTargetEffect.SetTexture("IntTexture", (Texture)null); - _renderTargetEffect.SetTexture("UIntTexture", viewedTexture); - _renderTargetEffect.SetTexture("Texture", (Texture)null); + _renderTargetMaterial.SetVariable("Mode", 2); + textureSlot = _renderTargetMaterial.Effect.Textures["UIntTexture"].PsSlot.Index; } else { // Float Texture - _renderTargetEffect.Variables["Mode"].SetData(0); - _renderTargetEffect.SetTexture("IntTexture", (Texture)null); - _renderTargetEffect.SetTexture("UIntTexture", (Texture)null); - _renderTargetEffect.SetTexture("Texture", viewedTexture); + _renderTargetMaterial.SetVariable("Mode", 0); + textureSlot = _renderTargetMaterial.Effect.Textures["Texture"].PsSlot.Index; } - _renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA)); + //_renderTargetMaterial.ApplyChanges(); + _renderTargetMaterial.Bind(); - _renderTargetEffect.ApplyChanges(); - _renderTargetEffect.Bind(); + if (let renderTargetGroup = textureAsset as RenderTargetGroup) + { + using (let viewBinding = renderTargetGroup.GetViewBinding((.)groupTargetIndex)) + { + RenderCommand.BindTexture(viewBinding, textureSlot, .Pixel); + } + } + else if (let texture = textureAsset as Texture) + { + using (let viewBinding = texture.GetViewBinding()) + { + RenderCommand.BindTexture(viewBinding, textureSlot, .Pixel); + } + } Quad.Draw(); } @@ -598,11 +629,11 @@ class TexturererViewerer { var desc = _groupIndex >= 0 ? viewedTexture.[Friend]_colorTargetDescriptions[_groupIndex] : viewedTexture.[Friend]_depthTargetDescription; - RenderTexture(viewedTexture.GetViewBinding(_groupIndex), float2(viewedTexture.Width, viewedTexture.Height), (.)desc.Format.GetShaderViewFormat()); + RenderTexture(viewedTexture, float2(viewedTexture.Width, viewedTexture.Height), (.)desc.Format.GetShaderViewFormat(), _groupIndex); } private void RenderTexture(Texture viewedTexture) { - RenderTexture(viewedTexture.GetViewBinding(), float2(viewedTexture.Width, viewedTexture.Height), viewedTexture.Format); + RenderTexture(viewedTexture, float2(viewedTexture.Width, viewedTexture.Height), viewedTexture.Format, -1); } } diff --git a/GlitchyEngine/src/Content/Loaders/ShaderLoader.bf b/GlitchyEngine/src/Content/Loaders/ShaderLoader.bf index 6c9f78d..3922d04 100644 --- a/GlitchyEngine/src/Content/Loaders/ShaderLoader.bf +++ b/GlitchyEngine/src/Content/Loaders/ShaderLoader.bf @@ -133,7 +133,7 @@ class ShaderLoader : IProcessedAssetLoader } Try!(stream.TryRead(buffer.RawData)); - Try!(buffer.Update()); + Try!(buffer.Apply()); if (vertexShaderBindPoint != -1) effect.VertexShader.Buffers.Add(vertexShaderBindPoint, buffer.Name, buffer); diff --git a/GlitchyEngine/src/ImGui/ImGuiLayer.bf b/GlitchyEngine/src/ImGui/ImGuiLayer.bf index c736ee8..847536b 100644 --- a/GlitchyEngine/src/ImGui/ImGuiLayer.bf +++ b/GlitchyEngine/src/ImGui/ImGuiLayer.bf @@ -233,6 +233,8 @@ namespace GlitchyEngine.ImGui ImGui.Render(); RenderCommand.SetDepthStencilTarget(null); + // TODO: This is dirty + RenderCommand.UnbindRenderTargets(); RenderCommand.SetRenderTarget(null); RenderCommand.BindRenderTargets(); diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11BufferCollection.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11BufferCollection.bf index 74cf6c4..0bb6f8c 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11BufferCollection.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11BufferCollection.bf @@ -12,7 +12,7 @@ namespace GlitchyEngine.Renderer internal ID3D11Buffer*[DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT] nativeBuffers; - internal void PlatformFetchNativeBuffers() + protected internal override void PlatformFetchNativeBuffers() { Debug.Profiler.ProfileRendererFunction!(); diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf index 5f68cb3..873250e 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf @@ -210,10 +210,18 @@ namespace GlitchyEngine.Renderer BindInputLayout(); if (_hasVs) + { + NativeContext.VertexShader.SetShaderResources(0, _vsShaderResources.Count, &_vsShaderResources); + NativeContext.VertexShader.SetSamplers(0, _vsSamplers.Count, &_vsSamplers); NativeContext.VertexShader.SetConstantBuffers(0, _vsBuffers.Count, &_vsBuffers); + } if (_hasPs) + { + NativeContext.PixelShader.SetShaderResources(0, _psShaderResources.Count, &_psShaderResources); + NativeContext.PixelShader.SetSamplers(0, _psSamplers.Count, &_psSamplers); NativeContext.PixelShader.SetConstantBuffers(0, _psBuffers.Count, &_psBuffers); + } } public override void Draw(uint32 vertexCount, uint32 startVertexIndex = 0) @@ -291,6 +299,11 @@ namespace GlitchyEngine.Renderer private ID3D11Buffer*[DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT] _vsBuffers; private ID3D11Buffer*[DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT] _psBuffers; + private ID3D11ShaderResourceView*[DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT] _vsShaderResources; + private ID3D11SamplerState*[DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT] _vsSamplers; + private ID3D11ShaderResourceView*[DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT] _psShaderResources; + private ID3D11SamplerState*[DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT] _psSamplers; + /** * Binds the given shader to the corresponding shader stage. * @param shader The shader that will be bound to the graphics context. @@ -299,24 +312,25 @@ namespace GlitchyEngine.Renderer { Debug.Profiler.ProfileRendererFunction!(); - uint32 _firstTexture = D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT; - uint32 _textureCount = 0; + //uint32 _firstTexture = D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT; + //uint32 _textureCount = 0; + + ID3D11ShaderResourceView*[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT] shaderResources = .(); + ID3D11SamplerState*[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT] samplers = .(); - ID3D11ShaderResourceView*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _textures = .(); - ID3D11SamplerState*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _samplers = .(); for (let entry in shader?.Textures) { - _textures[entry.Index] = entry.BoundTexture._nativeShaderResourceView; - _samplers[entry.Index] = entry.BoundTexture._nativeSamplerState; + shaderResources[entry.Index] = entry.BoundTexture._nativeShaderResourceView; + samplers[entry.Index] = entry.BoundTexture._nativeSamplerState; - if(entry.Index >= _textureCount) + /*if(entry.Index >= _textureCount) _textureCount = entry.Index + 1; if(entry.Index < _firstTexture) - _firstTexture = entry.Index; + _firstTexture = entry.Index;*/ } - shader.Buffers.PlatformFetchNativeBuffers(); + //constantBuffers.PlatformFetchNativeBuffers(); switch (typeof(TShader)) { @@ -325,38 +339,44 @@ namespace GlitchyEngine.Renderer _hasPs = shader != null; // TODO: bind uavs - if (_textureCount > 0) + /*if (_textureCount > 0) { - NativeContext.PixelShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]); - NativeContext.PixelShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]); - } + //NativeContext.PixelShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]); + //NativeContext.PixelShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]); + }*/ - _ps_FirstTexture = _firstTexture; - _ps_BoundTextures = _textureCount; + /*_ps_FirstTexture = _firstTexture; + _ps_BoundTextures = _textureCount;*/ - _psBuffers = shader.Buffers.nativeBuffers; + //_psBuffers = constantBuffers.nativeBuffers; //NativeContext.PixelShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers); + _psShaderResources = shaderResources; + _psSamplers = samplers; + NativeContext.PixelShader.SetShader((ID3D11PixelShader*)shader?.nativeShader); case typeof(VertexShader): _hasVs = shader != null; - if (_textureCount > 0) + /*if (_textureCount > 0) { NativeContext.VertexShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]); NativeContext.VertexShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]); - } + }*/ - _vsBuffers = shader.Buffers.nativeBuffers; + //_vsBuffers = constantBuffers.nativeBuffers; //NativeContext.VertexShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers); + + _vsShaderResources = shaderResources; + _vsSamplers = samplers; NativeContext.VertexShader.SetShader((ID3D11VertexShader*)shader?.nativeShader); - _vs_FirstTexture = _firstTexture; - _vs_BoundTextures = _textureCount; + /*_vs_FirstTexture = _firstTexture; + _vs_BoundTextures = _textureCount;*/ //if (VertexShader vs = shader as VertexShader) [ConstSkip] @@ -372,12 +392,14 @@ namespace GlitchyEngine.Renderer public override void UnbindTextures() { - void** voidArray = scope void*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT]*; - + _vsShaderResources = .(); + _psShaderResources = .(); + using (ContextMonitor.Enter()) { - NativeContext.PixelShader.SetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, (.)voidArray); - NativeContext.VertexShader.SetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, (.)voidArray); + // Do we need to set here, or is it enough to do it in BindState? + NativeContext.PixelShader.SetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, &_vsShaderResources); + NativeContext.VertexShader.SetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, &_psShaderResources); } } @@ -411,6 +433,34 @@ namespace GlitchyEngine.Renderer _psBuffers[slot] = buffer.nativeBuffer; } } + + public override void BindTexture(TextureViewBinding textureBinding, int slot, ShaderStage shaderStage) + { + Debug.Assert((slot >= 0) && (slot < DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT)); + + if (shaderStage.HasFlag(.Vertex)) + { + _vsShaderResources[slot] = textureBinding._nativeShaderResourceView; + _vsSamplers[slot] = textureBinding._nativeSamplerState; + } + + if (shaderStage.HasFlag(.Pixel)) + { + _psShaderResources[slot] = textureBinding._nativeShaderResourceView; + _psSamplers[slot] = textureBinding._nativeSamplerState; + } + } + + public override void BindConstantBuffers(BufferCollection bufferCollection, ShaderStage shaderStage) + { + bufferCollection.PlatformFetchNativeBuffers(); + + if (shaderStage.HasFlag(.Vertex)) + _vsBuffers = bufferCollection.nativeBuffers; + + if (shaderStage.HasFlag(.Pixel)) + _psBuffers = bufferCollection.nativeBuffers; + } } } diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf index 1585783..77e019b 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf @@ -291,6 +291,16 @@ namespace GlitchyEngine.Renderer { _context.BindPixelShader(pixelShader); } + + public override void BindConstantBuffers(BufferCollection bufferCollection, ShaderStage shaderStage) + { + _context.BindConstantBuffers(bufferCollection, shaderStage); + } + + public override void BindTexture(TextureViewBinding textureBinding, int slot, ShaderStage shaderStage) + { + _context.BindTexture(textureBinding, slot, shaderStage); + } } } diff --git a/GlitchyEngine/src/Renderer/BufferCollection.bf b/GlitchyEngine/src/Renderer/BufferCollection.bf index d28a86b..3e2619d 100644 --- a/GlitchyEngine/src/Renderer/BufferCollection.bf +++ b/GlitchyEngine/src/Renderer/BufferCollection.bf @@ -176,5 +176,7 @@ namespace GlitchyEngine.Renderer { return _buffers.GetEnumerator(); } + + protected internal extern void PlatformFetchNativeBuffers(); } } diff --git a/GlitchyEngine/src/Renderer/BufferVariable.bf b/GlitchyEngine/src/Renderer/BufferVariable.bf index 4c8923c..07d7925 100644 --- a/GlitchyEngine/src/Renderer/BufferVariable.bf +++ b/GlitchyEngine/src/Renderer/BufferVariable.bf @@ -4,6 +4,21 @@ namespace GlitchyEngine.Renderer { using internal GlitchyEngine.Renderer; + public enum VariableFlags + { + None = 0x00, + /// The variable is dirty and needs to be sent do the GPU. + Dirty = 0x01, + /// The variable is locked, its value cannot be overwritten in child buffers. + Locked = 0x02, + /// The variable is used in the shader. Conversely, this means that the variable is unused. + Used = 0x04, + /// (For inherited variables only) The variable doesn't override the value set in the parent buffer. + Unset = 0x08, + /// The variables value cannot be changed. (i.e. it is locked in the parent buffer) + Readonly = 0x10 + } + public class BufferVariable { private ConstantBuffer _constantBuffer; @@ -19,7 +34,7 @@ namespace GlitchyEngine.Renderer // Number of elements in the array internal uint32 _arrayElements; - private bool _isUsed; + private VariableFlags _flags; public ConstantBuffer ConstantBuffer => _constantBuffer; @@ -27,7 +42,7 @@ namespace GlitchyEngine.Renderer public String Name => _name; - public bool IsUsed => _isUsed; + public bool IsUsed => _flags.HasFlag(.Used); public uint32 Columns => _columns; public uint32 Rows => _rows; @@ -35,6 +50,20 @@ namespace GlitchyEngine.Renderer public uint32 Offset => _offset; + public bool IsDirty => _flags.HasFlag(.Dirty); + public bool IsLocked => _flags.HasFlag(.Locked); + + /// Only applies to variables of child buffers. If true, the variable is unset and + /// it's value will come from the parent buffer. + /// Note: Setting IsUnset to true will only have a guaranteed effect on the value stored in this BufferVariable once Apply has been called on the constant buffer. + public bool IsUnset + { + get => _flags.HasFlag(.Unset); + set => Enum.SetFlagConditionally(ref _flags, .Unset, value); + } + + public VariableFlags Flags => _flags; + /** * Gets a pointer to the start of the variable in the constant buffers backing data. */ @@ -51,7 +80,12 @@ namespace GlitchyEngine.Renderer _offset = offset; _sizeInBytes = sizeInBytes; _arrayElements = arrayElements; - _isUsed = isUsed; + + _flags = .None; + + Enum.SetFlagConditionally(ref _flags, .Used, isUsed); + Enum.SetFlag(ref _flags, .Dirty); + //Enum.SetFlag(ref _flags, .Locked); } public void EnsureTypeMatch(int rows, int cols, ShaderVariableType type) @@ -130,12 +164,21 @@ namespace GlitchyEngine.Renderer EnsureTypeMatch(1, 4, .Float); } } + + enum SetDataError + { + VariableNotFound, + TypeMismatch + } [Inline] private void SetData(T value) { EnsureTypeMatch(); + _flags |= .Dirty; + Enum.ClearFlag(ref _flags, .Unset); + *(T*)firstByte = value; } @@ -175,6 +218,9 @@ namespace GlitchyEngine.Renderer { EnsureTypeMatch(); + _flags |= .Dirty; + Enum.ClearFlag(ref _flags, .Unset); + // TODO: assert length Internal.MemCpy(firstByte, value.Ptr, sizeof(Matrix4x3) * Math.Min(value.Count, _arrayElements)); @@ -182,16 +228,23 @@ namespace GlitchyEngine.Renderer public void SetData(Matrix3x3 value) { - EnsureTypeMatch(); + EnsureTypeMatch(); + + _flags |= .Dirty; + Enum.ClearFlag(ref _flags, .Unset); + // TODO: D3D uses 16 Byte rows, smaller rows are padded. + // OpenGL and Vulkan do this by default, too. However they support compact buffers, + // if we want to support that we need to handle it here somehow (simple size check?). *(Matrix4x3*)firstByte = Matrix4x3(value); - - // Todo: maybe manual copy } public void SetData(Matrix3x3[] value) { - EnsureTypeMatch(); + EnsureTypeMatch(); + + _flags |= .Dirty; + Enum.ClearFlag(ref _flags, .Unset); // TODO: assert length @@ -205,7 +258,10 @@ namespace GlitchyEngine.Renderer public void SetData(Matrix[] value) { - EnsureTypeMatch(); + EnsureTypeMatch(); + + _flags |= .Dirty; + Enum.ClearFlag(ref _flags, .Unset); // TODO: assert length @@ -223,11 +279,14 @@ namespace GlitchyEngine.Renderer #if GE_SHADER_UNUSED_VARIABLE_IS_ERROR Log.EngineLogger.Assert(_isUsed, scope $"Setting data for unused Variable \"{_name}\" of constant buffer \"{_constantBuffer.Name}\"."); #elif GE_SHADER_UNUSED_VARIABLE_IS_WARNING - if(!_isUsed) + if(!_flags.HasFlag(.Used)) { Log.EngineLogger.Warning($"Setting data for unused Variable \"{_name}\" of constant buffer \"{_constantBuffer.Name}\"."); } -#endif +#endif + + _flags |= .Dirty; + Enum.ClearFlag(ref _flags, .Unset); if(rawData != null) Internal.MemCpy(firstByte, rawData, _sizeInBytes); diff --git a/GlitchyEngine/src/Renderer/ConstantBuffer.bf b/GlitchyEngine/src/Renderer/ConstantBuffer.bf index c4f4839..4db50ed 100644 --- a/GlitchyEngine/src/Renderer/ConstantBuffer.bf +++ b/GlitchyEngine/src/Renderer/ConstantBuffer.bf @@ -17,8 +17,12 @@ namespace GlitchyEngine.Renderer protected BufferVariableCollection _variables = new BufferVariableCollection() ~ delete _; + protected bool _isDirty = true; + + protected internal int _generation = 0; + /// Gets the name of the constant buffer. - public String Name => _name; + public StringView Name => _name; public BufferVariableCollection Variables => _variables; @@ -58,10 +62,32 @@ namespace GlitchyEngine.Renderer /** * Uploads the date to the GPU. + * @returns true if the GPU buffer was updated, false otherwise (i.e. it wasn't dirty). */ - public Result Update() + public virtual Result Apply() { - return PlatformSetData(rawData.CArray(), (uint32)rawData.Count, 0, .WriteDiscard); + bool isDirty = false; + + for (BufferVariable variable in _variables) + { + if (variable.IsDirty) + { + isDirty = true; + Enum.ClearFlag(ref variable.[Friend]_flags, .Dirty); + } + } + + if (isDirty) + { + Result setDataResult = PlatformSetData(rawData.CArray(), (uint32)rawData.Count, 0, .WriteDiscard); + + if (setDataResult case .Err) + return .Err; + + _generation++; + } + + return .Ok; } } diff --git a/GlitchyEngine/src/Renderer/Effect.bf b/GlitchyEngine/src/Renderer/Effect.bf index 7893afe..0777a46 100644 --- a/GlitchyEngine/src/Renderer/Effect.bf +++ b/GlitchyEngine/src/Renderer/Effect.bf @@ -123,7 +123,7 @@ public class Effect : Asset { if(let cbuffer = buffer.Buffer as ConstantBuffer) { - cbuffer.Update(); + cbuffer.Apply(); } } } diff --git a/GlitchyEngine/src/Renderer/GraphicsContext.bf b/GlitchyEngine/src/Renderer/GraphicsContext.bf index 3ba694f..c97edab 100644 --- a/GlitchyEngine/src/Renderer/GraphicsContext.bf +++ b/GlitchyEngine/src/Renderer/GraphicsContext.bf @@ -116,5 +116,9 @@ namespace GlitchyEngine.Renderer public extern void UnbindTextures(); public extern void BindConstantBuffer(Buffer buffer, int slot, ShaderStage stage); + + public extern void BindConstantBuffers(BufferCollection bufferCollection, ShaderStage shaderStage); + + public extern void BindTexture(TextureViewBinding textureBinding, int slot, ShaderStage shaderStage); } } diff --git a/GlitchyEngine/src/Renderer/Material.bf b/GlitchyEngine/src/Renderer/Material.bf index f8b798b..4aa086c 100644 --- a/GlitchyEngine/src/Renderer/Material.bf +++ b/GlitchyEngine/src/Renderer/Material.bf @@ -12,11 +12,11 @@ public class Material : Asset { private Effect _effect ~ _?.ReleaseRef(); - private uint8[] _rawVariables ~ delete _; + private Dictionary Handle, TextureDimension Dimension, int32? groupTarget)> _textures = new .() ~ DeleteDictionaryAndKeys!(_); - private Dictionary Handle, TextureDimension Dimension)> _textures = new .() ~ DeleteDictionaryAndKeys!(_); + private Dictionary _variables = new .() ~ delete _; - private Dictionary _variables = new .() ~ DeleteDictionaryAndKeys!(_); + private BufferCollection _bufferCollection ~ _?.ReleaseRef(); public Effect Effect { @@ -28,36 +28,7 @@ public class Material : Asset if (_effect == null) return; - // TODO: get variables from effect - decltype(_textures) newTextures = new .(); - - // Get texture slots from effect - for(let (name, effectTexture) in _effect.Textures) - { - // TODO: We need to be able to define default textures in the shader. - // At least things like "Black", "White", "Normal" - // At best whole paths. Shouldn't be that hard to do... - - AssetHandle textureHandle = .Invalid; - - if (_textures.TryGetValue(name, let oldMaterialTexture)) - { - textureHandle = oldMaterialTexture.Handle; - } - - if (textureHandle.IsValid) - { - if (textureHandle.Dimension != effectTexture.TextureDimension) - textureHandle = .Invalid; - } - - newTextures[new String(name)] = (textureHandle, effectTexture.TextureDimension); - } - - DeleteDictionaryAndKeys!(_textures); - _textures = newTextures; - - InitRawData(); + Init(); } } @@ -71,51 +42,81 @@ public class Material : Asset Effect = effect; } + private void Init() + { + decltype(_textures) newTextures = new .(); - /** @brief Initializes the raw data array for the variables. - */ - private void InitRawData() - { - uint32 bufferSize = 0; - - decltype(_variables) newVariables = new Dictionary(); - - for(let variable in _effect.Variables) + // Get texture slots from effect + for(let (name, effectTexture) in _effect.Textures) { - newVariables.Add(new .(variable.Name), (bufferSize, variable)); + // TODO: We need to be able to define default textures in the shader. + // At least things like "Black", "White", "Normal" + // At best whole paths. Shouldn't be that hard to do... - bufferSize += variable._sizeInBytes; + AssetHandle textureHandle = .Invalid; + int32? groupTarget = null; + + if (_textures.TryGetValue(name, let oldMaterialTexture)) + { + textureHandle = oldMaterialTexture.Handle; + groupTarget = oldMaterialTexture.groupTarget; + } + + if (textureHandle.IsValid) + { + if (textureHandle.Dimension != effectTexture.TextureDimension) + textureHandle = .Invalid; + } + + newTextures[new String(name)] = (textureHandle, effectTexture.TextureDimension, groupTarget); } - uint8[] newData = new uint8[bufferSize]; + DeleteDictionaryAndKeys!(_textures); + _textures = newTextures; - for (let (newKey, newValue) in newVariables) + //InitRawData(); + InitBuffers(); + } + + private void InitBuffers() + { + _variables.Clear(); + _bufferCollection?.ReleaseRef(); + _bufferCollection = new BufferCollection(); + + void InitVariables(ConstantBuffer buffer) { - if (_variables.TryGetValue(newKey, let oldEntry)) + for (BufferVariable variable in buffer.Variables) { - if (oldEntry.Variable.ElementType == newValue.Variable.ElementType) + if (_variables.ContainsKey(variable.Name)) { - int elementSize = newValue.Variable.ElementType.ElementSizeInBytes(); - - for (int r = 0; r < Math.Min(oldEntry.Variable.Rows, newValue.Variable.Rows); r++) - for (int c = 0; c < Math.Min(oldEntry.Variable.Columns, newValue.Variable.Columns); c++) - { - int oldElementIndex = r * oldEntry.Variable.Columns + c; - int newElementIndex = r * newValue.Variable.Columns + c; - - Internal.MemCpy(newData.Ptr + (newValue.Offset + elementSize * oldElementIndex), - _rawVariables.Ptr + (oldEntry.Offset + elementSize * newElementIndex), elementSize); - } + // TODO: Handle overlapping variable names? + Log.EngineLogger.Error("Variable with same name already added to Material, skipping..."); + continue; } - // TODO: we could try to convert e.g. Int <-> Float + + _variables.Add(variable.Name, variable); } } - delete _rawVariables; - _rawVariables = newData; + for (let (bufferName, buffer) in _effect.Buffers) + { + if (buffer == null) + continue; - DeleteDictionaryAndKeys!(_variables); - _variables = newVariables; + if (ConstantBuffer parentConstBuffer = buffer as ConstantBuffer) + { + using (OverridingConstantBuffer childConstBuffer = new OverridingConstantBuffer(parentConstBuffer)) + { + _bufferCollection.Add(@bufferName.Index, childConstBuffer.Name, childConstBuffer); + InitVariables(childConstBuffer); + } + } + else + { + Log.EngineLogger.Error("Found buffer in constant buffer collection that is not a constant buffer."); + } + } } /** @@ -124,7 +125,8 @@ public class Material : Asset public void Bind() { Debug.Profiler.ProfileRendererFunction!(); - + + // TODO: Bind textures, don't go through effect for that for(let (name, texture) in _textures) { switch (texture.Dimension) @@ -142,20 +144,30 @@ public class Material : Asset } } - for(let (name, variable) in _variables) + for (let (bufferName, buffer) in _bufferCollection) { - variable.Variable.SetRawData(RawPointer!(variable.Offset)); + if (let cbuffer = buffer as ConstantBuffer) + { + TrySilent!(cbuffer.Apply()); + } } - _effect.ApplyChanges(); + /*for(let (name, variable) in _variables) + { + variable.Variable.SetRawData(RawPointer!(variable.Offset)); + }*/ + + //_effect.ApplyChanges(); _effect.Bind(); + + RenderCommand.BindConstantBuffers(_bufferCollection); } /** @brief Sets a texture of the material. * @param name The name of the texture to set. * @param texture The texture to bind to the effect. */ - public void SetTexture(String name, AssetHandle texture) + public void SetTexture(String name, AssetHandle texture, int32? groupTargetIndex = null) { if(_textures.TryGetValue(name, var entry)) { @@ -174,6 +186,7 @@ public class Material : Asset }*/ //entry?.ReleaseRef(); _textures[name].Handle = texture; + _textures[name].groupTarget = groupTargetIndex; //texture?.AddRef(); } else @@ -183,135 +196,41 @@ public class Material : Asset } } - private mixin RawPointer(uint32 offset) + private mixin SetVariable(String name, var value) { - (T*)(&_rawVariables[offset]) - } - - [Inline] - private void SetVariable(String name, T value) where T : struct - { - Debug.Profiler.ProfileRendererFunction!(); - if(_variables.TryGetValue(name, let entry)) { - entry.Variable.EnsureTypeMatch(); - - *RawPointer!(entry.Offset) = value; - } - else - { - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); + entry.SetData(value); } } - public void SetVariable(String name, bool value) => SetVariable(name, value); - public void SetVariable(String name, bool2 value) => SetVariable(name, value); - public void SetVariable(String name, bool3 value) => SetVariable(name, value); - public void SetVariable(String name, bool4 value) => SetVariable(name, value); + public void SetVariable(String name, bool value) => SetVariable!(name, value); + public void SetVariable(String name, bool2 value) => SetVariable!(name, value); + public void SetVariable(String name, bool3 value) => SetVariable!(name, value); + public void SetVariable(String name, bool4 value) => SetVariable!(name, value); + + public void SetVariable(String name, int32 value) => SetVariable!(name, value); + public void SetVariable(String name, int2 value) => SetVariable!(name, value); + public void SetVariable(String name, int3 value) => SetVariable!(name, value); + public void SetVariable(String name, int4 value) => SetVariable!(name, value); - public void SetVariable(String name, int32 value) => SetVariable(name, value); - public void SetVariable(String name, int2 value) => SetVariable(name, value); - public void SetVariable(String name, int3 value) => SetVariable(name, value); - public void SetVariable(String name, int4 value) => SetVariable(name, value); + public void SetVariable(String name, uint32 value) => SetVariable!(name, value); + public void SetVariable(String name, uint2 value) => SetVariable!(name, value); + public void SetVariable(String name, uint3 value) => SetVariable!(name, value); + public void SetVariable(String name, uint4 value) => SetVariable!(name, value); - public void SetVariable(String name, uint32 value) => SetVariable(name, value); - public void SetVariable(String name, uint2 value) => SetVariable(name, value); - public void SetVariable(String name, uint3 value) => SetVariable(name, value); - public void SetVariable(String name, uint4 value) => SetVariable(name, value); + public void SetVariable(String name, float value) => SetVariable!(name, value); + public void SetVariable(String name, float2 value) => SetVariable!(name, value); + public void SetVariable(String name, float3 value) => SetVariable!(name, value); + public void SetVariable(String name, float4 value) => SetVariable!(name, value); - public void SetVariable(String name, float value) => SetVariable(name, value); - public void SetVariable(String name, float2 value) => SetVariable(name, value); - public void SetVariable(String name, float3 value) => SetVariable(name, value); - public void SetVariable(String name, float4 value) => SetVariable(name, value); + public void SetVariable(String name, Color value) => SetVariable!(name, value); + public void SetVariable(String name, ColorRGB value) => SetVariable!(name, value); + public void SetVariable(String name, ColorRGBA value) => SetVariable!(name, value); - /*public void SetVariable(String name, float value) => SetVariable(name, value); - public void SetVariable(String name, float2 value) => SetVariable(name, value); - public void SetVariable(String name, float3 value) => SetVariable(name, value); - public void SetVariable(String name, float4 value) => SetVariable(name, value);*/ - - public void SetVariable(String name, Color value) => SetVariable(name, (ColorRGBA)value); - public void SetVariable(String name, ColorRGB value) => SetVariable(name, value); - public void SetVariable(String name, ColorRGBA value) => SetVariable(name, value); - - public void SetVariable(String name, Matrix3x3 value) - { - if(_variables.TryGetValue(name, let entry)) - { - entry.Variable.EnsureTypeMatch(); - - // TODO: I'm not sure how to handle Matrix3x3 - // It seems to be 44 Bytes (11 Floats) large. - Log.EngineLogger.AssertDebug(entry.Variable._sizeInBytes == 44, "Made wrong assumption about the size of float3x3 in a hlsl constant-buffer."); - -#unwarn - *RawPointer!(entry.Offset) = *(float[11]*)&Matrix4x3(value); - } - else - { - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); - } - } - - public void SetVariable(String name, Matrix3x3[] values) - { - if(_variables.TryGetValue(name, let entry)) - { - entry.Variable.EnsureTypeMatch(); - - int count = Math.Min(values.Count, entry.Variable._arrayElements); - - for(int i < count) - { - (RawPointer!(entry.Offset))[i] = Matrix4x3(values[i]); - } - } - else - { - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); - } - } - - public void SetVariable(String name, Matrix4x3 value) => SetVariable(name, value); - public void SetVariable(String name, Matrix value) => SetVariable(name, value); - - public void SetVariable(String name, Matrix[] values) - { - if(_variables.TryGetValue(name, let entry)) - { - entry.Variable.EnsureTypeMatch(); - - Internal.MemCpy(RawPointer!(entry.Offset), values.Ptr, sizeof(Matrix) * Math.Min(values.Count, entry.Variable._arrayElements)); - } - else - { - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); - } - } - - // Supporeted types - // Bool, Bool2, Bool3, Bool4 - // Int, int2, int3, int4 - // UInt, UInt2, UInt3, UInt4 - // Color, ColorRGB, ColorRGBA - // Float, Float2, Float3, Float4 - // Matrix3x3, Matrix4x3, Matrix - - // TODO: Add missing variable types - // Half, Half2, Half3, Half4 - // Byte, Byte2, Byte3, Byte4 - - /** - * Sets the raw data of the variable. - * @param rawData The pointer to the raw data. If rawData is null the raw data will be set to zero. - */ - internal void SetRawData(uint32 offset, void* rawData, uint32 byteCount) - { - if(rawData != null) - Internal.MemCpy(&_rawVariables + offset, rawData, byteCount); - else - Internal.MemSet(&_rawVariables + offset, 0, byteCount); - } + public void SetVariable(String name, Matrix3x3 value) => SetVariable!(name, value); + public void SetVariable(String name, Matrix4x3 value) => SetVariable!(name, value); + public void SetVariable(String name, Matrix value) => SetVariable!(name, value); public void GetVariable(String name, out T value) where T : struct { @@ -319,9 +238,10 @@ public class Material : Asset if(_variables.TryGetValue(name, let entry)) { - entry.Variable.EnsureTypeMatch(); - - value = *RawPointer!(entry.Offset); + entry.EnsureTypeMatch(); + + // TODO: This obviously breaks for all cases where a custom SetData was necessary. + value = *(T*)entry.firstByte; } else { diff --git a/GlitchyEngine/src/Renderer/OverridingConstantBuffer.bf b/GlitchyEngine/src/Renderer/OverridingConstantBuffer.bf new file mode 100644 index 0000000..c9fdab9 --- /dev/null +++ b/GlitchyEngine/src/Renderer/OverridingConstantBuffer.bf @@ -0,0 +1,106 @@ +using System; + +namespace GlitchyEngine.Renderer; + +using internal GlitchyEngine.Renderer; + +class OverridingConstantBuffer : ConstantBuffer +{ + protected ConstantBuffer _parent; + + protected int _parentGeneration = 0; + + public ConstantBuffer Parent => _parent; + + public this(ConstantBuffer parent) : base(parent.Name, parent.RawData.Length) + { + Log.EngineLogger.AssertDebug(parent != null); + _parent = parent; + + InheritVariables(); + } + + private void InheritVariables() + { + for (BufferVariable parentVariable in _parent.Variables) + { + BufferVariable newVariable = new BufferVariable(parentVariable.Name, this, parentVariable.ElementType, parentVariable.Columns, + parentVariable.Rows, parentVariable.Offset, parentVariable._sizeInBytes, parentVariable.ArrayElements, parentVariable.IsUsed); + + if (parentVariable.Flags.HasFlag(.Locked)) + { + Enum.SetFlag(ref newVariable.[Friend]_flags, .Readonly | .Locked); + } + + // Default to using the value from the parent buffer. + Enum.SetFlag(ref newVariable.[Friend]_flags, .Unset); + + _variables.Add(newVariable); + } + } + + /** + * Uploads the date to the GPU. + * @returns true if the GPU buffer was updated, false otherwise (i.e. it wasn't dirty). + */ + public override Result Apply() + { + Result parentChangedResult = _parent.Apply(); + + if (parentChangedResult case .Err) + return .Err; + + bool isDirty = false; + + // If the parent got a newer generation then it was changed since our last apply. + if (_parentGeneration != _parent._generation) + { + uint8[] newData = scope uint8[rawData.Count]; + + isDirty = true; + _parent.RawData.CopyTo(newData); + + for (BufferVariable variable in _variables) + { + Enum.ClearFlag(ref variable.[Friend]_flags, .Dirty); + // TODO: Check if our variable is + + if (!variable.Flags.HasFlag(.Unset)) + { + Internal.MemCpy(newData.Ptr + variable.Offset, variable.firstByte, variable._sizeInBytes); + } + } + + newData.CopyTo(rawData); + } + else + { + for (BufferVariable variable in _variables) + { + if (variable.IsDirty) + { + isDirty = true; + Enum.ClearFlag(ref variable.[Friend]_flags, .Dirty); + + if (variable.Flags.HasFlag(.Unset)) + { + // If it is unset, we copy the value from the parent buffer into ourselves. + Internal.MemCpy(variable.firstByte, _parent.rawData.Ptr + variable.Offset, variable._sizeInBytes); + } + } + } + } + + if (isDirty) + { + Result setDataResult = PlatformSetData(rawData.Ptr, (uint32)rawData.Count, 0, .WriteDiscard); + + if (setDataResult case .Err) + return .Err; + + _generation++; + } + + return .Ok; + } +} \ No newline at end of file diff --git a/GlitchyEngine/src/Renderer/RenderCommand.bf b/GlitchyEngine/src/Renderer/RenderCommand.bf index 82358a1..b42345c 100644 --- a/GlitchyEngine/src/Renderer/RenderCommand.bf +++ b/GlitchyEngine/src/Renderer/RenderCommand.bf @@ -129,6 +129,11 @@ namespace GlitchyEngine.Renderer _rendererAPI.BindConstantBuffer(buffer, slot, stage); } + public static void BindConstantBuffers(BufferCollection constantBuffers, ShaderStage stage = .All) + { + _rendererAPI.BindConstantBuffers(constantBuffers, stage); + } + public static void BindVertexShader(VertexShader vertexShader) { _rendererAPI.BindVertexShader(vertexShader); @@ -138,5 +143,10 @@ namespace GlitchyEngine.Renderer { _rendererAPI.BindPixelShader(pixelShader); } + + public static void BindTexture(TextureViewBinding textureBinding, int slot, ShaderStage shaderStage) + { + _rendererAPI.BindTexture(textureBinding, slot, shaderStage); + } } } diff --git a/GlitchyEngine/src/Renderer/Renderer2D.bf b/GlitchyEngine/src/Renderer/Renderer2D.bf index 7acf382..af29304 100644 --- a/GlitchyEngine/src/Renderer/Renderer2D.bf +++ b/GlitchyEngine/src/Renderer/Renderer2D.bf @@ -132,6 +132,7 @@ namespace GlitchyEngine.Renderer #endif private static AssetHandle s_quadBatchEffect; + private static Material s_quadBatchMaterial ~ _?.ReleaseRef(); private static AssetHandle s_circleBatchEffect; private static AssetHandle s_lineBatchEffect; @@ -162,7 +163,7 @@ namespace GlitchyEngine.Renderer private static DrawOrder s_drawOrder; /// The effect that is currently used to draw the sprites. - private static AssetHandle s_currentQuadEffect; + //private static AssetHandle s_currentQuadEffect; private static AssetHandle s_currentCircleEffect; private static AssetHandle s_currentLineEffect; @@ -185,6 +186,7 @@ namespace GlitchyEngine.Renderer Debug.Profiler.ProfileFunction!(); s_quadBatchEffect = Content.LoadAsset("Resources/Shaders/spritebatch.hlsl", null, true); + s_quadBatchMaterial = new Material(s_quadBatchEffect); s_circleBatchEffect = Content.LoadAsset("Resources/Shaders/circlebatch.hlsl", null, true); s_lineBatchEffect = Content.LoadAsset("Resources/Shaders/linebatch.hlsl", null, true); } @@ -455,7 +457,7 @@ namespace GlitchyEngine.Renderer } // TODO: remove? - public static void BeginScene(OldCamera camera, DrawOrder drawOrder = .SortByTexture, AssetHandle effect = .Invalid, AssetHandle circleEffect = .Invalid) + public static void BeginScene(OldCamera camera, DrawOrder drawOrder = .SortByTexture) { Debug.Profiler.ProfileRendererFunction!(); #if DEBUG @@ -463,28 +465,29 @@ namespace GlitchyEngine.Renderer Log.EngineLogger.AssertDebug(!s_sceneRunning, "You have to call EndScene before you can make another call to BeginScene."); #endif - if(effect != .Invalid) + /*if(effect != .Invalid) { s_currentQuadEffect = effect; } else { s_currentQuadEffect = s_quadBatchEffect; - } + }*/ - if(circleEffect != .Invalid) + /*if(circleEffect != .Invalid) { s_currentCircleEffect = circleEffect; } else { s_currentCircleEffect = s_circleBatchEffect; - } + }*/ s_currentLineEffect = s_lineBatchEffect; - s_currentQuadEffect.Variables["ViewProjection"].SetData(camera.ViewProjection); - s_currentCircleEffect.Variables["ViewProjection"].SetData(camera.ViewProjection); + //s_currentQuadEffect.Variables["ViewProjection"].SetData(camera.ViewProjection); + s_quadBatchMaterial.SetVariable("ViewProjection", camera.ViewProjection); + //s_currentCircleEffect.Variables["ViewProjection"].SetData(camera.ViewProjection); s_drawOrder = drawOrder; @@ -493,7 +496,7 @@ namespace GlitchyEngine.Renderer #endif } - public static void BeginScene(Camera camera, Matrix transform, DrawOrder drawOrder = .SortByTexture, AssetHandle effect = .Invalid, AssetHandle circleEffect = .Invalid) + public static void BeginScene(Camera camera, Matrix transform, DrawOrder drawOrder = .SortByTexture) { Debug.Profiler.ProfileRendererFunction!(); #if DEBUG @@ -501,29 +504,30 @@ namespace GlitchyEngine.Renderer Log.EngineLogger.AssertDebug(!s_sceneRunning, "You have to call EndScene before you can make another call to BeginScene."); #endif - if(effect != .Invalid) + /*if(effect != .Invalid) { s_currentQuadEffect = effect; } else { s_currentQuadEffect = s_quadBatchEffect; - } + }*/ - if(circleEffect != .Invalid) + /*if(circleEffect != .Invalid) { s_currentCircleEffect = circleEffect; } else { s_currentCircleEffect = s_circleBatchEffect; - } + }*/ s_currentLineEffect = s_lineBatchEffect; Matrix viewProjection = camera.Projection * Matrix.Invert(transform); - - s_currentQuadEffect.Get()?.Variables["ViewProjection"].SetData(viewProjection); + + s_quadBatchMaterial.SetVariable("ViewProjection", viewProjection); + //s_currentQuadEffect.Get()?.Variables["ViewProjection"].SetData(viewProjection); s_currentCircleEffect.Get()?.Variables["ViewProjection"].SetData(viewProjection); s_currentLineEffect.Get()?.Variables["ViewProjection"].SetData(viewProjection); @@ -534,7 +538,7 @@ namespace GlitchyEngine.Renderer #endif } - public static void BeginScene(EditorCamera camera, DrawOrder drawOrder = .SortByTexture, AssetHandle effect = .Invalid, AssetHandle circleEffect = .Invalid) + public static void BeginScene(EditorCamera camera, DrawOrder drawOrder = .SortByTexture) { Debug.Profiler.ProfileRendererFunction!(); #if DEBUG @@ -542,7 +546,7 @@ namespace GlitchyEngine.Renderer Log.EngineLogger.AssertDebug(!s_sceneRunning, "You have to call EndScene before you can make another call to BeginScene."); #endif - if(effect != .Invalid) + /*if(effect != .Invalid) { s_currentQuadEffect = effect; } @@ -558,13 +562,14 @@ namespace GlitchyEngine.Renderer else { s_currentCircleEffect = s_circleBatchEffect; - } + }*/ s_currentLineEffect = s_lineBatchEffect; Matrix viewProjection = camera.Projection * camera.View; - s_currentQuadEffect.Get()?.Variables["ViewProjection"].SetData(viewProjection); + //s_currentQuadEffect.Get()?.Variables["ViewProjection"].SetData(viewProjection); + s_quadBatchMaterial.SetVariable("ViewProjection", viewProjection); s_currentCircleEffect.Get()?.Variables["ViewProjection"].SetData(viewProjection); s_currentLineEffect.Get()?.Variables["ViewProjection"].SetData(viewProjection); @@ -623,7 +628,7 @@ namespace GlitchyEngine.Renderer s_statistics.LineCount++; } - private static void FlushQuadInstances() + private static void FlushQuadInstances(Texture texture) { Debug.Profiler.ProfileRendererFunction!(); @@ -632,8 +637,24 @@ namespace GlitchyEngine.Renderer s_quadInstanceBuffer.SetData(s_rawQuadInstances.Ptr, s_setQuadInstances, 0, .WriteDiscard); - s_currentQuadEffect.ApplyChanges(); - s_currentQuadEffect.Bind(); + //s_currentQuadEffect.ApplyChanges(); + //s_currentQuadEffect.Bind(); + s_quadBatchMaterial.Bind(); + + using (TextureViewBinding tvb = texture.GetViewBinding()) + { + if (s_quadBatchMaterial.Effect.Textures.TryGetValue("Texture", let textureEntry)) + { + if (textureEntry.PsSlot != null) + { + RenderCommand.BindTexture(tvb, textureEntry.PsSlot.Index, .Pixel); + } + } + } + + //s_quadBatchMaterial.Effect.Textures["Texture"] + // + s_quadBatchBinding.InstanceCount = s_setQuadInstances; s_quadBatchBinding.Bind(); RenderCommand.DrawIndexedInstanced(s_quadBatchBinding); @@ -778,13 +799,13 @@ namespace GlitchyEngine.Renderer // TODO: per object blendstate RenderCommand.SetBlendState(s_transparentBlendState); - let quadEffect = s_currentQuadEffect.Get(); + let quadMaterial = s_quadBatchMaterial;//s_currentQuadEffect.Get(); - if (quadEffect == null) + if (quadMaterial == null) return; Texture texture = s_QuadinstanceQueue[0].Texture; - quadEffect.SetTexture("Texture", texture); + //quadMaterial.SetTexture("Texture", .Invalid); s_setQuadInstances = 0; @@ -795,21 +816,21 @@ namespace GlitchyEngine.Renderer // flush every time the texture changes if(quad.Texture != texture) { - FlushQuadInstances(); + FlushQuadInstances(texture); texture = quad.Texture; - quadEffect.SetTexture("Texture", texture); + //quadMaterial.SetTexture("Texture", .Invalid); } s_rawQuadInstances[s_setQuadInstances++] = .(quad.Transform, quad.Color, quad.uvTransform, quad.entityId); if(s_setQuadInstances == s_rawQuadInstances.Count) { - FlushQuadInstances(); + FlushQuadInstances(texture); } } - FlushQuadInstances(); + FlushQuadInstances(texture); s_QuadinstanceQueue.Clear(); } diff --git a/GlitchyEngine/src/Renderer/RendererAPI.bf b/GlitchyEngine/src/Renderer/RendererAPI.bf index 5d60b74..0134c57 100644 --- a/GlitchyEngine/src/Renderer/RendererAPI.bf +++ b/GlitchyEngine/src/Renderer/RendererAPI.bf @@ -68,5 +68,9 @@ namespace GlitchyEngine.Renderer public extern void BindVertexShader(VertexShader vertexShader); public extern void BindPixelShader(PixelShader pixelShader); + + public extern void BindConstantBuffers(BufferCollection bufferCollection, ShaderStage shaderStage); + + public extern void BindTexture(TextureViewBinding textureBinding, int slot, ShaderStage shaderStage); } } diff --git a/GlitchyEngine/src/Renderer/Text/FontRenderer.bf b/GlitchyEngine/src/Renderer/Text/FontRenderer.bf index ead3129..6d729b0 100644 --- a/GlitchyEngine/src/Renderer/Text/FontRenderer.bf +++ b/GlitchyEngine/src/Renderer/Text/FontRenderer.bf @@ -560,7 +560,7 @@ namespace GlitchyEngine.Renderer.Text public static void DrawText(PreparedText text, Matrix transform, ColorRGBA fontColor = .White) { - Debug.Profiler.ProfileRendererFunction!(); + /*Debug.Profiler.ProfileRendererFunction!(); text.AddRef(); defer text.ReleaseRef(); @@ -654,7 +654,7 @@ namespace GlitchyEngine.Renderer.Text for(int i < atlasses.Count) { atlasses[i].ReleaseRef(); - } + }*/ } } } diff --git a/GlitchyEngine/src/World/SceneRenderer.bf b/GlitchyEngine/src/World/SceneRenderer.bf index 6ae08b1..ee1d814 100644 --- a/GlitchyEngine/src/World/SceneRenderer.bf +++ b/GlitchyEngine/src/World/SceneRenderer.bf @@ -171,6 +171,8 @@ class SceneRenderer Debug.Profiler.ProfileRendererFunction!(); viewportTarget.AddRef(); + + RenderCommand.Clear(_compositeTarget, .ColorDepth); // 3D render Renderer.BeginScene(camera, _compositeTarget);