Skip to content

performace optimize: Avoid cast float to unsigned in Bgra4444.Pack - #864

Closed
7aGiven wants to merge 1 commit into
FNA-XNA:masterfrom
7aGiven:color2bgra4444
Closed

7aGiven wants to merge 1 commit into
FNA-XNA:masterfrom
7aGiven:color2bgra4444

Conversation

@7aGiven

@7aGiven 7aGiven commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor
  • I confirm that I am the author of this code and release it to the FNA project under the Ms-PL license. This contribution does not contain code from other sources, including code generated by a Large Language Model ("AI").

  • I confirm that no changes being submitted have been derived from decompiled software, be it from Microsoft XNA or other proprietary software.

JPG emample from: https://file-examples.com/index.php/sample-images-download/sample-jpg-download/

Benchmark:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.IO;

namespace Benchmark
{
    public static class MathHelper
    {
        public static float Clamp(float value, float min, float max)
        {
            if (value > max)
            {
                value = max;
            }
            else if (value < min)
            {
                value = min;
            }
            return value;
        }
    }
}

public struct Bgra4444
{
    internal static ushort Pack1(float x, float y, float z, float w)
    {
        return (ushort) (
            (((ushort) Math.Round(Benchmark.MathHelper.Clamp(x, 0, 1) * 15.0f)) << 8) |
            (((ushort) Math.Round(Benchmark.MathHelper.Clamp(y, 0, 1) * 15.0f)) << 4) |
            ((ushort) Math.Round(Benchmark.MathHelper.Clamp(z, 0, 1) * 15.0f)) |
            (((ushort) Math.Round(Benchmark.MathHelper.Clamp(w, 0, 1) * 15.0f)) << 12)
        );
    }
    internal static ushort Pack2(float x, float y, float z, float w)
    {
        return (ushort) (
            (((int) Math.Round(Benchmark.MathHelper.Clamp(x, 0, 1) * 15.0f)) << 8) |
            (((int) Math.Round(Benchmark.MathHelper.Clamp(y, 0, 1) * 15.0f)) << 4) |
            ((int) Math.Round(Benchmark.MathHelper.Clamp(z, 0, 1) * 15.0f)) |
            (((int) Math.Round(Benchmark.MathHelper.Clamp(w, 0, 1) * 15.0f)) << 12)
        );
    }
}

[DisassemblyDiagnoser]
public class MyBenchmark
{
    private static Vector4[] colors;
    private static ushort[] bgras;
    static MyBenchmark()
    {
        Game game = new Game();
        PresentationParameters pp = new PresentationParameters() { DeviceWindowHandle = game.Window.Handle, IsFullScreen = false };
        GraphicsDevice gd = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.HiDef, pp);
        Texture2D texture = Texture2D.FromStream(gd, File.OpenRead(@"C:\tmp\file_example_JPG_500kB.jpg"));
        Color[] colors = new Color[texture.Width * texture.Height];
        texture.GetData(colors);
        MyBenchmark.colors = new Vector4[colors.Length];
        bgras = new ushort[colors.Length];
        for (int i = 0; i < colors.Length; i++)
        {
            MyBenchmark.colors[i] = colors[i].ToVector4();
        }
    }

    [Benchmark]
    public void Pack1()
    {
        Vector4[] colors = MyBenchmark.colors;
        ushort[] bgras = MyBenchmark.bgras;
        for (int i = 0; i < colors.Length; i++)
        {
            bgras[i] = Bgra4444.Pack1(colors[i].X, colors[i].Y, colors[i].Z, colors[i].W);
        }
    }
    [Benchmark]
    public void Pack2()
    {
        Vector4[] colors = MyBenchmark.colors;
        ushort[] bgras = MyBenchmark.bgras;
        for (int i = 0; i < colors.Length; i++)
        {
            bgras[i] = Bgra4444.Pack2(colors[i].X, colors[i].Y, colors[i].Z, colors[i].W);
        }
    }
}

static class Program
{
    [STAThread]
    static void Main()
    {
        BenchmarkRunner.Run<MyBenchmark>();
    }
}

.NET Framework 4.8.1 (4.8.9363.0), X86 LegacyJIT (Job: DefaultJob)

