From 4bf73c71e06bce797ab3297732567510e7590d5d Mon Sep 17 00:00:00 2001 From: MeltyPlayer Date: Thu, 28 Nov 2024 23:10:36 -0600 Subject: [PATCH] Passed data directly from ImageSharp images into OpenGL textures rather than needlessly copying it to a new span. --- .../Fin/Fin.Ui/src/rendering/gl/GlTexture.cs | 76 ++++++++++++------- .../Fin/Fin/src/image/formats/L8Image.cs | 3 - .../Fin/Fin/src/image/formats/La16Image.cs | 3 - .../Fin/Fin/src/image/formats/Rgb24Image.cs | 3 - .../Fin/Fin/src/image/formats/Rgba32Image.cs | 3 - 5 files changed, 49 insertions(+), 39 deletions(-) diff --git a/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/GlTexture.cs b/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/GlTexture.cs index 5c53f6d60..3217bb672 100644 --- a/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/GlTexture.cs +++ b/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/GlTexture.cs @@ -60,7 +60,7 @@ private GlTexture(IReadOnlyTexture texture) { GL.BindTexture(target, this.id_); { var mipmapImages = texture.MipmapImages; - + this.LoadMipmapImagesIntoTexture_(mipmapImages); if (mipmapImages.Length == 1 && @@ -138,7 +138,7 @@ or FinTextureMinFilter.LINEAR_MIPMAP_NEAR GL.BindTexture(target, UNDEFINED_ID); } - private static readonly ArrayPool pool_ = ArrayPool.Shared; + private static readonly MemoryPool pool_ = MemoryPool.Shared; private void LoadMipmapImagesIntoTexture_(IReadOnlyImage[] mipmapImages) { for (var i = 0; i < mipmapImages.Length; ++i) { @@ -146,48 +146,58 @@ private void LoadMipmapImagesIntoTexture_(IReadOnlyImage[] mipmapImages) { } } - private void LoadImageIntoTexture_(IReadOnlyImage image, int level) { + private unsafe void LoadImageIntoTexture_(IReadOnlyImage image, int level) { var imageWidth = image.Width; var imageHeight = image.Height; PixelInternalFormat pixelInternalFormat; PixelFormat pixelFormat; - byte[] pixelBytes; switch (image) { case Rgba32Image rgba32Image: { - pixelBytes = pool_.Rent(4 * imageWidth * imageHeight); - pixelInternalFormat = PixelInternalFormat.Rgba; - pixelFormat = PixelFormat.Rgba; - rgba32Image.GetRgba32Bytes(pixelBytes.AsSpan().Cast()); + using var fastLock = rgba32Image.UnsafeLock(); + PassBytesIntoImage_(level, + PixelInternalFormat.Rgba, + imageWidth, + imageHeight, + PixelFormat.Rgba, + fastLock.byteScan0); break; } case Rgb24Image rgb24Image: { - pixelBytes = pool_.Rent(3 * imageWidth * imageHeight); - pixelInternalFormat = PixelInternalFormat.Rgb; - pixelFormat = PixelFormat.Rgb; - rgb24Image.GetRgb24Bytes(pixelBytes.AsSpan().Cast()); + using var fastLock = rgb24Image.UnsafeLock(); + PassBytesIntoImage_(level, + PixelInternalFormat.Rgb, + imageWidth, + imageHeight, + PixelFormat.Rgb, + fastLock.byteScan0); break; } case La16Image ia16Image: { - pixelBytes = pool_.Rent(2 * imageWidth * imageHeight); - pixelInternalFormat = PixelInternalFormat.LuminanceAlpha; - pixelFormat = PixelFormat.LuminanceAlpha; - ia16Image.GetIa16Bytes(pixelBytes.AsSpan().Cast()); + using var fastLock = ia16Image.UnsafeLock(); + PassBytesIntoImage_(level, + PixelInternalFormat.LuminanceAlpha, + imageWidth, + imageHeight, + PixelFormat.LuminanceAlpha, + fastLock.byteScan0); break; } case L8Image i8Image: { - pixelBytes = pool_.Rent(imageWidth * imageHeight); - pixelInternalFormat = PixelInternalFormat.Luminance; - pixelFormat = PixelFormat.Luminance; - i8Image.GetI8Bytes(pixelBytes.AsSpan().Cast()); + using var fastLock = i8Image.UnsafeLock(); + PassBytesIntoImage_(level, + PixelInternalFormat.Luminance, + imageWidth, + imageHeight, + PixelFormat.Luminance, + fastLock.byteScan0); break; } default: { - pixelBytes = pool_.Rent(4 * imageWidth * imageHeight); - pixelInternalFormat = PixelInternalFormat.Rgba; - pixelFormat = PixelFormat.Rgba; + using var rentedBytes = pool_.Rent(4 * imageWidth * imageHeight); image.Access(getHandler => { + var pixelBytes = rentedBytes.Memory.Span; for (var y = 0; y < imageHeight; y++) { for (var x = 0; x < imageWidth; x++) { getHandler(x, @@ -197,7 +207,7 @@ private void LoadImageIntoTexture_(IReadOnlyImage image, int level) { out var b, out var a); - var outI = 4 * (y * imageWidth + x); + var outI = 4 * (y * imageWidth + x); pixelBytes[outI] = r; pixelBytes[outI + 1] = g; pixelBytes[outI + 2] = b; @@ -205,10 +215,24 @@ private void LoadImageIntoTexture_(IReadOnlyImage image, int level) { } } }); + PassBytesIntoImage_(level, + PixelInternalFormat.Rgba, + imageWidth, + imageHeight, + PixelFormat.Rgba, + (byte*) rentedBytes.Memory.Pin().Pointer); break; } } + } + private static unsafe void PassBytesIntoImage_( + int level, + PixelInternalFormat pixelInternalFormat, + int imageWidth, + int imageHeight, + PixelFormat pixelFormat, + byte* scan0) { // This is required to fix a rare issue with alignment: // https://stackoverflow.com/questions/52460143/texture-not-showing-correctly GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); @@ -220,9 +244,7 @@ private void LoadImageIntoTexture_(IReadOnlyImage image, int level) { 0, pixelFormat, PixelType.UnsignedByte, - pixelBytes); - - pool_.Return(pixelBytes); + (IntPtr) scan0); } ~GlTexture() => this.ReleaseUnmanagedResources_(); diff --git a/FinModelUtility/Fin/Fin/src/image/formats/L8Image.cs b/FinModelUtility/Fin/Fin/src/image/formats/L8Image.cs index 9949a3f86..9ecd742eb 100644 --- a/FinModelUtility/Fin/Fin/src/image/formats/L8Image.cs +++ b/FinModelUtility/Fin/Fin/src/image/formats/L8Image.cs @@ -37,7 +37,4 @@ void InternalGetHandler( }); public override bool HasAlphaChannel => false; - - public void GetI8Bytes(Span dst) - => this.Impl.CopyPixelDataTo(dst); } \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin/src/image/formats/La16Image.cs b/FinModelUtility/Fin/Fin/src/image/formats/La16Image.cs index 3ca2c82a3..4d9c80bce 100644 --- a/FinModelUtility/Fin/Fin/src/image/formats/La16Image.cs +++ b/FinModelUtility/Fin/Fin/src/image/formats/La16Image.cs @@ -36,7 +36,4 @@ void GetHandler( } public override bool HasAlphaChannel => true; - - public void GetIa16Bytes(Span dst) - => this.Impl.CopyPixelDataTo(dst); } \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin/src/image/formats/Rgb24Image.cs b/FinModelUtility/Fin/Fin/src/image/formats/Rgb24Image.cs index 43d0bdb4e..1f9ceffeb 100644 --- a/FinModelUtility/Fin/Fin/src/image/formats/Rgb24Image.cs +++ b/FinModelUtility/Fin/Fin/src/image/formats/Rgb24Image.cs @@ -38,7 +38,4 @@ void GetHandler( } public override bool HasAlphaChannel => false; - - public void GetRgb24Bytes(Span dst) - => this.Impl.CopyPixelDataTo(dst); } \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin/src/image/formats/Rgba32Image.cs b/FinModelUtility/Fin/Fin/src/image/formats/Rgba32Image.cs index d60fe3ea9..183f07f8d 100644 --- a/FinModelUtility/Fin/Fin/src/image/formats/Rgba32Image.cs +++ b/FinModelUtility/Fin/Fin/src/image/formats/Rgba32Image.cs @@ -44,7 +44,4 @@ void InternalGetHandler( }); public override bool HasAlphaChannel => true; - - public void GetRgba32Bytes(Span bytes) - => this.Impl.CopyPixelDataTo(bytes); } \ No newline at end of file