assets stream data directly into unmanaged memory

pull/48/head
cosmonaut 2023-04-05 00:47:02 -07:00
parent 3bd435746b
commit 1cf04a7279
6 changed files with 210 additions and 228 deletions

View File

@ -53,11 +53,8 @@ namespace MoonWorks.Audio
} }
// mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385 // mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385
public static StaticSound LoadWav(AudioDevice device, string filePath) public static unsafe StaticSound LoadWav(AudioDevice device, string filePath)
{ {
// Sample data
byte[] data;
// WaveFormatEx data // WaveFormatEx data
ushort wFormatTag; ushort wFormatTag;
ushort nChannels; ushort nChannels;
@ -68,141 +65,144 @@ namespace MoonWorks.Audio
int samplerLoopStart = 0; int samplerLoopStart = 0;
int samplerLoopEnd = 0; int samplerLoopEnd = 0;
using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath))) using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var reader = new BinaryReader(stream);
// RIFF Signature
string signature = new string(reader.ReadChars(4));
if (signature != "RIFF")
{ {
// RIFF Signature throw new NotSupportedException("Specified stream is not a wave file.");
string signature = new string(reader.ReadChars(4));
if (signature != "RIFF")
{
throw new NotSupportedException("Specified stream is not a wave file.");
}
reader.ReadUInt32(); // Riff Chunk Size
string wformat = new string(reader.ReadChars(4));
if (wformat != "WAVE")
{
throw new NotSupportedException("Specified stream is not a wave file.");
}
// WAVE Header
string format_signature = new string(reader.ReadChars(4));
while (format_signature != "fmt ")
{
reader.ReadBytes(reader.ReadInt32());
format_signature = new string(reader.ReadChars(4));
}
int format_chunk_size = reader.ReadInt32();
wFormatTag = reader.ReadUInt16();
nChannels = reader.ReadUInt16();
nSamplesPerSec = reader.ReadUInt32();
nAvgBytesPerSec = reader.ReadUInt32();
nBlockAlign = reader.ReadUInt16();
wBitsPerSample = reader.ReadUInt16();
// Reads residual bytes
if (format_chunk_size > 16)
{
reader.ReadBytes(format_chunk_size - 16);
}
// data Signature
string data_signature = new string(reader.ReadChars(4));
while (data_signature.ToLowerInvariant() != "data")
{
reader.ReadBytes(reader.ReadInt32());
data_signature = new string(reader.ReadChars(4));
}
if (data_signature != "data")
{
throw new NotSupportedException("Specified wave file is not supported.");
}
int waveDataLength = reader.ReadInt32();
data = reader.ReadBytes(waveDataLength);
// Scan for other chunks
while (reader.PeekChar() != -1)
{
char[] chunkIDChars = reader.ReadChars(4);
if (chunkIDChars.Length < 4)
{
break; // EOL!
}
byte[] chunkSizeBytes = reader.ReadBytes(4);
if (chunkSizeBytes.Length < 4)
{
break; // EOL!
}
string chunk_signature = new string(chunkIDChars);
int chunkDataSize = BitConverter.ToInt32(chunkSizeBytes, 0);
if (chunk_signature == "smpl") // "smpl", Sampler Chunk Found
{
reader.ReadUInt32(); // Manufacturer
reader.ReadUInt32(); // Product
reader.ReadUInt32(); // Sample Period
reader.ReadUInt32(); // MIDI Unity Note
reader.ReadUInt32(); // MIDI Pitch Fraction
reader.ReadUInt32(); // SMPTE Format
reader.ReadUInt32(); // SMPTE Offset
uint numSampleLoops = reader.ReadUInt32();
int samplerData = reader.ReadInt32();
for (int i = 0; i < numSampleLoops; i += 1)
{
reader.ReadUInt32(); // Cue Point ID
reader.ReadUInt32(); // Type
int start = reader.ReadInt32();
int end = reader.ReadInt32();
reader.ReadUInt32(); // Fraction
reader.ReadUInt32(); // Play Count
if (i == 0) // Grab loopStart and loopEnd from first sample loop
{
samplerLoopStart = start;
samplerLoopEnd = end;
}
}
if (samplerData != 0) // Read Sampler Data if it exists
{
reader.ReadBytes(samplerData);
}
}
else // Read unwanted chunk data and try again
{
reader.ReadBytes(chunkDataSize);
}
}
// End scan
} }
return new StaticSound( reader.ReadUInt32(); // Riff Chunk Size
string wformat = new string(reader.ReadChars(4));
if (wformat != "WAVE")
{
throw new NotSupportedException("Specified stream is not a wave file.");
}
// WAVE Header
string format_signature = new string(reader.ReadChars(4));
while (format_signature != "fmt ")
{
reader.ReadBytes(reader.ReadInt32());
format_signature = new string(reader.ReadChars(4));
}
int format_chunk_size = reader.ReadInt32();
wFormatTag = reader.ReadUInt16();
nChannels = reader.ReadUInt16();
nSamplesPerSec = reader.ReadUInt32();
nAvgBytesPerSec = reader.ReadUInt32();
nBlockAlign = reader.ReadUInt16();
wBitsPerSample = reader.ReadUInt16();
// Reads residual bytes
if (format_chunk_size > 16)
{
reader.ReadBytes(format_chunk_size - 16);
}
// data Signature
string data_signature = new string(reader.ReadChars(4));
while (data_signature.ToLowerInvariant() != "data")
{
reader.ReadBytes(reader.ReadInt32());
data_signature = new string(reader.ReadChars(4));
}
if (data_signature != "data")
{
throw new NotSupportedException("Specified wave file is not supported.");
}
int waveDataLength = reader.ReadInt32();
var waveDataBuffer = NativeMemory.Alloc((nuint) waveDataLength);
var waveDataSpan = new Span<byte>(waveDataBuffer, waveDataLength);
stream.ReadExactly(waveDataSpan);
// Scan for other chunks
while (reader.PeekChar() != -1)
{
char[] chunkIDChars = reader.ReadChars(4);
if (chunkIDChars.Length < 4)
{
break; // EOL!
}
byte[] chunkSizeBytes = reader.ReadBytes(4);
if (chunkSizeBytes.Length < 4)
{
break; // EOL!
}
string chunk_signature = new string(chunkIDChars);
int chunkDataSize = BitConverter.ToInt32(chunkSizeBytes, 0);
if (chunk_signature == "smpl") // "smpl", Sampler Chunk Found
{
reader.ReadUInt32(); // Manufacturer
reader.ReadUInt32(); // Product
reader.ReadUInt32(); // Sample Period
reader.ReadUInt32(); // MIDI Unity Note
reader.ReadUInt32(); // MIDI Pitch Fraction
reader.ReadUInt32(); // SMPTE Format
reader.ReadUInt32(); // SMPTE Offset
uint numSampleLoops = reader.ReadUInt32();
int samplerData = reader.ReadInt32();
for (int i = 0; i < numSampleLoops; i += 1)
{
reader.ReadUInt32(); // Cue Point ID
reader.ReadUInt32(); // Type
int start = reader.ReadInt32();
int end = reader.ReadInt32();
reader.ReadUInt32(); // Fraction
reader.ReadUInt32(); // Play Count
if (i == 0) // Grab loopStart and loopEnd from first sample loop
{
samplerLoopStart = start;
samplerLoopEnd = end;
}
}
if (samplerData != 0) // Read Sampler Data if it exists
{
reader.ReadBytes(samplerData);
}
}
else // Read unwanted chunk data and try again
{
reader.ReadBytes(chunkDataSize);
}
}
// End scan
var sound = new StaticSound(
device, device,
wFormatTag, wFormatTag,
wBitsPerSample, wBitsPerSample,
nBlockAlign, nBlockAlign,
nChannels, nChannels,
nSamplesPerSec, nSamplesPerSec,
data, (nint) waveDataBuffer,
0, (uint) waveDataLength,
(uint) data.Length true
); );
return sound;
} }
public unsafe StaticSound( public StaticSound(
AudioDevice device, AudioDevice device,
ushort formatTag, ushort formatTag,
ushort bitsPerSample, ushort bitsPerSample,
ushort blockAlign, ushort blockAlign,
ushort channels, ushort channels,
uint samplesPerSecond, uint samplesPerSecond,
byte[] buffer, IntPtr bufferPtr,
uint bufferOffset, /* number of bytes */ uint bufferLengthInBytes,
uint bufferLength /* number of bytes */ bool ownsBuffer) : base(device)
) : base(device)
{ {
FormatTag = formatTag; FormatTag = formatTag;
BitsPerSample = bitsPerSample; BitsPerSample = bitsPerSample;
@ -210,19 +210,17 @@ namespace MoonWorks.Audio
Channels = channels; Channels = channels;
SamplesPerSecond = samplesPerSecond; SamplesPerSecond = samplesPerSecond;
Handle = new FAudio.FAudioBuffer(); Handle = new FAudio.FAudioBuffer
Handle.Flags = FAudio.FAUDIO_END_OF_STREAM; {
Handle.pContext = IntPtr.Zero; Flags = FAudio.FAUDIO_END_OF_STREAM,
Handle.AudioBytes = bufferLength; pContext = IntPtr.Zero,
Handle.pAudioData = (nint) NativeMemory.Alloc(bufferLength); pAudioData = bufferPtr,
Marshal.Copy(buffer, (int) bufferOffset, Handle.pAudioData, (int) bufferLength); AudioBytes = bufferLengthInBytes,
Handle.PlayBegin = 0; PlayBegin = 0,
Handle.PlayLength = 0; PlayLength = 0
};
LoopStart = 0; OwnsBuffer = ownsBuffer;
LoopLength = 0;
OwnsBuffer = true;
} }
public unsafe StaticSound( public unsafe StaticSound(
@ -256,35 +254,6 @@ namespace MoonWorks.Audio
OwnsBuffer = true; OwnsBuffer = true;
} }
public StaticSound(
AudioDevice device,
ushort formatTag,
ushort bitsPerSample,
ushort blockAlign,
ushort channels,
uint samplesPerSecond,
IntPtr bufferPtr,
uint bufferLengthInBytes) : base(device)
{
FormatTag = formatTag;
BitsPerSample = bitsPerSample;
BlockAlign = blockAlign;
Channels = channels;
SamplesPerSecond = samplesPerSecond;
Handle = new FAudio.FAudioBuffer
{
Flags = FAudio.FAUDIO_END_OF_STREAM,
pContext = IntPtr.Zero,
pAudioData = bufferPtr,
AudioBytes = bufferLengthInBytes,
PlayBegin = 0,
PlayLength = 0
};
OwnsBuffer = false;
}
/// <summary> /// <summary>
/// Gets a sound instance from the pool. /// Gets a sound instance from the pool.
/// NOTE: If you lose track of instances, you will create garbage collection pressure! /// NOTE: If you lose track of instances, you will create garbage collection pressure!

