diff --git a/GlitchyEditor/src/Assets/Importers/DdsImporter.bf b/GlitchyEditor/src/Assets/Importers/DdsImporter.bf index 6fcfc76..e875d01 100644 --- a/GlitchyEditor/src/Assets/Importers/DdsImporter.bf +++ b/GlitchyEditor/src/Assets/Importers/DdsImporter.bf @@ -48,7 +48,10 @@ static class DdsImporter { Unknown, WrongMagicWord, - StreamReadFailed + StreamReadFailed, + InvalidArraySize, + WrongDimensions, + IncompleteCubemap } private enum HeaderFlags : uint32 @@ -274,45 +277,91 @@ static class DdsImporter DdsPixelFormat pixelFormat = header.PixelFormat; // Currently supports only basic dxgi formats. - if (pixelFormat.SurfaceType.HasFlag(.RGBA)) - { - switch (pixelFormat.RGBBitCount) - { - case 32: - if (IsBitMask(pixelFormat, 0xFF, 0xFF00, 0xFF0000, 0xFF000000)) - return isSrgb ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm; - - if (IsBitMask(pixelFormat, 0xffff, 0xffff0000, 0x0, 0x0)) - return .R16G16_UNorm; - - if (IsBitMask(pixelFormat, 0x3ff, 0xffc00, 0x3ff00000, 0x0)) - return .R10G10B10A2_UNorm; - case 16: - if (IsBitMask(pixelFormat, 0x7c00, 0x3e0, 0x1f, 0x8000)) - return .B5G5R5A1_UNorm; - default: - return .Unknown; - } - } - + //if (pixelFormat.SurfaceType.HasFlag(.RGBA)) if (pixelFormat.SurfaceType.HasFlag(.RGB)) { switch (pixelFormat.RGBBitCount) { case 32: - if (IsBitMask(pixelFormat, 0xffff, 0xffff0000, 0x0, 0x0)) + if (IsBitMask(pixelFormat, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000)) + return isSrgb ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm; + + if (IsBitMask(pixelFormat, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000)) + return isSrgb ? .B8G8R8A8_UNorm_SRGB : .B8G8R8A8_UNorm; + + if (IsBitMask(pixelFormat, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)) + return isSrgb ? .B8G8R8X8_UNorm_SRGB : .B8G8R8X8_UNorm; + + if (IsBitMask(pixelFormat, 0x000003FF, 0x000FFc00, 0x3FF00000, 0)) + return .R10G10B10A2_UNorm; + + if (IsBitMask(pixelFormat, 0x3FF00000, 0x000FFc00, 0x000003FF, 0)) + return .R10G10B10A2_UNorm; + + if (IsBitMask(pixelFormat, 0x0000FFFF, 0xFFFF0000, 0, 0)) return .R16G16_UNorm; - break; + + if (IsBitMask(pixelFormat, 0xFFFFFFFF, 0, 0, 0)) + return .R32_Float; + + case 24: + // NO 24 bit formats + return .Unknown; + case 16: - if (IsBitMask(pixelFormat, 0xf800, 0x7e0, 0x1f, 0x0)) + if (IsBitMask(pixelFormat, 0x7C00, 0x03E0, 0x001F, 0x8000)) + return .B5G5R5A1_UNorm; + + if (IsBitMask(pixelFormat, 0xF800, 0x07E0, 0x001F, 0)) return .B5G6R5_UNorm; - break; + + if (IsBitMask(pixelFormat, 0x0F00, 0x00F0, 0x000F, 0xF000)) + return .B4G4R4A4_UNORM; + + if (IsBitMask(pixelFormat, 0x00FF, 0, 0, 0xFF00)) + return .R8G8_UNorm; + + if (IsBitMask(pixelFormat, 0xFFFF, 0, 0, 0)) + return .R16_UNorm; + + case 8: + if (IsBitMask(pixelFormat, 0xFF, 0, 0, 0)) + return .R8_UNorm; + default: return .Unknown; } } + else if (pixelFormat.SurfaceType.HasFlag(.Luminance)) + { + switch (pixelFormat.RGBBitCount) + { + case 16: + if (IsBitMask(pixelFormat, 0xFFFF, 0, 0, 0)) + return .R16_UNorm; - if (pixelFormat.SurfaceType.HasFlag(.FourCC)) + if (IsBitMask(pixelFormat, 0x00FF, 0, 0, 0xFF00)) + return .R8G8_UNorm; + + case 8: + if (IsBitMask(pixelFormat, 0xFF, 0, 0, 0)) + return .R8_UNorm; + + if (IsBitMask(pixelFormat, 0x00FF, 0, 0, 0xFF00)) + return .R8G8_UNorm; // Some DDS writers assume the bitcount should be 8 instead of 16 + + default: + return .Unknown; + } + } + else if (pixelFormat.SurfaceType.HasFlag(.Luminance)) + { + if (pixelFormat.RGBBitCount == 8) + { + return .A8_UNorm; + } + } + else if (pixelFormat.SurfaceType.HasFlag(.FourCC)) { switch (pixelFormat.FourCC) { @@ -322,15 +371,30 @@ static class DdsImporter return isSrgb ? .BC2_UNorm_SRGB : .BC2_UNorm; case MakeFourCC('D', 'X', 'T', '5'): return isSrgb ? .BC3_UNorm_SRGB : .BC3_UNorm; - // Legacy compression formats. + + // Legacy compression formats. + case MakeFourCC('D', 'X', 'T', '2'): + return isSrgb ? .BC2_UNorm_SRGB : .BC2_UNorm; + case MakeFourCC('D', 'X', 'T', '4'): + return isSrgb ? .BC3_UNorm_SRGB : .BC3_UNorm; + case MakeFourCC('B', 'C', '4', 'U'), MakeFourCC('A', 'T', 'I', '1'): return .BC4_UNorm; - case MakeFourCC('A', 'T', 'I', '2'): + case MakeFourCC('B', 'C', '4', 'S'): + return .BC4_SNorm; + case MakeFourCC('B', 'C', '5', 'U'), MakeFourCC('A', 'T', 'I', '2'): return .BC5_UNorm; + case MakeFourCC('B', 'C', '5', 'S'): + return .BC5_SNorm; + case MakeFourCC('R', 'G', 'B', 'G'): return .R8G8_B8G8_UNorm; case MakeFourCC('G', 'R', 'G', 'B'): return .G8R8_G8B8_UNorm; + + case MakeFourCC('Y', 'U', 'Y', '2'): + return .YUY2; + case 36: return .R16G16B16A16_UNorm; case 111: @@ -353,8 +417,137 @@ static class DdsImporter return .Unknown; } + public static void GetPitch(uint32 width, uint32 height, DirectX.DXGI.Format format, out uint32 rowBytes, out uint32 numRows, out uint64 numBytes) + { + rowBytes = 0; + numRows = 0; + numBytes = 0; + + bool bc = false; + bool packed = false; + bool planar = false; + uint32 bpe = 0; + + switch (format) + { + case .BC1_Typeless, .BC1_UNorm, .BC1_UNorm_SRGB, + .BC4_Typeless, .BC4_UNorm, .BC4_SNorm: + bc = true; + bpe = 8; + break; + + case .BC2_Typeless, .BC2_UNorm, .BC2_UNorm_SRGB, + .BC3_Typeless, .BC3_UNorm, .BC3_UNorm_SRGB, + .BC5_Typeless, .BC5_UNorm, .BC5_SNorm, + .BC6H_Typeless, .BC6H_UF16, .BC6H_SF16, + .BC7_Typeless, .BC7_UNorm, .BC7_UNorm_SRGB: + bc = true; + bpe = 16; + break; + + case .R8G8_B8G8_UNorm, .G8R8_G8B8_UNorm, .YUY2: + packed = true; + bpe = 4; + break; + + case .Y210, .Y216: + packed = true; + bpe = 8; + break; + + case .NV12, .P208, .OPAQUE_420: + planar = true; + bpe = 2; + break; + + case .P010, .P016: + planar = true; + bpe = 4; + break; + + /* + case .D16_UNORM_S8_UINT: + case .R16_UNORM_X8_TYPELESS: + case .X16_TYPELESS_G8_UINT: + planar = true; + bpe = 4; + break; + */ + default: + break; + } + + if (bc) + { + uint32 numBlocksWide = 0; + if (width > 0) + { + numBlocksWide = Math.Max(1, (width + 3u) / 4u); + } + uint32 numBlocksHigh = 0; + if (height > 0) + { + numBlocksHigh = Math.Max(1, (height + 3u) / 4u); + } + rowBytes = numBlocksWide * bpe; + numRows = numBlocksHigh; + numBytes = rowBytes * numBlocksHigh; + } + else if (packed) + { + rowBytes = (((uint32)width + 1u) >> 1) * bpe; + numRows = height; + numBytes = rowBytes * height; + } + else if (format == .NV11) + { + rowBytes = ((width + 3u) >> 2) * 4u; + numRows = height * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data + numBytes = rowBytes * numRows; + } + else if (planar) + { + rowBytes = ((width + 1u) >> 1) * bpe; + numBytes = (rowBytes * height) + ((rowBytes * height + 1u) >> 1); + numRows = height + ((height + 1u) >> 1); + } + else + { + uint32 bpp = format.BitsPerPixel();// BitsPerPixel(format); + if (bpp == 0) + return; + + rowBytes = (width * bpp + 7u) / 8u; // round up to nearest byte + numRows = height; + numBytes = rowBytes * height; + } + + //#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) + /*static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); + if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);*/ + /*#else + static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); + #endif*/ + + /*if (outNumBytes) + { + *outNumBytes = static_cast(numBytes); + } + if (outRowBytes) + { + *outRowBytes = static_cast(rowBytes); + } + if (outNumRows) + { + *outNumRows = static_cast(numRows); + }*/ + } + public static Result LoadDds(Stream data, bool isSrgb, List surfaces, out LoadedTextureInfo textureInfo) { + // TODO: Care about alpha mode + textureInfo = .(); var headerResult = DecodeHeader(data, let header, let extendedHeader); @@ -384,6 +577,9 @@ static class DdsImporter { textureInfo.ArraySize = extendedHeader.Value.ArraySize; surfaceCount *= textureInfo.ArraySize; + + if (textureInfo.ArraySize == 0) + return .Err(.InvalidArraySize); } else { @@ -409,15 +605,28 @@ static class DdsImporter { case .Texture1D: textureInfo.Dimension = .Texture1D; + + if (header.Flags.HasFlag(.DDSD_HEIGHT) && header.Height != 1) + return .Err(.WrongDimensions); + + textureInfo.Height = 1; + textureInfo.Depth = 1; + case .Texture2D: textureInfo.Dimension = .Texture2D; + + textureInfo.Depth = 1; + case .Texture3D: textureInfo.Dimension = .Texture3D; case null: if (header.Flags.HasFlag(.DDSD_DEPTH)) textureInfo.Dimension = .Texture3D; else + { textureInfo.Dimension = .Texture2D; + textureInfo.Depth = 1; + } } // Make sure we have enough space in the list (avoid allocations later) @@ -425,7 +634,7 @@ static class DdsImporter uint32 scanLineSize = 0; uint32 slicePitch = 0; - uint32 numBytes = 0; + uint64 numBytes = 0; uint32 blockSize = 0; // Block size in bytes. switch (textureInfo.PixelFormat) @@ -459,7 +668,7 @@ static class DdsImporter for (uint32 mipLevel = 0; mipLevel < textureInfo.MipMapCount; ++mipLevel) { - // This will recalculate te pitch of the main image as well. + /*// This will recalculate te pitch of the main image as well. if (header.Flags.HasFlag(.DDSD_LINEARSIZE)) { // compressed textures @@ -473,13 +682,22 @@ static class DdsImporter scanLineSize = (width * header.PixelFormat.RGBBitCount + 7u) / 8u; numBytes = scanLineSize * height; } + else + { + scanLineSize = header.PitchOrLinearSize; + numBytes = scanLineSize * height; + }*/ + + GetPitch(width, height, textureInfo.PixelFormat, out scanLineSize, let numRows, out numBytes); + LoadedSurface surface = .(); // We don't know how large data-array is, so we don't have one yet. // Only save the offset, we will adjust the pointer later. - surface.Data = Span((uint8*)(void*)offset, numBytes); + surface.Data = Span((uint8*)(void*)offset, (int)numBytes); surface.Pitch = scanLineSize; - surface.SlicePitch = slicePitch; + //surface.SlicePitch = slicePitch; + surface.SlicePitch = scanLineSize * numRows; surface.ArrayIndex = arrayIndex; surface.CubeFace = cubeFace; diff --git a/GlitchyEditor/src/Assets/TextureAssetLoader.bf b/GlitchyEditor/src/Assets/TextureAssetLoader.bf index 6d5d353..6fc0482 100644 --- a/GlitchyEditor/src/Assets/TextureAssetLoader.bf +++ b/GlitchyEditor/src/Assets/TextureAssetLoader.bf @@ -9,6 +9,7 @@ using GlitchyEngine.Math; using DirectXTK; using ImGui; using GlitchyEditor.Assets.Importers; +using System.Diagnostics; namespace GlitchyEditor.Assets; @@ -165,6 +166,9 @@ class EditorTextureAssetLoaderConfig : AssetLoaderConfig } } +using internal GlitchyEngine.Renderer; +using internal GlitchyEngine.Platform.DX11; + class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader { private static readonly List _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp" @@ -231,6 +235,35 @@ class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader return .Unknown; } + /** \brief Loads the texture from the specified path. + * @param path The path of the texture to load. + * @param texture The reference to the pointer that will hold the texture. + * @returns true if the texture was loaded successfully; false otherwise. + */ + protected static bool LoadDdsResourcePlatform(Stream stream) + { + Debug.Profiler.ProfileResourceFunction!(); + + uint8[] ddsData = new:ScopedAlloc! uint8[stream.Length]; + + var result = stream.TryRead(ddsData); + + if (result case .Err(let err)) + { + Log.EngineLogger.Error($"Failed to read texture data from stream. Error: {err}"); + } + + DirectX.D3D11.ID3D11Texture2D* texture = null; + DirectX.D3D11.ID3D11ShaderResourceView* view = null; + + DirectX.Common.HResult loadResult = DDSTextureLoader.CreateDDSTextureFromMemory(GlitchyEngine.Platform.DX11.NativeDevice, + ddsData.Ptr, (uint)ddsData.Count, (.)&texture, &view); + + view.Release(); + + return true; + } + private static Texture LoadTexture(Stream data, EditorTextureAssetLoaderConfig config) { Debug.Profiler.ProfileResourceFunction!(); @@ -245,6 +278,13 @@ class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader switch(GetTextureType(data)) { case .DDS: + + var v = data.Position; + + //LoadDdsResourcePlatform(data); + + data.Position = v; + Result result = LoadDds(data, config, surfaces, out textureInfo); if (result case .Err) @@ -260,7 +300,13 @@ class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader Log.EngineLogger.Error("Unknown texture format."); return null; } - + + // Make the format SRGB or non-SRGB + if (config.IsSRGB) + textureInfo.PixelFormat = textureInfo.PixelFormat.GetSRGB(); + else + textureInfo.PixelFormat = textureInfo.PixelFormat.GetNonSRGB(); + Texture texture = CreateTexture(surfaces, textureInfo); if (texture != null) @@ -274,6 +320,15 @@ class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader private static Texture CreateTexture(List surfaces, LoadedTextureInfo textureInfo) { + TextureSliceData[] slices = scope TextureSliceData[surfaces.Count]; + + for (int i < slices.Count) + { + slices[i] = .(surfaces[i].Data.Ptr, surfaces[i].Pitch, surfaces[i].SlicePitch); + } + + //slices[0].SlicePitch = (.)(surfaces[0].Pitch * textureInfo.Height); + switch (textureInfo.Dimension) { //case .Texture1D: @@ -296,6 +351,8 @@ class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader TextureCube cubeTexture = new TextureCube(staging); + //cubeTexture.SetData(); + for (let surface in surfaces) { cubeTexture.SetData(surface.Data.Ptr, surface.Pitch / staging.Width, (TextureCubeFace)surface.CubeFace, (.)surface.ArrayIndex, (.)surface.MipLevel); @@ -317,14 +374,16 @@ class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader // TODO: allow enabling read/write staging.CpuAccess = .None; - staging.Usage = .Default; + staging.Usage = .Immutable; Texture2D stagingTexture = new Texture2D(staging); - for (let surface in surfaces) + stagingTexture.SetData(slices); + + /*for (let surface in surfaces) { stagingTexture.[Friend]PlatformSetData(surface.Data.Ptr, surface.Pitch / staging.Width, 0, 0, staging.Width, staging.Height, (.)surface.ArrayIndex, (.)surface.MipLevel, .Write); - } + }*/ return stagingTexture; } diff --git a/GlitchyEditor/src/EditWindows/AssetViewer.bf b/GlitchyEditor/src/EditWindows/AssetViewer.bf index 5cd0df9..a02e96a 100644 --- a/GlitchyEditor/src/EditWindows/AssetViewer.bf +++ b/GlitchyEditor/src/EditWindows/AssetViewer.bf @@ -201,31 +201,6 @@ class TexturererViewerer ColorChannelSwizzle _swizzleB = .B; ColorChannelSwizzle _swizzleA = .A; - public void ViewTexture(RenderTargetGroup texture) - { - ShowSettings(texture.Width, texture.Height, texture.MipLevels - 1, texture.ArraySize - 1, texture); - - UpdateInput(); - - var viewportSize = ImGui.GetContentRegionAvail(); - - 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) - { - _target?.ReleaseRef(); - _target = new RenderTarget2D(.(.R8G8B8A8_UNorm, (.)viewportSize.x, (.)viewportSize.y)); - _target.SamplerState = SamplerStateManager.PointClamp; - _depth?.ReleaseRef(); - _depth = new DepthStencilTarget((.)viewportSize.x, (.)viewportSize.y, .D16_UNorm); - } - - RenderTexture(texture); - - ImGui.Image(_target, viewportSize); - } - public void ShowSettings(float width, float height, int maxMips, int arraySize, RenderTargetGroup rtGroup) { char8*[] items = scope .("White", "Black", "Pink", "Checkerboard", "Custom Color"); @@ -381,6 +356,38 @@ class TexturererViewerer ImGui.EndCombo(); } } + + public void ViewTexture(RenderTargetGroup texture) + { + ShowSettings(texture.Width, texture.Height, texture.MipLevels - 1, texture.ArraySize - 1, texture); + + if (ImGui.BeginChild("imageChild")) + { + UpdateInput(); + + var viewportSize = ImGui.GetContentRegionAvail(); + + 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) + { + _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); + } + + RenderBackground(); + + RenderTexture(texture); + + ImGui.Image(_target, viewportSize); + + ImGui.EndChild(); + } + } public void ViewTexture(Texture texture) { @@ -398,11 +405,13 @@ class TexturererViewerer if(_target == null || viewportSize.x != _target.Width || viewportSize.y != _target.Height) { _target?.ReleaseRef(); - _target = new RenderTarget2D(.(.R8G8B8A8_UNorm, (.)viewportSize.x, (.)viewportSize.y)); + _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); } + + RenderBackground(); RenderTexture(texture); @@ -461,7 +470,7 @@ class TexturererViewerer OrthographicCamera _camera = new OrthographicCamera() ~ delete _; - private void RenderTexture(RenderTargetGroup viewedTexture) + private void RenderBackground() { Viewport vp = .(0, 0, _target.Width, _target.Height); RenderCommand.SetViewport(vp); @@ -475,9 +484,6 @@ class TexturererViewerer RenderCommand.SetDepthStencilTarget(_depth); RenderCommand.BindRenderTargets(); - float2 textureSize = float2(viewedTexture.Width, viewedTexture.Height); - float2 zoomedTextureSize = textureSize * _zoom; - float2 targetSize = float2(_target.Width, _target.Height); _camera.Left = 0; @@ -489,7 +495,7 @@ class TexturererViewerer _camera.Update(); Renderer2D.BeginScene(_camera); - + switch(_backgroundMode) { case .Black: @@ -516,6 +522,17 @@ class TexturererViewerer } Renderer2D.EndScene(); + } + + private void RenderTexture(TextureViewBinding viewedTexture, float2 textureSize, Format format) + { + float2 mippedTextureSize = float2((int)textureSize.X >> _mipLevel, (int)textureSize.X >> _mipLevel); + + // Sicherstellen, dass die Auflösung nicht 0 wird + mippedTextureSize = max(mippedTextureSize, 1); + + //float2 textureSize = float2(viewedTexture.Width, viewedTexture.Height); + float2 zoomedTextureSize = textureSize * _zoom; // TODO: Sampler state for rt group //var sampler = viewedTexture.SamplerState; @@ -542,36 +559,37 @@ class TexturererViewerer _renderTargetEffect.Variables["AlphaOffset"].SetData(_alphaOffset); _renderTargetEffect.Variables["AlphaScale"].SetData(_alphaScale); - _renderTargetEffect.Variables["Texels"].SetData(float2(viewedTexture.Width, viewedTexture.Height)); + _renderTargetEffect.Variables["Texels"].SetData(mippedTextureSize); _renderTargetEffect.Variables["MipLevel"].SetData((float)_mipLevel); - + _renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA)); - var desc = _groupIndex >= 0 ? viewedTexture.[Friend]_colorTargetDescriptions[_groupIndex] : viewedTexture.[Friend]_depthTargetDescription; - - if (desc.Format.IsInt()) + if (format.IsInt()) { + // Int Texture _renderTargetEffect.Variables["Mode"].SetData(1); - _renderTargetEffect.SetTexture("IntTexture", viewedTexture.GetViewBinding(_groupIndex)); + _renderTargetEffect.SetTexture("IntTexture", viewedTexture); _renderTargetEffect.SetTexture("UIntTexture", null); _renderTargetEffect.SetTexture("Texture", null); } - else if (desc.Format.IsUInt()) + else if (format.IsUInt()) { + // UInt Texture _renderTargetEffect.Variables["Mode"].SetData(2); _renderTargetEffect.SetTexture("IntTexture", null); - _renderTargetEffect.SetTexture("UIntTexture", viewedTexture.GetViewBinding(_groupIndex)); + _renderTargetEffect.SetTexture("UIntTexture", viewedTexture); _renderTargetEffect.SetTexture("Texture", null); } else { + // Float Texture _renderTargetEffect.Variables["Mode"].SetData(0); _renderTargetEffect.SetTexture("IntTexture", null); _renderTargetEffect.SetTexture("UIntTexture", null); - _renderTargetEffect.SetTexture("Texture", viewedTexture.GetViewBinding(_groupIndex)); + _renderTargetEffect.SetTexture("Texture", viewedTexture); } - + _renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA)); _renderTargetEffect.ApplyChanges(); @@ -580,84 +598,15 @@ class TexturererViewerer Quad.Draw(); } + private void RenderTexture(RenderTargetGroup viewedTexture) + { + var desc = _groupIndex >= 0 ? viewedTexture.[Friend]_colorTargetDescriptions[_groupIndex] : viewedTexture.[Friend]_depthTargetDescription; + + RenderTexture(viewedTexture.GetViewBinding(_groupIndex), float2(viewedTexture.Width, viewedTexture.Height), desc.Format.GetShaderViewFormat()); + } + private void RenderTexture(Texture viewedTexture) { - Viewport vp = .(0, 0, _target.Width, _target.Height); - RenderCommand.SetViewport(vp); - - // TODO: don't clear pink! - RenderCommand.Clear(_target, .Pink); - RenderCommand.Clear(_depth, .Depth, 1.0f, 0); - - //_target.Bind(); - RenderCommand.SetRenderTarget(_target); - RenderCommand.SetDepthStencilTarget(_depth); - RenderCommand.BindRenderTargets(); - - float2 textureSize = float2(viewedTexture.Width, viewedTexture.Height); - float2 zoomedTextureSize = textureSize * _zoom; - - float2 targetSize = float2(_target.Width, _target.Height); - - _effect.Variables["ColorOffset"].SetData(_colorOffset); - _effect.Variables["ColorScale"].SetData(_colorScale); - _effect.Variables["AlphaOffset"].SetData(_alphaOffset); - _effect.Variables["AlphaScale"].SetData(_alphaScale); - - _camera.Left = 0; - _camera.Top = 0; - _camera.Right = targetSize.X; - _camera.Bottom = -targetSize.Y; - _camera.NearPlane = -5; - _camera.FarPlane = 5; - _camera.Update(); - - Renderer2D.BeginScene(_camera); - - switch(_backgroundMode) - { - case .Black: - Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, .Black); - case .White: - Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, .White); - case .Pink: - Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, .HotPink); - case .CustomColor: - Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, _backgroundColor); - case .Checkerboard: - float quadSize = 50.0f; - - float2 numQuads = (targetSize / 500f) * 10f; - - for(float x = 0; x < numQuads.X; x++) - { - for(float y = 0; y < numQuads.Y; y++) - { - Renderer2D.DrawQuadPivotCorner(float3(x * quadSize, -y * quadSize, 1), quadSize.XX, 0, ((x + y) % 2 == 0) ? .White : .Gray); - } - } - break; - } - - Renderer2D.EndScene(); - - var sampler = viewedTexture.SamplerState..AddRef(); - - switch(_sampleMode) - { - case .Point: - viewedTexture.SamplerState = SamplerStateManager.PointClamp; - case .Linear: - viewedTexture.SamplerState = SamplerStateManager.LinearClamp; - } - - Renderer2D.BeginScene(_camera, .SortByTexture, _effect); - - Renderer2D.DrawQuad(float3(_position * .(1, -1), 0), zoomedTextureSize, 0, viewedTexture); - - Renderer2D.EndScene(); - - viewedTexture.SamplerState = sampler; - sampler.ReleaseRef(); + RenderTexture(viewedTexture.GetViewBinding(), float2(viewedTexture.Width, viewedTexture.Height), viewedTexture.Format); } } diff --git a/GlitchyEngine/src/Math/floatVector.bf b/GlitchyEngine/src/Math/floatVector.bf index 851dd88..7f0f57c 100644 --- a/GlitchyEngine/src/Math/floatVector.bf +++ b/GlitchyEngine/src/Math/floatVector.bf @@ -70,7 +70,7 @@ public struct float4 public const float4 UnitZ = .(0f, 0f, 1f, 0f); public const float4 UnitW = .(0f, 0f, 0f, 1f); public const float4 One = .(1f, 1f, 1f, 1f); - + public static implicit operator int4(float4 value) { return int4((int32)value.X, (int32)value.Y, (int32)value.Z, (int32)value.W); diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf index 8b9d555..52877ea 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Texture.bf @@ -56,31 +56,65 @@ namespace GlitchyEngine.Renderer public override uint32 Height => nativeDesc.Height; public override uint32 ArraySize => nativeDesc.ArraySize; public override uint32 MipLevels => nativeDesc.MipLevels; + public override Format Format => nativeDesc.Format; - protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch) + + /*protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch) { Debug.Profiler.ProfileResourceFunction!(); PrepareTexturePlatform(desc, isRenderTarget); InternalCreateTexture(data, linePitch, 0); - } + }*/ - private void InternalCreateTexture(void* data, uint32 linePitch, uint32 slicePitch) + /// Creates the native texture. Initializes it, if slices is not null and has enough Entries to fill all slices. + /// @returns true if the texture has been initialized; false otherwise. + private bool InternalCreateTexture(Span slices) { Debug.Profiler.ProfileResourceFunction!(); - SubresourceData resData = .(data, linePitch, slicePitch); + SubresourceData[] data = null; - // If data is null, set subresourceData null - SubresourceData* resDataPtr = data == null ? null : &resData; + if (slices.Length == MipLevels * ArraySize) + { + data = scope:: SubresourceData[slices.Length]; - var result = NativeDevice.CreateTexture2D(ref nativeDesc, resDataPtr, &nativeTexture); + for (int i < slices.Length) + { + TextureSliceData slice = slices[i]; + + data[i] = .(slice.Data, slice.LinePitch, slice.SlicePitch); + } + } + + var result = NativeDevice.CreateTexture2D(ref nativeDesc, data?.Ptr, &nativeTexture); Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}"); result = NativeDevice.CreateShaderResourceView(nativeTexture, null, &_nativeResourceView); Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}"); + + // If Data is non-null the texture got initialized with data. + return (data != null); } + /*private void InternalCreateTexture(void* data, uint32 linePitch, uint32 slicePitch) + { + Debug.Profiler.ProfileResourceFunction!(); + + /*SubresourceData resData = .(data, linePitch, slicePitch); + + // If data is null, set subresourceData null + SubresourceData* resDataPtr = data == null ? null : &resData;*/ + + SubresourceData[2] resData = .(.(data, linePitch, slicePitch), .(null, 0, 0)); + + var result = NativeDevice.CreateTexture2D(ref nativeDesc, &resData, &nativeTexture); + Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}"); + + result = NativeDevice.CreateShaderResourceView(nativeTexture, null, &_nativeResourceView); + Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}"); + }*/ + protected override void PrepareTexturePlatform(Texture2DDesc desc, bool isRenderTarget) { Debug.Profiler.ProfileResourceFunction!(); @@ -96,6 +130,26 @@ namespace GlitchyEngine.Renderer nativeDesc.BindFlags |= .RenderTarget; } + protected override Result PlatformSetData(Span slices) + { + Debug.Profiler.ProfileResourceFunction!(); + + if(nativeTexture == null) + { + if (InternalCreateTexture(slices)) + return .Ok; + } + + Runtime.NotImplemented(); + + /*int index = firstArraySlice; + + for (TextureSliceData slice in slices) + { + + }*/ + } + // TODO: Update Texture Arrays! protected override Result PlatformSetData(void* data, uint32 elementSize, uint32 destX, uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType) @@ -104,6 +158,21 @@ namespace GlitchyEngine.Renderer if(nativeTexture == null) { + if(destX == 0 && destY == 0 && destWidth == nativeDesc.Width && destHeight == nativeDesc.Height && + arraySlice == 0 && mipLevel == 0 && ArraySize == 1 && MipLevels == 1) + { + TextureSliceData[1] dataSlice = .(.(data, elementSize * destWidth, elementSize * destWidth * destHeight)); + + // Turns out we need to initialize all slices at once or none... so... tough shit, we cant early out here. + if (InternalCreateTexture(dataSlice)) + return .Ok; + } + else + { + InternalCreateTexture(null); + } + + /* Old shit follows... 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. @@ -116,6 +185,7 @@ namespace GlitchyEngine.Renderer Log.EngineLogger.Assert(nativeDesc.Usage != .Immutable, "The entire immutable texture has to be initialized."); InternalCreateTexture(null, 0, 0); } + */ } //Log.EngineLogger.AssertDebug(); @@ -178,7 +248,7 @@ namespace GlitchyEngine.Renderer // Make sure the destination was initialized if(destination.nativeTexture == null) - destination.InternalCreateTexture(null, 0, 0); + destination.InternalCreateTexture(null); using (ContextMonitor.Enter()) { @@ -200,7 +270,7 @@ namespace GlitchyEngine.Renderer // Make sure the destination was initialized if(destination.nativeTexture == null) - destination.InternalCreateTexture(null, 0, 0); + destination.InternalCreateTexture(null); uint32 arraySlices = Math.Min(ArraySize, destination.ArraySize); uint32 mipSlices = Math.Min(MipLevels, destination.MipLevels); @@ -234,6 +304,7 @@ namespace GlitchyEngine.Renderer public override uint32 Height => nativeDesc.Height; public override uint32 ArraySize => nativeDesc.ArraySize / 6; public override uint32 MipLevels => nativeDesc.MipLevels; + public override Format Format => nativeDesc.Format; protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch) { diff --git a/GlitchyEngine/src/Renderer/Buffer.bf b/GlitchyEngine/src/Renderer/Buffer.bf index 6281cf1..04cc11e 100644 --- a/GlitchyEngine/src/Renderer/Buffer.bf +++ b/GlitchyEngine/src/Renderer/Buffer.bf @@ -1,6 +1,139 @@ using System; using GlitchyEngine.Core; +namespace DirectX.DXGI +{ + public extension Format + { + public bool IsInt() + { + switch(this) + { + case R32G32B32A32_SInt, R32G32B32_SInt, R16G16B16A16_SInt, R32G32_SInt, R8G8B8A8_SInt, R16G16_SInt, + R32_SInt, R8G8_SInt, R16_SInt, R8_SInt: + return true; + default: + return false; + } + } + + public bool IsUInt() + { + switch(this) + { + case R32G32B32A32_UInt, R32G32B32_UInt, R16G16B16A16_UInt, R32G32_UInt, R10G10B10A2_UInt, R8G8B8A8_UInt, R16G16_UInt, + R32_UInt, R8G8_UInt, R16_UInt, R8_UInt: + return true; + default: + return false; + } + } + + public uint32 BitsPerPixel() + { + switch (this) + { + case .R32G32B32A32_Typeless,.R32G32B32A32_Float,.R32G32B32A32_UInt,.R32G32B32A32_SInt: + return 128; + + case .R32G32B32_Typeless,.R32G32B32_Float,.R32G32B32_UInt,.R32G32B32_SInt: + return 96; + + case .R16G16B16A16_Typeless,.R16G16B16A16_Float,.R16G16B16A16_UNorm,.R16G16B16A16_UInt,.R16G16B16A16_SNorm,.R16G16B16A16_SInt,.R32G32_Typeless,.R32G32_Float,.R32G32_UInt,.R32G32_SInt,.R32G8X24_Typeless,.D32_Float_S8X24_UInt,.R32_Float_X8X24_Typeless,.X32_Typeless_G8X24_UInt,.Y416,.Y210,.Y216: + return 64; + + case .R10G10B10A2_Typeless,.R10G10B10A2_UNorm,.R10G10B10A2_UInt,.R11G11B10_Float,.R8G8B8A8_Typeless,.R8G8B8A8_UNorm,.R8G8B8A8_UNorm_SRGB,.R8G8B8A8_UInt,.R8G8B8A8_SNorm,.R8G8B8A8_SInt,.R16G16_Typeless,.R16G16_Float,.R16G16_UNorm,.R16G16_UInt,.R16G16_SNorm,.R16G16_SInt,.R32_Typeless,.D32_Float,.R32_Float,.R32_UInt,.R32_SInt,.R24G8_Typeless,.D24_UNorm_S8_UInt,.R24_UNorm_X8_Typeless,.X24_Typeless_G8_UInt,.R9G9B9E5_SHAREDEXP,.R8G8_B8G8_UNorm,.G8R8_G8B8_UNorm,.B8G8R8A8_UNorm,.B8G8R8X8_UNorm,.R10G10B10_XR_BIAS_A2_UNorm,.B8G8R8A8_Typeless,.B8G8R8A8_UNorm_SRGB,.B8G8R8X8_Typeless,.B8G8R8X8_UNorm_SRGB,.AYUV,.Y410,.YUY2: + //#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + //case .R10G10B10_7E3_A2_Float,.R10G10B10_6E4_A2_Float,.R10G10B10_SNorm_A2_UNorm: + //#endif + return 32; + + case .P010,.P016, .V408: + /*#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + case .V408: + #endif + #if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case .D16_UNorm_S8_UInt,.R16_UNorm_X8_Typeless,.X16_Typeless_G8_UInt: + #endif*/ + return 24; + + case .R8G8_Typeless,.R8G8_UNorm,.R8G8_UInt,.R8G8_SNorm,.R8G8_SInt,.R16_Typeless,.R16_Float,.D16_UNorm,.R16_UNorm,.R16_UInt,.R16_SNorm,.R16_SInt,.B5G6R5_UNorm,.B5G5R5A1_UNorm,.A8P8,.B4G4R4A4_UNORM, .P208,.V208: + /*#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + case .P208,.V208: + #endif*/ + return 16; + + case .NV12,.OPAQUE_420,.NV11: + return 12; + + case .R8_Typeless,.R8_UNorm,.R8_UInt,.R8_SNorm,.R8_SInt,.A8_UNorm,.BC2_Typeless,.BC2_UNorm,.BC2_UNorm_SRGB,.BC3_Typeless,.BC3_UNorm,.BC3_UNorm_SRGB,.BC5_Typeless,.BC5_UNorm,.BC5_SNorm,.BC6H_Typeless,.BC6H_UF16,.BC6H_SF16,.BC7_Typeless,.BC7_UNorm,.BC7_UNorm_SRGB,.AI44,.IA44,.P8: + /*#if (defined(_XBOX_ONE) && defined(_TITLE)) || defined(_GAMING_XBOX) + case .R4G4_UNorm: + #endif*/ + return 8; + + case .R1_UNorm: + return 1; + + case .BC1_Typeless,.BC1_UNorm,.BC1_UNorm_SRGB,.BC4_Typeless,.BC4_UNorm,.BC4_SNorm: + return 4; + + //case .Unknown,.FORCE_UInt: + default: + return 0; + } + } + + /// Returns the SRGB-Format for the given Format, or the Format itself, if no SRGB-variant exists. + public Format GetSRGB() + { + switch (this) + { + case .BC1_UNorm: + return .BC1_UNorm_SRGB; + case .BC2_UNorm: + return .BC2_UNorm_SRGB; + case .BC3_UNorm: + return .BC3_UNorm_SRGB; + case .BC7_UNorm: + return .BC7_UNorm_SRGB; + case .R8G8B8A8_UNorm: + return .R8G8B8A8_UNorm_SRGB; + case .B8G8R8A8_UNorm: + return .B8G8R8A8_UNorm_SRGB; + case .B8G8R8X8_UNorm: + return .B8G8R8X8_UNorm_SRGB; + default: + return this; + } + } + + /// Returns the non-SRGB-Format for the given Format. + public Format GetNonSRGB() + { + switch (this) + { + case .BC1_UNorm_SRGB: + return .BC1_UNorm; + case .BC2_UNorm_SRGB: + return .BC2_UNorm; + case .BC3_UNorm_SRGB: + return .BC3_UNorm; + case .BC7_UNorm_SRGB: + return .BC7_UNorm; + case .R8G8B8A8_UNorm_SRGB: + return .R8G8B8A8_UNorm; + case .B8G8R8A8_UNorm_SRGB: + return .B8G8R8A8_UNorm; + case .B8G8R8X8_UNorm_SRGB: + return .B8G8R8X8_UNorm; + default: + return this; + } + } + } +} + namespace GlitchyEngine.Renderer { /** @@ -29,6 +162,7 @@ namespace GlitchyEngine.Renderer typealias Format = DirectX.DXGI.Format; + public struct BufferDescription { /** diff --git a/GlitchyEngine/src/Renderer/RenderTarget.bf b/GlitchyEngine/src/Renderer/RenderTarget.bf index c535b7b..8d063f9 100644 --- a/GlitchyEngine/src/Renderer/RenderTarget.bf +++ b/GlitchyEngine/src/Renderer/RenderTarget.bf @@ -48,6 +48,7 @@ namespace GlitchyEngine.Renderer public override uint32 Depth => 1; public override uint32 ArraySize => _description.ArraySize; public override uint32 MipLevels => _description.MipLevels; + public override Format Format => _description.PixelFormat; protected internal DepthStencilTarget _depthStenilTarget ~ _?.ReleaseRef(); diff --git a/GlitchyEngine/src/Renderer/Renderer2D.bf b/GlitchyEngine/src/Renderer/Renderer2D.bf index 88ab958..15a36b4 100644 --- a/GlitchyEngine/src/Renderer/Renderer2D.bf +++ b/GlitchyEngine/src/Renderer/Renderer2D.bf @@ -368,7 +368,9 @@ namespace GlitchyEngine.Renderer s_whiteTexture = new Texture2D(tex2Ddesc); Color color = .White; - s_whiteTexture.SetData(&color); + + TextureSliceData[1] data = .(.(&color, sizeof(Color), sizeof(Color))); + s_whiteTexture.SetData(data); // Create the default sampler for the white texture SamplerState sampler = SamplerStateManager.GetSampler(SamplerStateDescription()); diff --git a/GlitchyEngine/src/Renderer/Texture.bf b/GlitchyEngine/src/Renderer/Texture.bf index 14b8eaf..ada746d 100644 --- a/GlitchyEngine/src/Renderer/Texture.bf +++ b/GlitchyEngine/src/Renderer/Texture.bf @@ -29,6 +29,8 @@ namespace GlitchyEngine.Renderer public abstract uint32 ArraySize {get;} public abstract uint32 MipLevels {get;} + public abstract Format Format {get;} + public abstract TextureViewBinding GetViewBinding(); } @@ -56,6 +58,22 @@ namespace GlitchyEngine.Renderer } } + /// Represents the Data of one Texture Slice (a specific mip-level of a array-slice of the texture) + /// @remarks Basically works like described here: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_subresource_data + public struct TextureSliceData + { + public void* Data; + public uint32 LinePitch; + public uint32 SlicePitch; + + public this(void* data, uint32 linePitch, uint32 slicePitch) + { + Data = data; + LinePitch = linePitch; + SlicePitch = slicePitch; + } + } + public class Texture2D : Texture { //public override extern uint32 Width {get;} @@ -69,6 +87,17 @@ namespace GlitchyEngine.Renderer PrepareTexturePlatform(desc, false); } + public Result SetData(Span slices) + { + if (slices.Length != ArraySize * MipLevels) + { + Log.EngineLogger.Error("Not enough slices provided to initialize texture."); + return .Err; + } + + return PlatformSetData(slices); + } + public void SetData(T* data, uint32 arraySlice = 0, uint32 mipSlice = 0) { PlatformSetData(data, (.)sizeof(T), 0, 0, Width, Height, arraySlice, mipSlice, .Write); @@ -84,8 +113,6 @@ namespace GlitchyEngine.Renderer uint32 mipLevels = 1, uint32 arraySize = 1, Usage usage = .Default, CPUAccessFlags cpuAccess = .None */ - protected extern void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch); - /** * Prepares the texture so that a call to SetData can successfully upload the data to the gpu. */ @@ -93,6 +120,8 @@ namespace GlitchyEngine.Renderer protected extern Result PlatformSetData(void* data, uint32 elementSize, uint32 destX, uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType); + + protected extern Result PlatformSetData(Span slices); /** * Copies the texel-data of this texture to the given destination texture.