Files
2025-11-30 08:35:03 +02:00

294 lines
8.1 KiB
C#

using System;
using System.IO;
using UnityEngine;
namespace UnityGLTF
{
// It seems that BinaryWriter creates a ton of garbage: the implementation allocates a byte[4] for every float write. (and for double as well)
// foreach (var v in arr)
// {
// _bufferWriter.Write(v); // this will allocate 36B per call
// }
// For large accessors, this adds up to hundreds of megabytes of garbage.
// This class adds special implementations that skip BitConverterLE and instead convert with the same logic,
// differentiating between LittleEndian and BigEndian.
// Following https://github.com/mono/mono/blob/4a5ffcabd58d6439e60126f46e0063bcf30e7a47/mcs/class/referencesource/mscorlib/system/io/binarywriter.cs#L381
// Here's the 4-byte allocation we're seeing: https://github.com/mono/mono/blob/4a5ffcabd58d6439e60126f46e0063bcf30e7a47/mcs/class/System.ServiceModel/Mono.Security.Protocol.Ntlm/BitConverterLE.cs#L127
// Another discussion of this problem: https://forum.unity.com/threads/binarywriter-floats.1108478/
// The implementations here pull BitConverter.IsLittleEndian out of the loop and directly write converted bytes into the BinaryWriter.
internal class BinaryWriterWithLessAllocations : BinaryWriter
{
private static readonly byte[] _buffer = new byte[16]; // temp space for writing primitives to.
public BinaryWriterWithLessAllocations(Stream binStream) : base(binStream) { }
public unsafe void Write(float[] value)
{
if (BitConverter.IsLittleEndian)
{
foreach (var v in value)
{
uint tmpValue = *(uint *)&v;
_buffer[0] = (byte) (tmpValue);
_buffer[1] = (byte) (tmpValue >> 8);
_buffer[2] = (byte) (tmpValue >> 16);
_buffer[3] = (byte) (tmpValue >> 24);
Write(_buffer, 0, 4);
}
}
else
{
foreach (var v in value)
{
uint tmpValue = *(uint *)&v;
_buffer[0] = (byte) (tmpValue >> 24);
_buffer[1] = (byte) (tmpValue >> 16);
_buffer[2] = (byte) (tmpValue >> 8);
_buffer[3] = (byte) (tmpValue);
Write(_buffer, 0, 4);
}
}
}
public override unsafe void Write(float value)
{
if (BitConverter.IsLittleEndian)
{
uint tmpValue = *(uint *)&value;
_buffer[0] = (byte) (tmpValue);
_buffer[1] = (byte) (tmpValue >> 8);
_buffer[2] = (byte) (tmpValue >> 16);
_buffer[3] = (byte) (tmpValue >> 24);
Write(_buffer, 0, 4);
}
else
{
uint tmpValue = *(uint *)&value;
_buffer[0] = (byte) (tmpValue >> 24);
_buffer[1] = (byte) (tmpValue >> 16);
_buffer[2] = (byte) (tmpValue >> 8);
_buffer[3] = (byte) (tmpValue);
Write(_buffer, 0, 4);
}
}
public unsafe void Write(Vector4[] arr)
{
if (BitConverter.IsLittleEndian)
{
foreach (var v0 in arr)
{
float vx = v0.x;
float vy = v0.y;
float vz = v0.z;
float vw = v0.w;
uint tmpValue = *(uint *)&vx;
_buffer[ 0] = (byte) (tmpValue);
_buffer[ 1] = (byte) (tmpValue >> 8);
_buffer[ 2] = (byte) (tmpValue >> 16);
_buffer[ 3] = (byte) (tmpValue >> 24);
tmpValue = *(uint *)&vy;
_buffer[ 4] = (byte) (tmpValue);
_buffer[ 5] = (byte) (tmpValue >> 8);
_buffer[ 6] = (byte) (tmpValue >> 16);
_buffer[ 7] = (byte) (tmpValue >> 24);
tmpValue = *(uint *)&vz;
_buffer[ 8] = (byte) (tmpValue);
_buffer[ 9] = (byte) (tmpValue >> 8);
_buffer[10] = (byte) (tmpValue >> 16);
_buffer[11] = (byte) (tmpValue >> 24);
tmpValue = *(uint *)&vw;
_buffer[12] = (byte) (tmpValue);
_buffer[13] = (byte) (tmpValue >> 8);
_buffer[14] = (byte) (tmpValue >> 16);
_buffer[15] = (byte) (tmpValue >> 24);
Write(_buffer, 0, 16);
}
}
else
{
foreach (var v0 in arr)
{
float vx = v0.x;
float vy = v0.y;
float vz = v0.z;
float vw = v0.w;
uint tmpValue = *(uint *)&vx;
_buffer[ 0] = (byte) (tmpValue >> 24);
_buffer[ 1] = (byte) (tmpValue >> 16);
_buffer[ 2] = (byte) (tmpValue >> 8);
_buffer[ 3] = (byte) (tmpValue);
tmpValue = *(uint *)&vy;
_buffer[ 4] = (byte) (tmpValue >> 24);
_buffer[ 5] = (byte) (tmpValue >> 16);
_buffer[ 6] = (byte) (tmpValue >> 8);
_buffer[ 7] = (byte) (tmpValue);
tmpValue = *(uint *)&vz;
_buffer[ 8] = (byte) (tmpValue >> 24);
_buffer[ 9] = (byte) (tmpValue >> 16);
_buffer[10] = (byte) (tmpValue >> 8);
_buffer[11] = (byte) (tmpValue);
tmpValue = *(uint *)&vw;
_buffer[12] = (byte) (tmpValue >> 24);
_buffer[13] = (byte) (tmpValue >> 16);
_buffer[14] = (byte) (tmpValue >> 8);
_buffer[15] = (byte) (tmpValue);
Write(_buffer, 0, 16);
}
}
}
public unsafe void Write(Vector3[] arr)
{
if (BitConverter.IsLittleEndian)
{
foreach (var v0 in arr)
{
float vx = v0.x;
float vy = v0.y;
float vz = v0.z;
uint tmpValue = *(uint *)&vx;
_buffer[ 0] = (byte) (tmpValue);
_buffer[ 1] = (byte) (tmpValue >> 8);
_buffer[ 2] = (byte) (tmpValue >> 16);
_buffer[ 3] = (byte) (tmpValue >> 24);
tmpValue = *(uint *)&vy;
_buffer[ 4] = (byte) (tmpValue);
_buffer[ 5] = (byte) (tmpValue >> 8);
_buffer[ 6] = (byte) (tmpValue >> 16);
_buffer[ 7] = (byte) (tmpValue >> 24);
tmpValue = *(uint *)&vz;
_buffer[ 8] = (byte) (tmpValue);
_buffer[ 9] = (byte) (tmpValue >> 8);
_buffer[10] = (byte) (tmpValue >> 16);
_buffer[11] = (byte) (tmpValue >> 24);
Write(_buffer, 0, 12);
}
}
else
{
foreach (var v0 in arr)
{
float vx = v0.x;
float vy = v0.y;
float vz = v0.z;
uint tmpValue = *(uint *)&vx;
_buffer[ 0] = (byte) (tmpValue >> 24);
_buffer[ 1] = (byte) (tmpValue >> 16);
_buffer[ 2] = (byte) (tmpValue >> 8);
_buffer[ 3] = (byte) (tmpValue);
tmpValue = *(uint *)&vy;
_buffer[ 4] = (byte) (tmpValue >> 24);
_buffer[ 5] = (byte) (tmpValue >> 16);
_buffer[ 6] = (byte) (tmpValue >> 8);
_buffer[ 7] = (byte) (tmpValue);
tmpValue = *(uint *)&vz;
_buffer[ 8] = (byte) (tmpValue >> 24);
_buffer[ 9] = (byte) (tmpValue >> 16);
_buffer[10] = (byte) (tmpValue >> 8);
_buffer[11] = (byte) (tmpValue);
Write(_buffer, 0, 12);
}
}
}
public unsafe void Write(Quaternion[] arr)
{
if (BitConverter.IsLittleEndian)
{
foreach (var v0 in arr)
{
float vx = v0.x;
float vy = v0.y;
float vz = v0.z;
float vw = v0.w;
uint tmpValue = *(uint *)&vx;
_buffer[ 0] = (byte) (tmpValue);
_buffer[ 1] = (byte) (tmpValue >> 8);
_buffer[ 2] = (byte) (tmpValue >> 16);
_buffer[ 3] = (byte) (tmpValue >> 24);
tmpValue = *(uint *)&vy;
_buffer[ 4] = (byte) (tmpValue);
_buffer[ 5] = (byte) (tmpValue >> 8);
_buffer[ 6] = (byte) (tmpValue >> 16);
_buffer[ 7] = (byte) (tmpValue >> 24);
tmpValue = *(uint *)&vz;
_buffer[ 8] = (byte) (tmpValue);
_buffer[ 9] = (byte) (tmpValue >> 8);
_buffer[10] = (byte) (tmpValue >> 16);
_buffer[11] = (byte) (tmpValue >> 24);
tmpValue = *(uint *)&vw;
_buffer[12] = (byte) (tmpValue);
_buffer[13] = (byte) (tmpValue >> 8);
_buffer[14] = (byte) (tmpValue >> 16);
_buffer[15] = (byte) (tmpValue >> 24);
Write(_buffer, 0, 16);
}
}
else
{
foreach (var v0 in arr)
{
float vx = v0.x;
float vy = v0.y;
float vz = v0.z;
float vw = v0.w;
uint tmpValue = *(uint *)&vx;
_buffer[ 0] = (byte) (tmpValue >> 24);
_buffer[ 1] = (byte) (tmpValue >> 16);
_buffer[ 2] = (byte) (tmpValue >> 8);
_buffer[ 3] = (byte) (tmpValue);
tmpValue = *(uint *)&vy;
_buffer[ 4] = (byte) (tmpValue >> 24);
_buffer[ 5] = (byte) (tmpValue >> 16);
_buffer[ 6] = (byte) (tmpValue >> 8);
_buffer[ 7] = (byte) (tmpValue);
tmpValue = *(uint *)&vz;
_buffer[ 8] = (byte) (tmpValue >> 24);
_buffer[ 9] = (byte) (tmpValue >> 16);
_buffer[10] = (byte) (tmpValue >> 8);
_buffer[11] = (byte) (tmpValue);
tmpValue = *(uint *)&vw;
_buffer[12] = (byte) (tmpValue >> 24);
_buffer[13] = (byte) (tmpValue >> 16);
_buffer[14] = (byte) (tmpValue >> 8);
_buffer[15] = (byte) (tmpValue);
Write(_buffer, 0, 16);
}
}
}
}
}