; Bgra4444.Pack1(Single, Single, Single, Single)
       push      ebp
       mov       ebp,esp
       push      esi
       sub       esp,0C
       push      dword ptr [ebp+14]
       push      0
       push      3F800000
       call      dword ptr ds:[104989FC]; Benchmark.MathHelper.Clamp(Single, Single, Single)
       fmul      dword ptr ds:[28C7B48]
       fstp      qword ptr [ebp-10]
       fld       qword ptr [ebp-10]
       fistp     dword ptr [ebp-8]
       mov       esi,[ebp-8]
       and       esi,0FFFF
       shl       esi,8
       push      dword ptr [ebp+10]
       push      0
       push      3F800000
       call      dword ptr ds:[104989FC]; Benchmark.MathHelper.Clamp(Single, Single, Single)
       fmul      dword ptr ds:[28C7B50]
       fstp      qword ptr [ebp-10]
       fld       qword ptr [ebp-10]
       fistp     dword ptr [ebp-8]
       mov       eax,[ebp-8]
       and       eax,0FFFF
       shl       eax,4
       or        eax,esi
       mov       esi,eax
       push      dword ptr [ebp+0C]
       push      0
       push      3F800000
       call      dword ptr ds:[104989FC]; Benchmark.MathHelper.Clamp(Single, Single, Single)
       fmul      dword ptr ds:[28C7B58]
       fstp      qword ptr [ebp-10]
       fld       qword ptr [ebp-10]
       fistp     dword ptr [ebp-8]
       mov       eax,[ebp-8]
       and       eax,0FFFF
       or        eax,esi
       mov       esi,eax
       push      dword ptr [ebp+8]
       push      0
       push      3F800000
       call      dword ptr ds:[104989FC]; Benchmark.MathHelper.Clamp(Single, Single, Single)
       fmul      dword ptr ds:[28C7B60]
       fstp      qword ptr [ebp-10]
       fld       qword ptr [ebp-10]
       fistp     dword ptr [ebp-8]
       mov       eax,[ebp-8]
       and       eax,0FFFF
       shl       eax,0C
       or        eax,esi
       and       eax,0FFFF
       lea       esp,[ebp-4]
       pop       esi
       pop       ebp
       ret       10
; Total bytes of code 196

.NET Framework 4.8.1 (4.8.9363.0), X86 LegacyJIT (Job: DefaultJob)

; Bgra4444.Pack2(Single, Single, Single, Single)
       push      ebp
       mov       ebp,esp
       push      esi
       sub       esp,0C
       push      dword ptr [ebp+14]
       push      0
       push      3F800000
       call      dword ptr ds:[104B89FC]; Benchmark.MathHelper.Clamp(Single, Single, Single)
       fmul      dword ptr ds:[49C7B30]
       fstp      qword ptr [ebp-10]
       fld       qword ptr [ebp-10]
       fistp     dword ptr [ebp-8]
       mov       esi,[ebp-8]
       shl       esi,8
       push      dword ptr [ebp+10]
       push      0
       push      3F800000
       call      dword ptr ds:[104B89FC]; Benchmark.MathHelper.Clamp(Single, Single, Single)
       fmul      dword ptr ds:[49C7B38]
       fstp      qword ptr [ebp-10]
       fld       qword ptr [ebp-10]
       fistp     dword ptr [ebp-8]
       mov       eax,[ebp-8]
       shl       eax,4
       or        eax,esi
       mov       esi,eax
       push      dword ptr [ebp+0C]
       push      0
       push      3F800000
       call      dword ptr ds:[104B89FC]; Benchmark.MathHelper.Clamp(Single, Single, Single)
       fmul      dword ptr ds:[49C7B40]
       fstp      qword ptr [ebp-10]
       fld       qword ptr [ebp-10]
       fistp     dword ptr [ebp-8]
       mov       eax,[ebp-8]
       or        eax,esi
       mov       esi,eax
       push      dword ptr [ebp+8]
       push      0
       push      3F800000
       call      dword ptr ds:[104B89FC]; Benchmark.MathHelper.Clamp(Single, Single, Single)
       fmul      dword ptr ds:[49C7B48]
       fstp      qword ptr [ebp-10]
       fld       qword ptr [ebp-10]
       fistp     dword ptr [ebp-8]
       mov       eax,[ebp-8]
       shl       eax,0C
       or        eax,esi
       and       eax,0FFFF
       lea       esp,[ebp-4]
       pop       esi
       pop       ebp
       ret       10
; Total bytes of code 175
Method Mean Error StdDev Code Size
Pack1 76.86 ms 0.842 ms 0.787 ms 315 B
Pack2 77.26 ms 1.261 ms 1.239 ms 294 B

@7aGiven

7aGiven commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

link #862

@7aGiven
7aGiven marked this pull request as draft October 16, 2026 01:10
@7aGiven
7aGiven marked this pull request as ready for review October 16, 2026 01:22
@7aGiven

7aGiven commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Game rarely use PackedVecotr, So do benchmark to show assembly.

@7aGiven 7aGiven changed the title performace optimize: Avoid cast float to unsigned in Bgra4444.Pack logical optimize: Math.Min(uint) instead of MathHelper.Clamp(float) in Bgra4444.Pack Oct 16, 2026
@7aGiven
7aGiven marked this pull request as draft October 16, 2026 01:42
@7aGiven

7aGiven commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

The asm difference is 4 instruction of and eax,0FFFF

So performance is rarely different, merge or not merge both OK.

@7aGiven
7aGiven marked this pull request as ready for review October 16, 2026 02:15
@7aGiven
7aGiven marked this pull request as draft October 16, 2026 02:50
@7aGiven 7aGiven changed the title logical optimize: Math.Min(uint) instead of MathHelper.Clamp(float) in Bgra4444.Pack performace optimize: Avoid cast float to unsigned in Bgra4444.Pack Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants