From 819476a3ec84a44970f2faf51b0511c21d5dfeef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Wed, 23 Jun 2021 19:11:37 +0200 Subject: [PATCH] Added constructor and SetData to Texture2D obviously this has to be done for the other texture types as well at some point --- GlitchyEngine/src/Math/ResourceBox.bf | 62 ++++++++ .../src/Platform/DX11/Renderer/Dx11Texture.bf | 138 +++++++++++++++++- GlitchyEngine/src/Renderer/Texture.bf | 43 ++++++ 3 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 GlitchyEngine/src/Math/ResourceBox.bf diff --git a/GlitchyEngine/src/Math/ResourceBox.bf b/GlitchyEngine/src/Math/ResourceBox.bf new file mode 100644 index 0000000..a48c155 --- /dev/null +++ b/GlitchyEngine/src/Math/ResourceBox.bf @@ -0,0 +1,62 @@ +using System; + +namespace GlitchyEngine.Math +{ + public struct ResourceBox + { + /// The x position of the left hand side of the box. + public uint32 Left; + /// The y position of the top of the box. + public uint32 Top; + /// The z position of the front of the box. + public uint32 Front; + /// The x position of the right hand side of the box. + public uint32 Right; + /// The y position of the bottom of the box. + public uint32 Bottom; + /// The z position of the back of the box. + public uint32 Back; + + /** + * Returns true if the box is empty. + * A box is empty if the top value is greater than or equal to the bottom value, + * or the left value is greater than or equal to the right value, + * or the front value is greater than or equal to the back value. + */ + public bool IsEmpty => Top >= Bottom || Left >= Right || Front >= Back; + + /** + * Instantiates a new instance of an uninitialized Box structure. + */ + public this() + { + this = default; + } + + /** + * Instantiates a new instance of a Box structure that is initialized with the dimensions of a box. + */ + public this(uint32 left, uint32 top, uint32 front, uint32 right, uint32 bottom, uint32 back) + { + Left = left; + Top = top; + Front = front; + Right = right; + Bottom = bottom; + Back = back; + } + + [Inline] + public static bool operator ==(ResourceBox l, ResourceBox r) + { + return l.Left == r.Left && l.Top == r.Top && l.Front == r.Front && + l.Right == r.Right && l.Bottom == r.Bottom && l.Back == r.Back; + } + + [Inline] + public static bool operator !=(ResourceBox l, ResourceBox r) + { + return !(l == r); + } + } +} diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf index 93a4578..f96de74 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf @@ -2,6 +2,7 @@ using System; using DirectX.D3D11; using DirectX.Common; using DirectXTK; +using GlitchyEngine.Math; using internal GlitchyEngine.Renderer; @@ -34,15 +35,15 @@ namespace GlitchyEngine.Renderer // TODO: missing options desc.SampleDesc = .(1, 0); - desc.Usage = .Immutable; + desc.Usage = (.)Usage; desc.BindFlags = .ShaderResource; - desc.CpuAccessFlags = .None; + desc.CpuAccessFlags = (.)CpuAccess; desc.MiscFlags = .None; return desc; } - public static implicit operator NativeTex2DDesc(Self desc) => desc.ToNative(); + public static explicit operator NativeTex2DDesc(Self desc) => desc.ToNative(); } extension Texture2D @@ -84,7 +85,7 @@ namespace GlitchyEngine.Renderer nativeTexture?.Release(); nativeView?.Release(); - nativeDesc = desc; + nativeDesc = (NativeTex2DDesc)desc; SubresourceData resData = .(data, linePitch, 0); var result = _context.[Friend]nativeDevice.CreateTexture2D(ref nativeDesc, &resData, &nativeTexture); @@ -95,6 +96,135 @@ namespace GlitchyEngine.Renderer Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}"); } + + private void InternalCreateTexture(void* data, uint32 linePitch, uint32 slicePitch) + { + SubresourceData resData = .(data, linePitch, slicePitch); + + // If data is null, set subresourceData null + SubresourceData* resDataPtr = data == null ? null : &resData; + + var result = _context.nativeDevice.CreateTexture2D(ref nativeDesc, resDataPtr, &nativeTexture); + Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}"); + + result = _context.[Friend]nativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeView); + Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}"); + } + + protected override void PrepareTexturePlatform(Texture2DDesc desc) + { + nativeTexture?.Release(); + nativeTexture = null; + nativeView?.Release(); + + nativeDesc = (NativeTex2DDesc)desc; + } + + // TODO: Update Texture Arrays! + protected override System.Result PlatformSetData(void* data, uint32 elementSize, uint32 destX, + uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType) + { + if(nativeTexture == null) + { + if(destX == 0 && destY == 0 && destWidth == nativeDesc.Width && destHeight == nativeDesc.Height) + { + // We can pass the data while creating the buffer, so we can return here. + var rowPitch = elementSize * destWidth; + return InternalCreateTexture(data, rowPitch, rowPitch * destHeight); + } + else + { + // If we don't set the entire texture we need to create the texture with unknown content and set it manually + Log.EngineLogger.Assert(nativeDesc.Usage != .Immutable, "The entire immutable texture has to be initialized."); + InternalCreateTexture(null, 0, 0); + } + } + + //Log.EngineLogger.AssertDebug(); + // TODO: Debug.Assert(dstByteOffset + byteLength <= _description.Size, "The destination offset and byte length are too long for the target buffer."); + + uint32 subresourceIndex = D3D11.CalcSubresource(mipLevel, arraySlice, nativeDesc.MipLevels); + + switch(nativeDesc.Usage) + { + case .Default: + Box dataBox = .(destX, destY, 0, destX + destWidth, destY + destHeight, 1); + var rowPitch = elementSize * destWidth; + _context.nativeContext.UpdateSubresource(nativeTexture, subresourceIndex, &dataBox, data, rowPitch, rowPitch * destHeight); + case .Dynamic: + Runtime.NotImplemented(); + /* + Log.EngineLogger.Assert(mapType.CanWrite, "The map type has to have write access."); + // Todo: DoNotWaitFlag + MappedSubresource map = ?; + _context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map); + Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength); + _context.nativeContext.Unmap(nativeBuffer, 0); + */ + case .Immutable: + Log.EngineLogger.Error("Can't set the data of an immutable resource."); + return .Err; + default: + Log.EngineLogger.Error($"Unknown resource usage: {nativeDesc.Usage}"); + return .Err; + } + + return .Ok; + } + + public static override void CopySubresourceRegion(Texture2D source, Texture2D destination, + ResourceBox sourceBox = default, uint32 destX = 0, uint32 destY = 0, + uint32 srcArraySlice = 0, uint32 srcMipSlice = 0, uint32 destArraySlice = 0, uint32 destMipSlice = 0) + { + Log.EngineLogger.AssertDebug(source != destination || srcArraySlice != destArraySlice + || srcMipSlice != destMipSlice, "Cannot copy from and to the same sub resource."); + + Log.EngineLogger.AssertDebug(source.nativeDesc.Format == destination.nativeDesc.Format, + "Source and destination resources must have the same texel-format."); + + var sourceBox; + + if(sourceBox.Right == 0) + { + sourceBox.Right = source.Width; + sourceBox.Bottom = source.Height; + sourceBox.Back = 1; + } + + // Make sure the destination was initialized + if(destination.nativeTexture == null) + destination.InternalCreateTexture(null, 0, 0); + + source._context.nativeContext.CopySubresourceRegion(destination.nativeTexture, + D3D11.CalcSubresource(destMipSlice, destArraySlice, destination.MipLevels), destX, destY, 0, + source.nativeTexture, D3D11.CalcSubresource(srcMipSlice, srcArraySlice, source.MipLevels), (.)&sourceBox); + } + + public override void CopyTo(Texture2D destination) + { + if(nativeTexture == null) + return; + + Log.EngineLogger.AssertDebug(nativeDesc.Format == destination.nativeDesc.Format, + "Source and destination resources must have the same texel-format."); + + // Make sure the destination was initialized + if(destination.nativeTexture == null) + destination.InternalCreateTexture(null, 0, 0); + + uint32 arraySlices = Math.Min(ArraySize, destination.ArraySize); + uint32 mipSlices = Math.Min(MipLevels, destination.MipLevels); + + for(uint32 arraySlice < arraySlices) + for(uint32 mipSlice < mipSlices) + { + Box sourceBox = .(0, 0, 0, Width, Height, 1); + + _context.nativeContext.CopySubresourceRegion(destination.nativeTexture, + D3D11.CalcSubresource(mipSlice, arraySlice, destination.MipLevels), 0, 0, 0, + nativeTexture, D3D11.CalcSubresource(mipSlice, arraySlice, MipLevels), (.)&sourceBox); + } + } } extension TextureCube diff --git a/GlitchyEngine/src/Renderer/Texture.bf b/GlitchyEngine/src/Renderer/Texture.bf index 6a1db07..a26f079 100644 --- a/GlitchyEngine/src/Renderer/Texture.bf +++ b/GlitchyEngine/src/Renderer/Texture.bf @@ -1,4 +1,5 @@ using System; +using GlitchyEngine.Math; namespace GlitchyEngine.Renderer { @@ -53,6 +54,8 @@ namespace GlitchyEngine.Renderer public uint32 ArraySize; public uint32 MipLevels; public Format Format; + public Usage Usage; + public CPUAccessFlags CpuAccess; } public class Texture2D : Texture @@ -73,9 +76,49 @@ namespace GlitchyEngine.Renderer LoadTexturePlatform(); } + public this(GraphicsContext context, Texture2DDesc desc) : base(context) + { + PrepareTexturePlatform(desc); + } + + public void SetData(T* data, uint32 arraySlice = 0, uint32 mipSlice = 0) + { + PlatformSetData(data, (.)sizeof(T), 0, 0, Width, Height, arraySlice, mipSlice, .Write); + } + + public void SetData(T* data, uint32 x, uint32 y, uint32 width, uint32 height, uint32 arraySlice = 0, uint32 mipSlice = 0) + { + PlatformSetData(data, (.)sizeof(T), x, y, width, height, arraySlice, mipSlice, .Write); + } + + /* + uint32 width, uint32 height, Format format, + uint32 mipLevels = 1, uint32 arraySize = 1, Usage usage = .Default, CPUAccessFlags cpuAccess = .None + */ + protected extern void LoadTexturePlatform(); protected extern void CreateTexturePlatform(Texture2DDesc desc, void* data, uint32 linePitch); + + /** + * Prepares the texture so that a call to SetData can successfully upload the data to the gpu. + */ + protected extern void PrepareTexturePlatform(Texture2DDesc desc); + + protected extern Result PlatformSetData(void* data, uint32 elementSize, uint32 destX, + uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType); + + /** + * Copies the texel-data of this texture to the given destination texture. + */ + public static extern void CopySubresourceRegion(Texture2D source, Texture2D destination, + ResourceBox sourceBox = default, uint32 destX = 0, uint32 destY = 0, + uint32 srcArraySlice = 0, uint32 srcMipSlice = 0, uint32 destArraySlice = 0, uint32 destMipSlice = 0); + + /** + * Copies the data to the given destination. + */ + public extern void CopyTo(Texture2D destination); } public class TextureCube : Texture