Use spans to improve performance in StreamExts.
Also avoid ReadBytes calls that allocate a buffer by either updating the stream position (if not interested in the bytes), by reusing an input buffer (if interested in the bytes), or using a stackalloc buffer to avoid the allocation (for small reads).
This commit is contained in:
@@ -76,9 +76,7 @@ namespace OpenRA.Mods.Common.AudioLoaders
|
||||
{
|
||||
readonly OggFormat format;
|
||||
|
||||
// This buffer can be static because it can only be used by 1 instance per thread.
|
||||
[ThreadStatic]
|
||||
static float[] conversionBuffer;
|
||||
float[] conversionBuffer;
|
||||
|
||||
public OggStream(OggFormat format)
|
||||
{
|
||||
@@ -105,13 +103,10 @@ namespace OpenRA.Mods.Common.AudioLoaders
|
||||
// Make sure we don't have an odd count.
|
||||
count -= count % format.reader.Channels;
|
||||
|
||||
// Get the buffer, creating a new one if none exists or the existing one is too small.
|
||||
var floatBuffer = conversionBuffer ??= new float[count];
|
||||
if (floatBuffer.Length < count)
|
||||
floatBuffer = conversionBuffer = new float[count];
|
||||
var floatBuffer = EnsureArraySize(ref conversionBuffer, count);
|
||||
|
||||
// Let NVorbis do the actual reading.
|
||||
var samples = format.reader.ReadSamples(floatBuffer, offset, count);
|
||||
var samples = format.reader.ReadSamples(floatBuffer);
|
||||
|
||||
// Move the data back to the request buffer and convert to 16-bit signed samples for OpenAL.
|
||||
for (var i = 0; i < samples; i++)
|
||||
@@ -137,6 +132,13 @@ namespace OpenRA.Mods.Common.AudioLoaders
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
static Span<float> EnsureArraySize(ref float[] array, int desiredSize)
|
||||
{
|
||||
if (array == null || array.Length < desiredSize)
|
||||
array = new float[desiredSize];
|
||||
return array.AsSpan(..desiredSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace OpenRA.Mods.Common.FileFormats
|
||||
{
|
||||
@@ -56,15 +56,14 @@ namespace OpenRA.Mods.Common.FileFormats
|
||||
return (short)current;
|
||||
}
|
||||
|
||||
public static byte[] LoadImaAdpcmSound(byte[] raw, ref int index)
|
||||
public static byte[] LoadImaAdpcmSound(ReadOnlySpan<byte> raw, ref int index)
|
||||
{
|
||||
var currentSample = 0;
|
||||
return LoadImaAdpcmSound(raw, ref index, ref currentSample);
|
||||
}
|
||||
|
||||
public static byte[] LoadImaAdpcmSound(byte[] raw, ref int index, ref int currentSample)
|
||||
public static byte[] LoadImaAdpcmSound(ReadOnlySpan<byte> raw, ref int index, ref int currentSample)
|
||||
{
|
||||
var s = new MemoryStream(raw);
|
||||
var dataSize = raw.Length;
|
||||
var outputSize = raw.Length * 4;
|
||||
|
||||
@@ -73,7 +72,7 @@ namespace OpenRA.Mods.Common.FileFormats
|
||||
|
||||
while (dataSize-- > 0)
|
||||
{
|
||||
var b = s.ReadUInt8();
|
||||
var b = raw[offset / 4];
|
||||
|
||||
var t = DecodeImaAdpcmSample(b, ref index, ref currentSample);
|
||||
output[offset++] = (byte)t;
|
||||
|
||||
@@ -67,12 +67,12 @@ namespace OpenRA.Mods.Common.FileFormats
|
||||
blockAlign = s.ReadInt16();
|
||||
sampleBits = s.ReadInt16();
|
||||
lengthInSeconds = (float)(s.Length * 8) / (channels * sampleRate * sampleBits);
|
||||
s.ReadBytes(fmtChunkSize - 16);
|
||||
s.Position += fmtChunkSize - 16;
|
||||
break;
|
||||
case "fact":
|
||||
var chunkSize = s.ReadInt32();
|
||||
uncompressedSize = s.ReadInt32();
|
||||
s.ReadBytes(chunkSize - 4);
|
||||
s.Position += chunkSize - 4;
|
||||
break;
|
||||
case "data":
|
||||
dataSize = s.ReadInt32();
|
||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.FileFormats
|
||||
case "LIST":
|
||||
case "cue ":
|
||||
var listCueChunkSize = s.ReadInt32();
|
||||
s.ReadBytes(listCueChunkSize);
|
||||
s.Position += listCueChunkSize;
|
||||
break;
|
||||
default:
|
||||
s.Position = s.Length; // Skip to end of stream
|
||||
@@ -156,12 +156,13 @@ namespace OpenRA.Mods.Common.FileFormats
|
||||
|
||||
// Decode and output remaining data in this block
|
||||
var blockOffset = 0;
|
||||
Span<byte> chunk = stackalloc byte[4];
|
||||
while (blockOffset < blockDataSize)
|
||||
{
|
||||
for (var c = 0; c < channels; c++)
|
||||
{
|
||||
// Decode 4 bytes (to 16 bytes of output) per channel
|
||||
var chunk = baseStream.ReadBytes(4);
|
||||
baseStream.ReadBytes(chunk);
|
||||
var decoded = ImaAdpcmReader.LoadImaAdpcmSound(chunk, ref index[c], ref predictor[c]);
|
||||
|
||||
// Interleave output, one sample per channel
|
||||
|
||||
@@ -20,11 +20,11 @@ namespace OpenRA.Mods.Common.FileFormats
|
||||
static readonly int[] AudWsStepTable2 = { -2, -1, 0, 1 };
|
||||
static readonly int[] AudWsStepTable4 = { -9, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8 };
|
||||
|
||||
public static void DecodeWestwoodCompressedSample(byte[] input, byte[] output)
|
||||
public static void DecodeWestwoodCompressedSample(ReadOnlySpan<byte> input, Span<byte> output)
|
||||
{
|
||||
if (input.Length == output.Length)
|
||||
{
|
||||
Array.Copy(input, output, output.Length);
|
||||
input.CopyTo(output);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace OpenRA.Mods.Common.FileSystem
|
||||
var dirName = s.ReadASCII(nameLength);
|
||||
|
||||
// Skip to the end of the chunk
|
||||
s.ReadBytes(chunkSize - nameLength - 6);
|
||||
s.Position += chunkSize - nameLength - 6;
|
||||
directories.Add(dirName, fileCount);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user