From 1418063d68000b5b172a571c8b3e8b79e8171edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Fri, 20 May 2022 18:37:59 +0200 Subject: [PATCH] RenderTargetGroup --- .../DX11/Renderer/Dx11GraphicsContext.bf | 17 +- .../DX11/Renderer/Dx11RenderTarget.bf | 256 ++++++++++++++++++ .../Platform/DX11/Renderer/Dx11RendererAPI.bf | 52 +++- .../src/Platform/DX11/Renderer/Dx11Shader.bf | 2 +- .../src/Platform/DX11/Renderer/Dx11Texture.bf | 10 + .../DX11/Renderer/Dx11TextureViewBinding.bf | 33 +++ GlitchyEngine/src/Renderer/Effect.bf | 57 ++-- GlitchyEngine/src/Renderer/Material.bf | 14 +- GlitchyEngine/src/Renderer/RenderCommand.bf | 10 + GlitchyEngine/src/Renderer/RenderTarget.bf | 159 +++++++++++ GlitchyEngine/src/Renderer/Renderer.bf | 62 +++-- GlitchyEngine/src/Renderer/RendererAPI.bf | 4 + .../src/Renderer/ShaderTextureCollection.bf | 12 +- GlitchyEngine/src/Renderer/Texture.bf | 16 ++ .../src/Renderer/TextureViewBinding.bf | 16 ++ 15 files changed, 667 insertions(+), 53 deletions(-) create mode 100644 GlitchyEngine/src/Platform/DX11/Renderer/Dx11TextureViewBinding.bf create mode 100644 GlitchyEngine/src/Renderer/TextureViewBinding.bf diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf index c58e19f..7b9bc2a 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf @@ -120,6 +120,11 @@ namespace GlitchyEngine.Renderer _depthStencilTarget = target?.nativeView; } + internal void SetNativeDepthStencilTarget(ID3D11DepthStencilView* depthStencilTarget) + { + _depthStencilTarget = depthStencilTarget; + } + public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthTarget) { _renderTargets[slot] = (renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView; @@ -129,6 +134,14 @@ namespace GlitchyEngine.Renderer SetDepthStencilTarget((renderTarget ?? _swapChain.BackBuffer).DepthStencilTarget); } } + + internal void SetNativeRenderTargets(Span renderTargets, int startSlot) + { + for (int i < renderTargets.Length) + { + _renderTargets[i + startSlot] = renderTargets[i]; + } + } public override void UnbindRenderTargets() { @@ -206,8 +219,8 @@ namespace GlitchyEngine.Renderer for(let entry in shader.Textures) { - _textures[entry.Index] = entry.Texture?._nativeResourceView; - _samplers[entry.Index] = entry.Texture?.SamplerState?.nativeSamplerState; + _textures[entry.Index] = entry.BoundTexture._nativeShaderResourceView; + _samplers[entry.Index] = entry.BoundTexture._nativeSamplerState; if(entry.Index >= _textureCount) _textureCount = entry.Index + 1; diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RenderTarget.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RenderTarget.bf index 387d54f..02b8800 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RenderTarget.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RenderTarget.bf @@ -118,6 +118,262 @@ namespace GlitchyEngine.Renderer Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view"); } } + + protected override TextureViewBinding PlatformGetViewBinding() + { + return .(_nativeResourceView, _samplerState.nativeSamplerState); + } + } + + extension RenderTargetFormat + { + public DirectX.DXGI.Format GetTextureFormat() + { + switch(this) + { + case .R8G8B8A8_UNorm: + return .R8G8B8A8_UNorm; + case .R8G8B8A8_SNorm: + return .R8G8B8A8_SNorm; + + case .R16G16B16A16_SNorm: + return .R16G16B16A16_SNorm; + case .R16G16B16A16_Float: + return .R16G16B16A16_Float; + + case .R32G32B32A32_Float: + return .R32G32B32A32_Float; + + case .D24_UNorm_S8_UInt: + return .R24G8_Typeless; + + default: + return .Unknown; + } + } + + public DirectX.DXGI.Format GetShaderViewFormat() + { + switch(this) + { + case .D24_UNorm_S8_UInt: + return .R24_UNorm_X8_Typeless; + + default: + return GetTextureFormat(); + } + } + + public DirectX.DXGI.Format GetTargetViewFormat() + { + switch(this) + { + case .D24_UNorm_S8_UInt: + return .D24_UNorm_S8_UInt; + + default: + return GetTextureFormat(); + } + } + } + + extension RenderTargetGroup + { + internal ID3D11Texture2D*[] _nativeTextures; + internal ID3D11RenderTargetView*[] _renderTargetViews; + internal ID3D11ShaderResourceView*[] _nativeResourceViews; + + internal ID3D11Texture2D* _nativeDepthTexture; + internal ID3D11DepthStencilView* _nativeDepthTargetView; + internal ID3D11ShaderResourceView* _nativeDepthResourceView; + + ~this() + { + ReleaseEveryThing(); + } + + private ID3D11Texture2D* PlatformCreateTexture(TargetDescription target) + { + Debug.Profiler.ProfileResourceFunction!(); + + Texture2DDescription desc = .() + { + Width = _description.Width, + Height = _description.Height, + ArraySize = _description.ArraySize, + MipLevels = _description.MipLevels, + Format = target.Format.GetTextureFormat(), + // Always bindable as ShaderResource and RenderTarget + BindFlags = .ShaderResource | (target.Format.IsDepth ? .DepthStencil : .RenderTarget), + // TODO: CpuAccessFlags = (.)_description.CpuAccess, + //CpuAccessFlags = (.)_description.CpuAccess, + // 2D RenderTarget never has misc flags + MiscFlags = .None, + SampleDesc = .(_description.Samples, 0), // TODO: SampleQuality? + // RenderTarget always has Default usage + Usage = .Default + }; + + ID3D11Texture2D* texture = null; + var result = NativeDevice.CreateTexture2D(ref desc, null, &texture); + Log.EngineLogger.Assert(result.Succeeded, "Failed to create RenderTarget2D"); + + return texture; + } + + private (ID3D11ShaderResourceView* ResourceView, ID3D11DeviceChild* TargetOrDepthView) CreateViews(TargetDescription target, ID3D11Texture2D* texture) + { + Debug.Profiler.ProfileResourceFunction!(); + + ShaderResourceViewDescription svDesc = .(); + RenderTargetViewDescription rtDesc = .(); + DepthStencilViewDescription dsDesc = .(); + + svDesc.Format = target.Format.GetShaderViewFormat(); + rtDesc.Format = target.Format.GetTargetViewFormat(); + dsDesc.Format = target.Format.GetTargetViewFormat(); + + if (_description.ArraySize > 1) + { + if (_description.Samples > 1) + { + svDesc.ViewDimension = .Texture2DMultisampledArray; + rtDesc.ViewDimension = .Texture2DArrayMultisample; + dsDesc.ViewDimension = .Texture2DMultisampledArray; + } + else + { + svDesc.ViewDimension = .Texture2DArray; + rtDesc.ViewDimension = .Texture2DArray; + dsDesc.ViewDimension = .Texture2DArray; + } + } + else + { + if (_description.Samples > 1) + { + svDesc.ViewDimension = .Texture2DMultisampled; + rtDesc.ViewDimension = .Texture2DMultisample; + dsDesc.ViewDimension = .Texture2DMultisampled; + } + else + { + svDesc.ViewDimension = .Texture2D; + rtDesc.ViewDimension = .Texture2D; + dsDesc.ViewDimension = .Texture2D; + } + } + + svDesc.Description = .(svDesc.ViewDimension); + rtDesc.Description = .(rtDesc.ViewDimension); + dsDesc.Description = .(dsDesc.ViewDimension); + + ID3D11ShaderResourceView* resourceView = null; + ID3D11DeviceChild* targetOrDepthView = null; + + var result = NativeDevice.CreateShaderResourceView(texture, &svDesc, &resourceView); + Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view"); + + if (target.Format.IsDepth) + result = NativeDevice.CreateDepthStencilView(texture, &dsDesc, (.)&targetOrDepthView); + else + result = NativeDevice.CreateRenderTargetView(texture, &rtDesc, (.)&targetOrDepthView); + + Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view"); + + // TODO: UAVs + + return (resourceView, targetOrDepthView); + } + + mixin DeleteContainerReleaseItemsAndNullify(var container) + { + if (container != null) + { + for (var tex in container) + { + tex.Release(); + } + DeleteAndNullify!(container); + } + } + + private void ReleaseEveryThing() + { + DeleteContainerReleaseItemsAndNullify!(_nativeTextures); + DeleteContainerReleaseItemsAndNullify!(_renderTargetViews); + DeleteContainerReleaseItemsAndNullify!(_nativeResourceViews); + + ReleaseAndNullify!(_nativeDepthTexture); + ReleaseAndNullify!(_nativeDepthTargetView); + ReleaseAndNullify!(_nativeDepthResourceView); + } + + public override void ApplyChanges() + { + Debug.Profiler.ProfileResourceFunction!(); + + ReleaseEveryThing(); + + if (_colorTargetDescriptions != null) + { + _nativeTextures = new .[_colorTargetDescriptions.Count]; + _renderTargetViews = new .[_colorTargetDescriptions.Count]; + _nativeResourceViews = new .[_colorTargetDescriptions.Count]; + + for (int i < _nativeTextures.Count) + { + TargetDescription target = _colorTargetDescriptions[i]; + + if (target.IsSwapchainTarget) + { + // TODO: if the engine supports multiple windows it has to support multiple swap chains. + + // kinda dirty... + var context = GraphicsContext.Get(); + + context.SwapChain.GetBackbuffer(out _nativeTextures[i]); + } + else + { + _nativeTextures[i] = PlatformCreateTexture(target); + } + + (_nativeResourceViews[i], _renderTargetViews[i]) = (.)CreateViews(target, _nativeTextures[i]); + } + } + + if (_depthTargetDescription.Format != .None) + { + _nativeDepthTexture = PlatformCreateTexture(_depthTargetDescription); + + (_nativeDepthResourceView, _nativeDepthTargetView) = (.)CreateViews(_depthTargetDescription, _nativeDepthTexture); + } + } + + public override void Resize(uint32 width, uint32 height) + { + Debug.Profiler.ProfileResourceFunction!(); + + _description.Width = width; + _description.Height = height; + + ApplyChanges(); + } + + protected override TextureViewBinding PlatformGetViewBinding(int index) + { + if (index == -1) + { + return .(_nativeDepthResourceView, _depthSamplerState.nativeSamplerState); + } + else + { + Log.EngineLogger.AssertDebug(index < _nativeResourceViews.Count); + + return .(_nativeResourceViews[index], _colorSamplerStates[index].nativeSamplerState); + } + } } } diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf index 86945c8..03ee8f7 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf @@ -61,13 +61,63 @@ namespace GlitchyEngine.Renderer NativeContext.ClearDepthStencilView(target.nativeView, flags, depth, stencil); } + public override void Clear(RenderTargetGroup renderTarget, ClearOptions options, ColorRGBA? color = null, float? depth = null, uint8? stencil = null) + { + if (options.HasFlag(.Color) && renderTarget._renderTargetViews != null) + { + for (int i < renderTarget._renderTargetViews.Count) + { + NativeContext.ClearRenderTargetView(renderTarget._renderTargetViews[i], + color ?? renderTarget._colorTargetDescriptions[i].ClearColor); + } + } + + if (renderTarget._nativeDepthTargetView != null) + { + DirectX.D3D11.ClearFlag flags = default; + + if(options.HasFlag(.Depth)) + { + flags |= .Depth; + } + + if(options.HasFlag(.Stencil)) + { + flags |= .Stencil; + } + + if (flags != default) + { + NativeContext.ClearDepthStencilView(renderTarget._nativeDepthTargetView, flags, + renderTarget._depthTargetDescription.ClearColor.R, + (uint8)renderTarget._depthTargetDescription.ClearColor.G); + } + } + } + public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthBuffer) { Debug.Profiler.ProfileRendererFunction!(); _context.SetRenderTarget(renderTarget, slot, setDepthBuffer); } - + + public override void SetRenderTargetGroup(RenderTargetGroup renderTarget, bool setDepthBuffer) + { + if (renderTarget._renderTargetViews != null) + { + for (int i < renderTarget._renderTargetViews.Count) + { + _context.SetNativeRenderTargets(renderTarget._renderTargetViews, 0); + } + } + + if (setDepthBuffer) + { + _context.SetNativeDepthStencilTarget(renderTarget._nativeDepthTargetView); + } + } + public override void SetDepthStencilTarget(DepthStencilTarget target) { Debug.Profiler.ProfileRendererFunction!(); diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Shader.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Shader.bf index 73f35b3..7025d1e 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Shader.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Shader.bf @@ -100,7 +100,7 @@ namespace GlitchyEngine.Renderer buffer.ReleaseRef(); } case .Texture: - _textures.Add(scope String(bindDesc.Name), bindDesc.BindPoint, null); + _textures.Add(scope String(bindDesc.Name), bindDesc.BindPoint, TextureViewBinding(null, null)); case .Sampler: // TODO: do we have to do something for samplers? default: diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf index 09fd531..5a34efa 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf @@ -256,6 +256,11 @@ namespace GlitchyEngine.Renderer nativeTexture, D3D11.CalcSubresource(mipSlice, arraySlice, MipLevels), (.)&sourceBox); } } + + protected override TextureViewBinding PlatformGetViewBinding() + { + return .(_nativeResourceView, _samplerState.nativeSamplerState); + } } extension TextureCube @@ -282,6 +287,11 @@ namespace GlitchyEngine.Renderer Log.EngineLogger.Assert(nativeDesc.MiscFlags.HasFlag(.TextureCube), scope $"The texture \"{_path}\" is not a texture cube."); // TODO: load fallback texture } + + protected override TextureViewBinding PlatformGetViewBinding() + { + return .(_nativeResourceView, _samplerState.nativeSamplerState); + } } } diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11TextureViewBinding.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11TextureViewBinding.bf new file mode 100644 index 0000000..aac1924 --- /dev/null +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11TextureViewBinding.bf @@ -0,0 +1,33 @@ +using DirectX.D3D11; + +namespace GlitchyEngine.Renderer +{ + extension TextureViewBinding + { + internal ID3D11ShaderResourceView* _nativeShaderResourceView; + internal ID3D11SamplerState* _nativeSamplerState; + + public override bool IsEmpty => _nativeShaderResourceView != null; + + internal this(ID3D11ShaderResourceView* shaderResourceView, ID3D11SamplerState* samplerState) + { + _nativeShaderResourceView = shaderResourceView; + _nativeShaderResourceView?.AddRef(); + + _nativeSamplerState = samplerState; + _nativeSamplerState?.AddRef(); + } + + public override void AddRef() + { + _nativeShaderResourceView?.AddRef(); + _nativeSamplerState?.AddRef(); + } + + public override void ReleaseRef() + { + _nativeShaderResourceView?.Release(); + _nativeSamplerState?.Release(); + } + } +} diff --git a/GlitchyEngine/src/Renderer/Effect.bf b/GlitchyEngine/src/Renderer/Effect.bf index 33ac255..7a3bb63 100644 --- a/GlitchyEngine/src/Renderer/Effect.bf +++ b/GlitchyEngine/src/Renderer/Effect.bf @@ -123,8 +123,8 @@ namespace GlitchyEngine.Renderer BufferCollection _bufferCollection ~ delete _; BufferVariableCollection _variables ~ delete _; - - typealias TextureEntry = (Texture Texture, ShaderTextureCollection.ResourceEntry* VsSlot, ShaderTextureCollection.ResourceEntry* PsSlot); + + typealias TextureEntry = (TextureViewBinding BoundTexture, ShaderTextureCollection.ResourceEntry* VsSlot, ShaderTextureCollection.ResourceEntry* PsSlot); Dictionary _textures ~ delete _; public Dictionary Textures => _textures; @@ -220,7 +220,7 @@ namespace GlitchyEngine.Renderer for(let entry in _textures) { - entry.value.Texture?.ReleaseRef(); + entry.value.BoundTexture.ReleaseRef(); } } @@ -230,9 +230,32 @@ namespace GlitchyEngine.Renderer ref TextureEntry entry = ref _textures[name]; - entry.Texture?.ReleaseRef(); - entry.Texture = texture; - entry.Texture?.AddRef(); + entry.BoundTexture.ReleaseRef(); + entry.BoundTexture = texture.GetViewBinding(); + } + + public void SetTexture(String name, RenderTargetGroup renderTargetGroup, int32 firstTarget, uint32 targetCount = 1) + { + Debug.Profiler.ProfileRendererFunction!(); + + if (targetCount != 1) + Runtime.NotImplemented("Binding multiple rendertargets to a slot is not yet implemented."); + + ref TextureEntry entry = ref _textures[name]; + + entry.BoundTexture.ReleaseRef(); + //entry.BoundTexture = .RenderTargetGroup(renderTargetGroup..AddRef(), firstTarget, targetCount); + entry.BoundTexture = renderTargetGroup.GetViewBinding(firstTarget); + } + + public void SetTexture(String name, TextureViewBinding textureViewBinding) + { + Debug.Profiler.ProfileRendererFunction!(); + + ref TextureEntry entry = ref _textures[name]; + + entry.BoundTexture.ReleaseRef(); + entry.BoundTexture = textureViewBinding..AddRef(); } private void ApplyTextures() @@ -241,13 +264,13 @@ namespace GlitchyEngine.Renderer for(let (name, entry) in _textures) { - entry.VsSlot?.Texture?.ReleaseRef(); - entry.VsSlot?.Texture = entry.Texture; - entry.VsSlot?.Texture?.AddRef(); + entry.VsSlot?.BoundTexture.ReleaseRef(); + entry.VsSlot?.BoundTexture = entry.BoundTexture; + entry.VsSlot?.BoundTexture.AddRef(); - entry.PsSlot?.Texture?.ReleaseRef(); - entry.PsSlot?.Texture = entry.Texture; - entry.PsSlot?.Texture?.AddRef(); + entry.PsSlot?.BoundTexture.ReleaseRef(); + entry.PsSlot?.BoundTexture = entry.BoundTexture; + entry.PsSlot?.BoundTexture.AddRef(); } } @@ -650,8 +673,8 @@ namespace GlitchyEngine.Renderer // Get existing entry or create new if(!_textures.TryGetValue(shaderEntry.Name, out entry)) { - entry = (shaderEntry.Texture, null, null); - entry.Texture?.AddRef(); + entry = (shaderEntry.BoundTexture, null, null); + entry.BoundTexture.AddRef(); } // Set the corresponding shader resource slot @@ -665,10 +688,10 @@ namespace GlitchyEngine.Renderer } // If the entry has no texture but the shader has one -> set texture - if(entry.Texture == null && shaderEntry.Texture != null) + if(entry.BoundTexture.IsEmpty && !shaderEntry.BoundTexture.IsEmpty) { - entry.Texture = shaderEntry.Texture; - entry.Texture?.AddRef(); + entry.BoundTexture = shaderEntry.BoundTexture; + entry.BoundTexture.AddRef(); } // save entry diff --git a/GlitchyEngine/src/Renderer/Material.bf b/GlitchyEngine/src/Renderer/Material.bf index 2dab070..ff8a17f 100644 --- a/GlitchyEngine/src/Renderer/Material.bf +++ b/GlitchyEngine/src/Renderer/Material.bf @@ -13,7 +13,7 @@ namespace GlitchyEngine.Renderer private uint8[] _rawVariables ~ delete _; - private Dictionary _textures = new .(); + private Dictionary _textures = new .(); private Dictionary _variables = new .() ~ delete _; @@ -27,8 +27,8 @@ namespace GlitchyEngine.Renderer for(let (name, entry) in _effect.Textures) { - var texture = entry.Texture; - texture?.AddRef(); + var texture = entry.BoundTexture; + texture.AddRef(); _textures.Add(name, texture); } @@ -40,7 +40,7 @@ namespace GlitchyEngine.Renderer { for(let (name, texture) in _textures) { - texture?.ReleaseRef(); + texture.ReleaseRef(); } delete _textures; @@ -90,9 +90,9 @@ namespace GlitchyEngine.Renderer { if(_textures.TryGetValue(name, var entry)) { - entry?.ReleaseRef(); - _textures[name] = texture; - texture?.AddRef(); + entry.ReleaseRef(); + _textures[name] = texture.GetViewBinding(); + //texture?.AddRef(); } else { diff --git a/GlitchyEngine/src/Renderer/RenderCommand.bf b/GlitchyEngine/src/Renderer/RenderCommand.bf index b4052cf..a3cd8a1 100644 --- a/GlitchyEngine/src/Renderer/RenderCommand.bf +++ b/GlitchyEngine/src/Renderer/RenderCommand.bf @@ -53,11 +53,21 @@ namespace GlitchyEngine.Renderer _rendererAPI.Clear(renderTarget, options, color, depth, stencil); } + public static void Clear(RenderTargetGroup renderTarget, ClearOptions options, ColorRGBA? color = null, float? depth = null, uint8? stencil = null) + { + _rendererAPI.Clear(renderTarget, options, color, depth, stencil); + } + public static void SetRenderTarget(RenderTarget2D renderTarget, int slot = 0, bool setDepthBuffer = false) { _rendererAPI.SetRenderTarget(renderTarget, slot, setDepthBuffer); } + public static void SetRenderTargetGroup(RenderTargetGroup renderTarget, bool setDepthBuffer = true) + { + _rendererAPI.SetRenderTargetGroup(renderTarget, setDepthBuffer); + } + public static void SetDepthStencilTarget(DepthStencilTarget target) { _rendererAPI.SetDepthStencilTarget(target); diff --git a/GlitchyEngine/src/Renderer/RenderTarget.bf b/GlitchyEngine/src/Renderer/RenderTarget.bf index c2898d5..243bd55 100644 --- a/GlitchyEngine/src/Renderer/RenderTarget.bf +++ b/GlitchyEngine/src/Renderer/RenderTarget.bf @@ -1,4 +1,7 @@ using GlitchyEngine.Core; +using System; +using System.Collections; +using GlitchyEngine.Math; namespace GlitchyEngine.Renderer { @@ -68,5 +71,161 @@ namespace GlitchyEngine.Renderer public extern void Resize(uint32 width, uint32 height); protected extern void PlatformApplyChanges(); + + public override TextureViewBinding GetViewBinding() + { + return PlatformGetViewBinding(); + } + + protected extern TextureViewBinding PlatformGetViewBinding(); + } + + [AllowDuplicates] + public enum RenderTargetFormat : uint8 + { + /// Sets the most significant bit to 1. + const uint8 DepthMarker = 1 << 7; + + case None = 0; + + case R8G8B8A8_UNorm; + case R8G8B8A8_SNorm; + + case R16G16B16A16_SNorm; + case R16G16B16A16_Float; + + case R32G32B32A32_Float; + + case D24_UNorm_S8_UInt = DepthMarker | 1; + + /// Default depth format + case Depth = D24_UNorm_S8_UInt; + + public bool IsDepth => HasFlag(DepthMarker); + } + + public struct TargetDescription + { + public RenderTargetFormat Format = .None; + + public bool IsSwapchainTarget = false; + + public bool IsShaderReadable = true; + + /// Clear color. For DepthStencilBuffers: R is Depth, G is Stencil + public ColorRGBA ClearColor = .Black; + + public SamplerStateDescription SamplerDescription = .(); + + public this() { } + + public this(RenderTargetFormat format, bool isSwapchainTarget = false, bool isShaderReadable = true, ColorRGBA clearColor = .Black, SamplerStateDescription samplerDescription = .()) + { + Format = format; + IsSwapchainTarget = isSwapchainTarget; + IsShaderReadable = isShaderReadable; + SamplerDescription = samplerDescription; + ClearColor = clearColor; + } + + public static implicit operator Self(RenderTargetFormat format) + { + return Self(format); + } + } + + public struct RenderTargetGroupDescription + { + public uint32 Width = 0, Height = 0, ArraySize = 1, MipLevels = 1; + + public uint32 Samples = 1; // TODO: SampleQuality? + + public Span ColorTargetDescriptions = null; + + public TargetDescription DepthTargetDescription = .(.None); + + // TODO: CpuAccess + + public this() { } + + public this(uint32 width, uint32 height, Span colorTargetDescriptions = null, TargetDescription depthTargetDescription = .()) + { + Width = width; + Height = height; + ColorTargetDescriptions = colorTargetDescriptions; + DepthTargetDescription = depthTargetDescription; + } + } + + public class RenderTargetGroup : RefCounter + { + internal RenderTargetGroupDescription _description; + + internal TargetDescription[] _colorTargetDescriptions ~ delete _; + internal SamplerState[] _colorSamplerStates ~ DeleteContainerAndReleaseItems!(_); + internal SamplerState _depthSamplerState ~ _?.ReleaseRef(); + + internal TargetDescription _depthTargetDescription; + + public uint32 Width => _description.Width; + public uint32 Height => _description.Height; + public uint32 ArraySize => _description.ArraySize; + public uint32 MipLevels => _description.MipLevels; + + public uint32 Samples => _description.Samples; + + [AllowAppend] + public this(RenderTargetGroupDescription description) + { + _description = description; + + var colorTargets = description.ColorTargetDescriptions; + + if (!colorTargets.IsNull && !colorTargets.IsEmpty) + { + _colorTargetDescriptions = new TargetDescription[colorTargets.Length]; + _colorSamplerStates = new SamplerState[colorTargets.Length]; + + bool swapchainTargetBound = false; + + for (int i < colorTargets.Length) + { + Log.EngineLogger.AssertDebug(!colorTargets[i].Format.IsDepth, "Cannot use depth format as color target."); + + _colorTargetDescriptions[i] = colorTargets[i]; + + if (colorTargets[i].IsSwapchainTarget) + { + Log.EngineLogger.AssertDebug(!swapchainTargetBound, "Cannot bind swapchaintarget multiple times."); + + swapchainTargetBound = true; + } + + _colorSamplerStates[i] = SamplerStateManager.GetSampler(_colorTargetDescriptions[i].SamplerDescription); + } + } + + _depthTargetDescription = description.DepthTargetDescription; + Log.EngineLogger.AssertDebug(_depthTargetDescription.Format.IsDepth, "Depth target must have depth format."); + + if (_depthTargetDescription.Format != .None) + { + _depthSamplerState = SamplerStateManager.GetSampler(_depthTargetDescription.SamplerDescription); + } + + ApplyChanges(); + } + + public extern void ApplyChanges(); + + public extern void Resize(uint32 width, uint32 height); + + /// -1 for Depthbuffer + public TextureViewBinding GetViewBinding(int index) + { + return PlatformGetViewBinding(index); + } + + protected extern TextureViewBinding PlatformGetViewBinding(int index); } } diff --git a/GlitchyEngine/src/Renderer/Renderer.bf b/GlitchyEngine/src/Renderer/Renderer.bf index ac5cdd9..d2ef627 100644 --- a/GlitchyEngine/src/Renderer/Renderer.bf +++ b/GlitchyEngine/src/Renderer/Renderer.bf @@ -25,14 +25,14 @@ namespace GlitchyEngine.Renderer private uint32 _width; private uint32 _height; - public DepthStencilTarget DepthStencil; + //public DepthStencilTarget DepthStencil; public uint32 Width => _width; public uint32 Height => _height; public Int2 Size => .(_width, _height); - // RGB: Albedo.rgb A: ? + /*// RGB: Albedo.rgb A: ? public RenderTarget2D Albedo ~ _?.ReleaseRef(); // RG: TextureNormal.xy BA: GeometryNormal.xy public RenderTarget2D Normal ~ _?.ReleaseRef(); @@ -41,7 +41,9 @@ namespace GlitchyEngine.Renderer // RGB: Worldspace Position A: ? public RenderTarget2D Position ~ _?.ReleaseRef(); // R: Metallicity G: Roughness B: Ambient A: ? - public RenderTarget2D Material ~ _?.ReleaseRef(); + public RenderTarget2D Material ~ _?.ReleaseRef();*/ + + public RenderTargetGroup Target ~ _?.ReleaseRef(); public void EnsureSize(uint32 width, uint32 height) { @@ -51,7 +53,7 @@ namespace GlitchyEngine.Renderer if (_width == 0 || _height == 0) { // Note: Depth-Buffer in Color-Target for convenience - RenderTarget2DDescription albedoDesc = .(.R8G8B8A8_UNorm, width, height, 1, 1, .D24_UNorm_S8_UInt); + /*RenderTarget2DDescription albedoDesc = .(.R8G8B8A8_UNorm, width, height, 1, 1, .D24_UNorm_S8_UInt); Albedo = new RenderTarget2D(albedoDesc); RenderTarget2DDescription normalDesc = .(.R16G16B16A16_SNorm, width, height); @@ -64,42 +66,66 @@ namespace GlitchyEngine.Renderer Position = new RenderTarget2D(positionDesc); RenderTarget2DDescription materialDesc = .(.R8G8B8A8_UNorm, width, height); - Material = new RenderTarget2D(materialDesc); + Material = new RenderTarget2D(materialDesc);*/ + + SamplerStateDescription desc = .(); + desc.MinFilter = .Point; + desc.MagFilter = .Point; + + RenderTargetGroupDescription targetDesc = .(width, height, + TargetDescription[]( + .(RenderTargetFormat.R8G8B8A8_UNorm){SamplerDescription = desc}, + .(RenderTargetFormat.R16G16B16A16_SNorm){SamplerDescription = desc}, + .(RenderTargetFormat.R16G16B16A16_SNorm){SamplerDescription = desc}, + .(RenderTargetFormat.R32G32B32A32_Float){SamplerDescription = desc}, + .(RenderTargetFormat.R8G8B8A8_UNorm){SamplerDescription = desc} + ), + TargetDescription(.D24_UNorm_S8_UInt){ + SamplerDescription = desc, + ClearColor = .(1.0f, 0, 0) + }); + Target = new RenderTargetGroup(targetDesc); } _width = width; _height = height; - Albedo.Resize(_width, _height); + /*Albedo.Resize(_width, _height); Normal.Resize(_width, _height); Tangent.Resize(_width, _height); Position.Resize(_width, _height); - Material.Resize(_width, _height); + Material.Resize(_width, _height);*/ - DepthStencil = Albedo.DepthStencilTarget; + //DepthStencil = Albedo.DepthStencilTarget; + + Target.Resize(_width, _height); } public void Bind() { - RenderCommand.SetDepthStencilTarget(DepthStencil); + /*RenderCommand.SetDepthStencilTarget(DepthStencil); RenderCommand.UnbindRenderTargets(); RenderCommand.SetRenderTarget(Albedo, 0); RenderCommand.SetRenderTarget(Normal, 1); RenderCommand.SetRenderTarget(Tangent, 2); RenderCommand.SetRenderTarget(Position, 3); - RenderCommand.SetRenderTarget(Material, 4); + RenderCommand.SetRenderTarget(Material, 4);*/ + + RenderCommand.SetRenderTargetGroup(Target); RenderCommand.BindRenderTargets(); } public void Clear() { - RenderCommand.Clear(_gBuffer.Albedo, .Color | .Depth, .HotPink, 1, 0); + /*RenderCommand.Clear(_gBuffer.Albedo, .Color | .Depth, .HotPink, 1, 0); RenderCommand.Clear(_gBuffer.Normal, .HotPink); RenderCommand.Clear(_gBuffer.Tangent, .HotPink); RenderCommand.Clear(_gBuffer.Position, .HotPink); - RenderCommand.Clear(_gBuffer.Material, .HotPink); + RenderCommand.Clear(_gBuffer.Material, .HotPink);*/ + + RenderCommand.Clear(Target, .Color | .Depth); } } @@ -396,13 +422,11 @@ namespace GlitchyEngine.Renderer Vector3 lightDir = -light.Transform.Forward; - _gBuffer.Albedo.SamplerState = SamplerStateManager.PointClamp; - - TestFullscreenEffect.SetTexture("GBuffer_Albedo", _gBuffer.Albedo); - TestFullscreenEffect.SetTexture("GBuffer_Normal", _gBuffer.Normal); - TestFullscreenEffect.SetTexture("GBuffer_Tangent", _gBuffer.Tangent); - TestFullscreenEffect.SetTexture("GBuffer_Position", _gBuffer.Position); - TestFullscreenEffect.SetTexture("GBuffer_Material", _gBuffer.Material); + TestFullscreenEffect.SetTexture("GBuffer_Albedo", _gBuffer.Target, 0); + TestFullscreenEffect.SetTexture("GBuffer_Normal", _gBuffer.Target, 1); + TestFullscreenEffect.SetTexture("GBuffer_Tangent", _gBuffer.Target, 2); + TestFullscreenEffect.SetTexture("GBuffer_Position", _gBuffer.Target, 3); + TestFullscreenEffect.SetTexture("GBuffer_Material", _gBuffer.Target, 4); TestFullscreenEffect.Variables["LightColor"].SetData(light.Light.Color); TestFullscreenEffect.Variables["Illuminance"].SetData(light.Light.Illuminance); diff --git a/GlitchyEngine/src/Renderer/RendererAPI.bf b/GlitchyEngine/src/Renderer/RendererAPI.bf index 6f56c6a..c584d9e 100644 --- a/GlitchyEngine/src/Renderer/RendererAPI.bf +++ b/GlitchyEngine/src/Renderer/RendererAPI.bf @@ -21,6 +21,8 @@ namespace GlitchyEngine.Renderer public extern void Clear(DepthStencilTarget target, ClearOptions options, float depth, uint8 stencil); + public extern void Clear(RenderTargetGroup renderTarget, ClearOptions options, ColorRGBA? color = null, float? depth = null, uint8? stencil = null); + public void Clear(RenderTarget2D renderTarget, ClearOptions options, ColorRGBA color, float depth, uint8 stencil) { Debug.Profiler.ProfileRendererFunction!(); @@ -36,6 +38,8 @@ namespace GlitchyEngine.Renderer public extern void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthBuffer); + public extern void SetRenderTargetGroup(RenderTargetGroup renderTarget, bool setDepthBuffer); + public extern void SetDepthStencilTarget(DepthStencilTarget target); public extern void UnbindRenderTargets(); diff --git a/GlitchyEngine/src/Renderer/ShaderTextureCollection.bf b/GlitchyEngine/src/Renderer/ShaderTextureCollection.bf index 345e01f..acbc1f6 100644 --- a/GlitchyEngine/src/Renderer/ShaderTextureCollection.bf +++ b/GlitchyEngine/src/Renderer/ShaderTextureCollection.bf @@ -3,9 +3,9 @@ using System.Collections; namespace GlitchyEngine.Renderer { - public class ShaderTextureCollection : IEnumerable<(String Name, uint32 Index, Texture Texture)> + public class ShaderTextureCollection : IEnumerable<(String Name, uint32 Index, TextureViewBinding BoundTexture)> { - public typealias ResourceEntry = (String Name, uint32 Index, Texture Texture); + public typealias ResourceEntry = (String Name, uint32 Index, TextureViewBinding BoundTexture); List _textures ~ DeleteTextureEntries!(_); @@ -31,7 +31,7 @@ namespace GlitchyEngine.Renderer for(let entry in entries) { delete entry.Name; - entry.Texture?.ReleaseRef(); + entry.BoundTexture.ReleaseRef(); } delete entries; @@ -39,15 +39,15 @@ namespace GlitchyEngine.Renderer // TODO: finish implementation (like BufferCollection) - public void Add(String name, uint32 index, Texture texture) + public void Add(String name, uint32 index, TextureViewBinding texture) { Add((name, index, texture)); } public void Add(ResourceEntry entry) { - ResourceEntry copy = (new String(entry.Name), entry.Index, entry.Texture); - entry.Texture?.AddRef(); + ResourceEntry copy = (new String(entry.Name), entry.Index, entry.BoundTexture); + entry.BoundTexture.AddRef(); _textures.Add(copy); diff --git a/GlitchyEngine/src/Renderer/Texture.bf b/GlitchyEngine/src/Renderer/Texture.bf index 5c11638..f3d6d9a 100644 --- a/GlitchyEngine/src/Renderer/Texture.bf +++ b/GlitchyEngine/src/Renderer/Texture.bf @@ -27,6 +27,8 @@ namespace GlitchyEngine.Renderer public abstract uint32 Depth {get;} public abstract uint32 ArraySize {get;} public abstract uint32 MipLevels {get;} + + public abstract TextureViewBinding GetViewBinding(); } public struct Texture2DDesc @@ -183,6 +185,13 @@ namespace GlitchyEngine.Renderer * Copies the data to the given destination. */ public extern void CopyTo(Texture2D destination); + + public override TextureViewBinding GetViewBinding() + { + return PlatformGetViewBinding(); + } + + protected extern TextureViewBinding PlatformGetViewBinding(); } public class TextureCube : Texture @@ -212,5 +221,12 @@ namespace GlitchyEngine.Renderer } protected extern void LoadTexturePlatform(Stream stream); + + public override TextureViewBinding GetViewBinding() + { + return PlatformGetViewBinding(); + } + + protected extern TextureViewBinding PlatformGetViewBinding(); } } diff --git a/GlitchyEngine/src/Renderer/TextureViewBinding.bf b/GlitchyEngine/src/Renderer/TextureViewBinding.bf new file mode 100644 index 0000000..3b82f99 --- /dev/null +++ b/GlitchyEngine/src/Renderer/TextureViewBinding.bf @@ -0,0 +1,16 @@ +using System; + +namespace GlitchyEngine.Renderer +{ + /// Represents a reference to a texture that can be used as shader input resource. + public struct TextureViewBinding : IRefCounted, IDisposable + { + /// True if the view binding actually has a texture. False otherwise. + public extern bool IsEmpty { get; } + + public extern void AddRef(); + public extern void ReleaseRef(); + + public void Dispose() => ReleaseRef(); + } +}