View File

@ -15,10 +15,13 @@ namespace MoonWorks.Audio
public unsafe static StreamingSoundOgg Load(AudioDevice device, string filePath) public unsafe static StreamingSoundOgg Load(AudioDevice device, string filePath)
{ {
var fileData = File.ReadAllBytes(filePath); var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
var fileDataPtr = NativeMemory.Alloc((nuint) fileData.Length); var fileDataPtr = NativeMemory.Alloc((nuint) fileStream.Length);
Marshal.Copy(fileData, 0, (IntPtr) fileDataPtr, fileData.Length); var fileDataSpan = new Span<byte>(fileDataPtr, (int) fileStream.Length);
var vorbisHandle = FAudio.stb_vorbis_open_memory((IntPtr) fileDataPtr, fileData.Length, out int error, IntPtr.Zero); fileStream.ReadExactly(fileDataSpan);
fileStream.Close();
var vorbisHandle = FAudio.stb_vorbis_open_memory((IntPtr) fileDataPtr, fileDataSpan.Length, out int error, IntPtr.Zero);
if (error != 0) if (error != 0)
{ {
NativeMemory.Free(fileDataPtr); NativeMemory.Free(fileDataPtr);

View File

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using WellspringCS; using WellspringCS;
namespace MoonWorks.Graphics.Font namespace MoonWorks.Graphics.Font
@ -12,11 +13,15 @@ namespace MoonWorks.Graphics.Font
public unsafe Font(string path) public unsafe Font(string path)
{ {
var bytes = File.ReadAllBytes(path); var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
fixed (byte* pByte = &bytes[0]) var fileByteBuffer = NativeMemory.Alloc((nuint) fileStream.Length);
{ var fileByteSpan = new Span<byte>(fileByteBuffer, (int) fileStream.Length);
Handle = Wellspring.Wellspring_CreateFont((IntPtr) pByte, (uint) bytes.Length); fileStream.ReadExactly(fileByteSpan);
} fileStream.Close();
Handle = Wellspring.Wellspring_CreateFont((IntPtr) fileByteBuffer, (uint) fileByteSpan.Length);
NativeMemory.Free(fileByteBuffer);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)

View File

@ -1,6 +1,7 @@
using RefreshCS; using RefreshCS;
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics
{ {
@ -13,10 +14,8 @@ namespace MoonWorks.Graphics
public unsafe ShaderModule(GraphicsDevice device, string filePath) : base(device) public unsafe ShaderModule(GraphicsDevice device, string filePath) : base(device)
{ {
using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
{ Handle = CreateFromStream(device, stream);
Handle = CreateFromStream(device, stream);
}
} }
public unsafe ShaderModule(GraphicsDevice device, Stream stream) : base(device) public unsafe ShaderModule(GraphicsDevice device, Stream stream) : base(device)
@ -24,19 +23,20 @@ namespace MoonWorks.Graphics
Handle = CreateFromStream(device, stream); Handle = CreateFromStream(device, stream);
} }
private unsafe static IntPtr CreateFromStream(GraphicsDevice device, Stream stream) private static unsafe IntPtr CreateFromStream(GraphicsDevice device, Stream stream)
{ {
var bytecode = new byte[stream.Length]; var bytecodeBuffer = NativeMemory.Alloc((nuint) stream.Length);
stream.Read(bytecode, 0, (int) stream.Length); var bytecodeSpan = new Span<byte>(bytecodeBuffer, (int) stream.Length);
stream.ReadExactly(bytecodeSpan);
fixed (byte* ptr = bytecode) Refresh.ShaderModuleCreateInfo shaderModuleCreateInfo;
{ shaderModuleCreateInfo.codeSize = (nuint) stream.Length;
Refresh.ShaderModuleCreateInfo shaderModuleCreateInfo; shaderModuleCreateInfo.byteCode = (nint) bytecodeBuffer;
shaderModuleCreateInfo.codeSize = (UIntPtr) bytecode.Length;
shaderModuleCreateInfo.byteCode = (IntPtr) ptr;
return Refresh.Refresh_CreateShaderModule(device.Handle, shaderModuleCreateInfo); var shaderModule = Refresh.Refresh_CreateShaderModule(device.Handle, shaderModuleCreateInfo);
}
NativeMemory.Free(bytecodeBuffer);
return shaderModule;
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using RefreshCS; using RefreshCS;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics
@ -217,47 +218,44 @@ namespace MoonWorks.Graphics
return texture; return texture;
} }
public static Texture LoadDDS(GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, System.IO.Stream stream) public unsafe static Texture LoadDDS(GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, System.IO.Stream stream)
{ {
using (var reader = new BinaryReader(stream)) using var reader = new BinaryReader(stream);
Texture texture;
int faces;
ParseDDS(reader, out var format, out var width, out var height, out var levels, out var isCube);
if (isCube)
{ {
Texture texture; texture = CreateTextureCube(graphicsDevice, (uint) width, format, TextureUsageFlags.Sampler, (uint) levels);
int faces; faces = 6;
ParseDDS(reader, out var format, out var width, out var height, out var levels, out var isCube);
if (isCube)
{
texture = CreateTextureCube(graphicsDevice, (uint) width, format, TextureUsageFlags.Sampler, (uint) levels);
faces = 6;
}
else
{
texture = CreateTexture2D(graphicsDevice, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, (uint) levels);
faces = 1;
}
for (int i = 0; i < faces; i += 1)
{
for (int j = 0; j < levels; j += 1)
{
var levelWidth = width >> j;
var levelHeight = height >> j;
var pixels = reader.ReadBytes(
Texture.CalculateDDSLevelSize(
levelWidth,
levelHeight,
format
)
);
var textureSlice = new TextureSlice(texture, new Rect(0, 0, levelWidth, levelHeight), 0, (uint) i, (uint) j);
commandBuffer.SetTextureData(textureSlice, pixels);
}
}
return texture;
} }
else
{
texture = CreateTexture2D(graphicsDevice, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, (uint) levels);
faces = 1;
}
for (int i = 0; i < faces; i += 1)
{
for (int j = 0; j < levels; j += 1)
{
var levelWidth = width >> j;
var levelHeight = height >> j;
var levelSize = CalculateDDSLevelSize(levelWidth, levelHeight, format);
var byteBuffer = NativeMemory.Alloc((nuint) levelSize);
var byteSpan = new Span<byte>(byteBuffer, levelSize);
stream.ReadExactly(byteSpan);
var textureSlice = new TextureSlice(texture, new Rect(0, 0, levelWidth, levelHeight), 0, (uint) i, (uint) j);
commandBuffer.SetTextureData(textureSlice, (nint) byteBuffer, (uint) levelSize);
NativeMemory.Free(byteBuffer);
}
}
return texture;
} }
/// <summary> /// <summary>

View File

@ -1,6 +1,8 @@
/* Heavily based on https://github.com/FNA-XNA/FNA/blob/master/src/Media/Xiph/VideoPlayer.cs */ /* Heavily based on https://github.com/FNA-XNA/FNA/blob/master/src/Media/Xiph/VideoPlayer.cs */
using System; using System;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SDL2;
namespace MoonWorks.Video namespace MoonWorks.Video
{ {
@ -16,6 +18,7 @@ namespace MoonWorks.Video
internal IntPtr Handle; internal IntPtr Handle;
private IntPtr rwData; private IntPtr rwData;
private void* videoData; private void* videoData;
private int videoDataLength;
public double FramesPerSecond => fps; public double FramesPerSecond => fps;
public int Width => yWidth; public int Width => yWidth;
@ -31,16 +34,19 @@ namespace MoonWorks.Video
public Video(string filename) public Video(string filename)
{ {
if (!System.IO.File.Exists(filename)) if (!File.Exists(filename))
{ {
throw new ArgumentException("Video file not found!"); throw new ArgumentException("Video file not found!");
} }
var bytes = System.IO.File.ReadAllBytes(filename); var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
videoData = NativeMemory.Alloc((nuint) bytes.Length); videoDataLength = (int) fileStream.Length;
Marshal.Copy(bytes, 0, (IntPtr) videoData, bytes.Length); videoData = NativeMemory.Alloc((nuint) videoDataLength);
rwData = SDL2.SDL.SDL_RWFromMem((IntPtr) videoData, bytes.Length); var fileBufferSpan = new Span<byte>(videoData, videoDataLength);
fileStream.ReadExactly(fileBufferSpan);
fileStream.Close();
rwData = SDL.SDL_RWFromMem((IntPtr) videoData, videoDataLength);
if (Theorafile.tf_open_callbacks(rwData, out Handle, callbacks) < 0) if (Theorafile.tf_open_callbacks(rwData, out Handle, callbacks) < 0)
{ {
throw new ArgumentException("Invalid video file!"); throw new ArgumentException("Invalid video file!");
@ -98,6 +104,7 @@ namespace MoonWorks.Video
// free unmanaged resources (unmanaged objects) // free unmanaged resources (unmanaged objects)
Theorafile.tf_close(ref Handle); Theorafile.tf_close(ref Handle);
SDL.SDL_RWclose(rwData);
NativeMemory.Free(videoData); NativeMemory.Free(videoData);
IsDisposed = true; IsDisposed = true;