Skip to content

Commit

Permalink
Passed data directly from ImageSharp images into OpenGL textures rath…
Browse files Browse the repository at this point in the history
…er than needlessly copying it to a new span.
  • Loading branch information
MeltyPlayer committed Nov 29, 2024
1 parent 58ce7e9 commit 4bf73c7
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 39 deletions.
76 changes: 49 additions & 27 deletions FinModelUtility/Fin/Fin.Ui/src/rendering/gl/GlTexture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private GlTexture(IReadOnlyTexture texture) {
GL.BindTexture(target, this.id_);
{
var mipmapImages = texture.MipmapImages;

this.LoadMipmapImagesIntoTexture_(mipmapImages);

if (mipmapImages.Length == 1 &&
Expand Down Expand Up @@ -138,56 +138,66 @@ or FinTextureMinFilter.LINEAR_MIPMAP_NEAR
GL.BindTexture(target, UNDEFINED_ID);
}

private static readonly ArrayPool<byte> pool_ = ArrayPool<byte>.Shared;
private static readonly MemoryPool<byte> pool_ = MemoryPool<byte>.Shared;

private void LoadMipmapImagesIntoTexture_(IReadOnlyImage[] mipmapImages) {
for (var i = 0; i < mipmapImages.Length; ++i) {
this.LoadImageIntoTexture_(mipmapImages[i], i);
}
}

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<byte, Rgba32>());
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<byte, Rgb24>());
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<byte, La16>());
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<byte, L8>());
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,
Expand All @@ -197,18 +207,32 @@ 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;
pixelBytes[outI + 3] = a;
}
}
});
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);
Expand All @@ -220,9 +244,7 @@ private void LoadImageIntoTexture_(IReadOnlyImage image, int level) {
0,
pixelFormat,
PixelType.UnsignedByte,
pixelBytes);

pool_.Return(pixelBytes);
(IntPtr) scan0);
}

~GlTexture() => this.ReleaseUnmanagedResources_();
Expand Down
3 changes: 0 additions & 3 deletions FinModelUtility/Fin/Fin/src/image/formats/L8Image.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,4 @@ void InternalGetHandler(
});

public override bool HasAlphaChannel => false;

public void GetI8Bytes(Span<L8> dst)
=> this.Impl.CopyPixelDataTo(dst);
}
3 changes: 0 additions & 3 deletions FinModelUtility/Fin/Fin/src/image/formats/La16Image.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,4 @@ void GetHandler(
}

public override bool HasAlphaChannel => true;

public void GetIa16Bytes(Span<La16> dst)
=> this.Impl.CopyPixelDataTo(dst);
}
3 changes: 0 additions & 3 deletions FinModelUtility/Fin/Fin/src/image/formats/Rgb24Image.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,4 @@ void GetHandler(
}

public override bool HasAlphaChannel => false;

public void GetRgb24Bytes(Span<Rgb24> dst)
=> this.Impl.CopyPixelDataTo(dst);
}
3 changes: 0 additions & 3 deletions FinModelUtility/Fin/Fin/src/image/formats/Rgba32Image.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,4 @@ void InternalGetHandler(
});

public override bool HasAlphaChannel => true;

public void GetRgba32Bytes(Span<Rgba32> bytes)
=> this.Impl.CopyPixelDataTo(bytes);
}

0 comments on commit 4bf73c7

Please sign in to comment.