Setup entry point + integrated moonworks stuff

This commit is contained in:
2024-07-05 14:32:58 +02:00
parent e7a4a862be
commit 8334a24fd1
116 changed files with 16988 additions and 3 deletions
.idea/.idea.Nerfed/.idea
Libraries
Nerfed.Runtime
Assert.cs
Audio
Engine.csFrameLimiterSettings.cs
Graphics
Input
Interop
Log.csNerfed.Runtime.csprojProfiler.csProgram.cs
Storage
Util
Video
Window

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@@ -0,0 +1,33 @@
using System;
using System.Collections.Concurrent;
namespace Nerfed.Runtime.Graphics;
internal class CommandBufferPool
{
private GraphicsDevice GraphicsDevice;
private ConcurrentQueue<CommandBuffer> CommandBuffers = new ConcurrentQueue<CommandBuffer>();
public CommandBufferPool(GraphicsDevice graphicsDevice)
{
GraphicsDevice = graphicsDevice;
}
public CommandBuffer Obtain()
{
if (CommandBuffers.TryDequeue(out CommandBuffer commandBuffer))
{
return commandBuffer;
}
else
{
return new CommandBuffer(GraphicsDevice);
}
}
public void Return(CommandBuffer commandBuffer)
{
commandBuffer.Handle = IntPtr.Zero;
CommandBuffers.Enqueue(commandBuffer);
}
}

@@ -0,0 +1,171 @@
using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
public class ComputePass
{
public nint Handle { get; private set; }
internal void SetHandle(nint handle)
{
Handle = handle;
}
#if DEBUG
internal bool active;
ComputePipeline currentComputePipeline;
#endif
/// <summary>
/// Binds a compute pipeline so that compute work may be dispatched.
/// </summary>
/// <param name="computePipeline">The compute pipeline to bind.</param>
public void BindComputePipeline(
ComputePipeline computePipeline
) {
#if DEBUG
AssertComputePassActive();
// TODO: validate formats?
#endif
Refresh.Refresh_BindComputePipeline(
Handle,
computePipeline.Handle
);
#if DEBUG
currentComputePipeline = computePipeline;
#endif
}
/// <summary>
/// Binds a texture to be used in the compute shader.
/// This texture must have been created with the ComputeShaderRead usage flag.
/// </summary>
public unsafe void BindStorageTexture(
in TextureSlice textureSlice,
uint slot = 0
) {
#if DEBUG
AssertComputePassActive();
AssertComputePipelineBound();
AssertTextureNonNull(textureSlice.Texture);
AssertTextureHasComputeStorageReadFlag(textureSlice.Texture);
#endif
Refresh.TextureSlice refreshTextureSlice = textureSlice.ToRefresh();
Refresh.Refresh_BindComputeStorageTextures(
Handle,
slot,
&refreshTextureSlice,
1
);
}
/// <summary>
/// Binds a buffer to be used in the compute shader.
/// This buffer must have been created with the ComputeShaderRead usage flag.
/// </summary>
public unsafe void BindStorageBuffer(
Buffer buffer,
uint slot = 0
) {
#if DEBUG
AssertComputePassActive();
AssertComputePipelineBound();
AssertBufferNonNull(buffer);
AssertBufferHasComputeStorageReadFlag(buffer);
#endif
IntPtr bufferHandle = buffer.Handle;
Refresh.Refresh_BindComputeStorageBuffers(
Handle,
slot,
&bufferHandle,
1
);
}
/// <summary>
/// Dispatches compute work.
/// </summary>
public void Dispatch(
uint groupCountX,
uint groupCountY,
uint groupCountZ
) {
#if DEBUG
AssertComputePassActive();
AssertComputePipelineBound();
if (groupCountX < 1 || groupCountY < 1 || groupCountZ < 1)
{
throw new System.ArgumentException("All dimensions for the compute work groups must be >= 1!");
}
#endif
Refresh.Refresh_DispatchCompute(
Handle,
groupCountX,
groupCountY,
groupCountZ
);
}
#if DEBUG
private void AssertComputePassActive(string message = "Render pass is not active!")
{
if (!active)
{
throw new System.InvalidOperationException(message);
}
}
private void AssertComputePipelineBound(string message = "No compute pipeline is bound!")
{
if (currentComputePipeline == null)
{
throw new System.InvalidOperationException(message);
}
}
private void AssertTextureNonNull(in TextureSlice textureSlice)
{
if (textureSlice.Texture == null || textureSlice.Texture.Handle == nint.Zero)
{
throw new System.NullReferenceException("Texture must not be null!");
}
}
private void AssertTextureHasComputeStorageReadFlag(Texture texture)
{
if ((texture.UsageFlags & TextureUsageFlags.ComputeStorageRead) == 0)
{
throw new System.ArgumentException("The bound Texture's UsageFlags must include TextureUsageFlags.ComputeStorageRead!");
}
}
private void AssertBufferNonNull(Buffer buffer)
{
if (buffer == null || buffer.Handle == nint.Zero)
{
throw new System.NullReferenceException("Buffer must not be null!");
}
}
private void AssertBufferHasComputeStorageReadFlag(Buffer buffer)
{
if ((buffer.UsageFlags & BufferUsageFlags.ComputeStorageRead) == 0)
{
throw new System.ArgumentException("The bound Buffer's UsageFlags must include BufferUsageFlag.ComputeStorageRead!");
}
}
#endif
}

@@ -0,0 +1,25 @@
using System.Collections.Concurrent;
namespace Nerfed.Runtime.Graphics;
internal class ComputePassPool
{
private ConcurrentQueue<ComputePass> ComputePasses = new ConcurrentQueue<ComputePass>();
public ComputePass Obtain()
{
if (ComputePasses.TryDequeue(out ComputePass computePass))
{
return computePass;
}
else
{
return new ComputePass();
}
}
public void Return(ComputePass computePass)
{
ComputePasses.Enqueue(computePass);
}
}

@@ -0,0 +1,241 @@
using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
public class CopyPass
{
public nint Handle { get; private set; }
internal void SetHandle(nint handle)
{
Handle = handle;
}
/// <summary>
/// Uploads data from a TransferBuffer to a TextureSlice.
/// This copy occurs on the GPU timeline.
///
/// Overwriting the contents of the TransferBuffer before the command buffer
/// has finished execution will cause undefined behavior.
///
/// You MAY assume that the copy has finished for subsequent commands.
/// </summary>
/// <param name="cycle">If true, cycles the texture if the given slice is bound.</param>
public void UploadToTexture(
in TextureTransferInfo source,
in TextureRegion destination,
bool cycle
) {
#if DEBUG
AssertTransferBufferNotMapped(source.TransferBuffer);
#endif
Refresh.Refresh_UploadToTexture(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
Conversions.BoolToInt(cycle)
);
}
/// <summary>
/// Uploads the contents of an entire buffer to a 2D texture with no mips.
/// </summary>
public void UploadToTexture(
TransferBuffer source,
Texture destination,
bool cycle
) {
UploadToTexture(
new TextureTransferInfo(source),
new TextureRegion(destination),
cycle
);
}
/// <summary>
/// Uploads data from a TransferBuffer to a Buffer.
/// This copy occurs on the GPU timeline.
///
/// Overwriting the contents of the TransferBuffer before the command buffer
/// has finished execution will cause undefined behavior.
///
/// You MAY assume that the copy has finished for subsequent commands.
/// </summary>
/// <param name="cycle">If true, cycles the buffer if it is bound.</param>
public void UploadToBuffer(
in TransferBufferLocation source,
in BufferRegion destination,
bool cycle
) {
#if DEBUG
AssertBufferBoundsCheck(source.TransferBuffer.Size, source.Offset, destination.Size);
AssertBufferBoundsCheck(destination.Buffer.Size, destination.Offset, destination.Size);
AssertTransferBufferNotMapped(source.TransferBuffer);
#endif
Refresh.Refresh_UploadToBuffer(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
Conversions.BoolToInt(cycle)
);
}
/// <summary>
/// Copies the entire contents of a TransferBuffer to a Buffer.
/// </summary>
public void UploadToBuffer(
TransferBuffer source,
Buffer destination,
bool cycle
) {
UploadToBuffer(
new TransferBufferLocation(source),
new BufferRegion(destination, 0, destination.Size),
cycle
);
}
/// <summary>
/// Copies data element-wise into from a TransferBuffer to a Buffer.
/// </summary>
public void UploadToBuffer<T>(
TransferBuffer source,
Buffer destination,
uint sourceStartElement,
uint destinationStartElement,
uint numElements,
bool cycle
) where T : unmanaged
{
int elementSize = Marshal.SizeOf<T>();
uint dataLengthInBytes = (uint) (elementSize * numElements);
uint srcOffsetInBytes = (uint) (elementSize * sourceStartElement);
uint dstOffsetInBytes = (uint) (elementSize * destinationStartElement);
UploadToBuffer(
new TransferBufferLocation(source, srcOffsetInBytes),
new BufferRegion(destination, dstOffsetInBytes, dataLengthInBytes),
cycle
);
}
/// <summary>
/// Copies the contents of a TextureLocation to another TextureLocation.
/// This copy occurs on the GPU timeline.
///
/// You MAY assume that the copy has finished in subsequent commands.
/// </summary>
public void CopyTextureToTexture(
in TextureLocation source,
in TextureLocation destination,
uint w,
uint h,
uint d,
bool cycle
) {
#if DEBUG
AssertTextureBoundsCheck(source, w, h, d);
AssertTextureBoundsCheck(destination, w, h, d);
#endif
Refresh.Refresh_CopyTextureToTexture(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
w,
h,
d,
Conversions.BoolToInt(cycle)
);
}
/// <summary>
/// Copies data from a Buffer to another Buffer.
/// This copy occurs on the GPU timeline.
///
/// You MAY assume that the copy has finished in subsequent commands.
/// </summary>
public void CopyBufferToBuffer(
in BufferLocation source,
in BufferLocation destination,
uint size,
bool cycle
) {
#if DEBUG
AssertBufferBoundsCheck(source.Buffer.Size, source.Offset, size);
AssertBufferBoundsCheck(destination.Buffer.Size, destination.Offset, size);
#endif
Refresh.Refresh_CopyBufferToBuffer(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
size,
Conversions.BoolToInt(cycle)
);
}
public void DownloadFromBuffer(
in BufferRegion source,
in TransferBufferLocation destination
) {
#if DEBUG
AssertBufferBoundsCheck(source.Buffer.Size, source.Offset, source.Size);
AssertBufferBoundsCheck(destination.TransferBuffer.Size, destination.Offset, source.Size);
AssertTransferBufferNotMapped(destination.TransferBuffer);
#endif
Refresh.Refresh_DownloadFromBuffer(
Handle,
source.ToRefresh(),
destination.ToRefresh()
);
}
public void DownloadFromTexture(
in TextureRegion source,
in TextureTransferInfo destination
) {
#if DEBUG
AssertTransferBufferNotMapped(destination.TransferBuffer);
#endif
Refresh.Refresh_DownloadFromTexture(
Handle,
source.ToRefresh(),
destination.ToRefresh()
);
}
#if DEBUG
private void AssertBufferBoundsCheck(uint bufferLengthInBytes, uint offsetInBytes, uint copyLengthInBytes)
{
if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes)
{
throw new System.InvalidOperationException($"SetBufferData overflow! buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}");
}
}
private void AssertTextureBoundsCheck(in TextureLocation textureLocation, uint w, uint h, uint d)
{
if (
textureLocation.X + w > textureLocation.TextureSlice.Texture.Width ||
textureLocation.Y + h > textureLocation.TextureSlice.Texture.Height ||
textureLocation.Z + d > textureLocation.TextureSlice.Texture.Depth
) {
throw new System.InvalidOperationException($"Texture data is out of bounds!");
}
}
private void AssertTransferBufferNotMapped(TransferBuffer transferBuffer)
{
if (transferBuffer.Mapped)
{
throw new System.InvalidOperationException("Transfer buffer must not be mapped!");
}
}
#endif
}

@@ -0,0 +1,25 @@
using System.Collections.Concurrent;
namespace Nerfed.Runtime.Graphics;
internal class CopyPassPool
{
private ConcurrentQueue<CopyPass> CopyPasses = new ConcurrentQueue<CopyPass>();
public CopyPass Obtain()
{
if (CopyPasses.TryDequeue(out CopyPass copyPass))
{
return copyPass;
}
else
{
return new CopyPass();
}
}
public void Return(CopyPass copyPass)
{
CopyPasses.Enqueue(copyPass);
}
}

@@ -0,0 +1,716 @@
namespace Nerfed.Runtime.Graphics;
internal class EmbeddedShadersSpirV : IEmbeddedShaders
{
public ShaderFormat ShaderFormat => ShaderFormat.SPIRV;
public byte[] FullscreenVert { get; } =
[
0x3, 0x2, 0x23, 0x7, 0x0, 0x0, 0x1, 0x0, 0xB, 0x0,
0x8, 0x0, 0x2E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x11, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0xB, 0x0,
0x6, 0x0, 0x1, 0x0, 0x0, 0x0, 0x47, 0x4C, 0x53, 0x4C,
0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x0, 0x0,
0x0, 0x0, 0xE, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0xF, 0x0, 0x8, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6D, 0x61, 0x69, 0x6E,
0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0xC, 0x0,
0x0, 0x0, 0x1D, 0x0, 0x0, 0x0, 0x3, 0x0, 0x3, 0x0,
0x2, 0x0, 0x0, 0x0, 0xC2, 0x1, 0x0, 0x0, 0x5, 0x0,
0x4, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6D, 0x61, 0x69, 0x6E,
0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x5, 0x0, 0x9, 0x0,
0x0, 0x0, 0x6F, 0x75, 0x74, 0x54, 0x65, 0x78, 0x43, 0x6F,
0x6F, 0x72, 0x64, 0x0, 0x5, 0x0, 0x6, 0x0, 0xC, 0x0,
0x0, 0x0, 0x67, 0x6C, 0x5F, 0x56, 0x65, 0x72, 0x74, 0x65,
0x78, 0x49, 0x6E, 0x64, 0x65, 0x78, 0x0, 0x0, 0x5, 0x0,
0x6, 0x0, 0x1B, 0x0, 0x0, 0x0, 0x67, 0x6C, 0x5F, 0x50,
0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x0, 0x0,
0x0, 0x0, 0x6, 0x0, 0x6, 0x0, 0x1B, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73,
0x69, 0x74, 0x69, 0x6F, 0x6E, 0x0, 0x6, 0x0, 0x7, 0x0,
0x1B, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x67, 0x6C,
0x5F, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65,
0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x7, 0x0, 0x1B, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x67, 0x6C, 0x5F, 0x43,
0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63,
0x65, 0x0, 0x6, 0x0, 0x7, 0x0, 0x1B, 0x0, 0x0, 0x0,
0x3, 0x0, 0x0, 0x0, 0x67, 0x6C, 0x5F, 0x43, 0x75, 0x6C,
0x6C, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x0,
0x5, 0x0, 0x3, 0x0, 0x1D, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0x9, 0x0, 0x0, 0x0,
0x1E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47, 0x0,
0x4, 0x0, 0xC, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x2A, 0x0, 0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0x1B, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0x1B, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0x1B, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x3, 0x0, 0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0x1B, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x4, 0x0, 0x0, 0x0, 0x47, 0x0, 0x3, 0x0, 0x1B, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x13, 0x0, 0x2, 0x0,
0x2, 0x0, 0x0, 0x0, 0x21, 0x0, 0x3, 0x0, 0x3, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x16, 0x0, 0x3, 0x0,
0x6, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x17, 0x0,
0x4, 0x0, 0x7, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0,
0x2, 0x0, 0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x8, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0,
0x3B, 0x0, 0x4, 0x0, 0x8, 0x0, 0x0, 0x0, 0x9, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x15, 0x0, 0x4, 0x0,
0xA, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0xB, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0xB, 0x0, 0x0, 0x0, 0xC, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0xA, 0x0,
0x0, 0x0, 0xE, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x2B, 0x0, 0x4, 0x0, 0xA, 0x0, 0x0, 0x0, 0x10, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x17, 0x0, 0x4, 0x0,
0x17, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x4, 0x0,
0x0, 0x0, 0x15, 0x0, 0x4, 0x0, 0x18, 0x0, 0x0, 0x0,
0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2B, 0x0,
0x4, 0x0, 0x18, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x4, 0x0, 0x1A, 0x0,
0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0,
0x1E, 0x0, 0x6, 0x0, 0x1B, 0x0, 0x0, 0x0, 0x17, 0x0,
0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0x0,
0x1A, 0x0, 0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x1C, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1B, 0x0, 0x0, 0x0,
0x3B, 0x0, 0x4, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x1D, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0,
0xA, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0,
0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x2B, 0x0,
0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x21, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xC0, 0x2C, 0x0, 0x5, 0x0, 0x7, 0x0,
0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
0x21, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xBF,
0x2B, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x25, 0x0,
0x0, 0x0, 0x0, 0x0, 0x80, 0x3F, 0x2C, 0x0, 0x5, 0x0,
0x7, 0x0, 0x0, 0x0, 0x26, 0x0, 0x0, 0x0, 0x24, 0x0,
0x0, 0x0, 0x25, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0,
0x6, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x2C, 0x0, 0x0, 0x0,
0x3, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x36, 0x0,
0x5, 0x0, 0x2, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0xF8, 0x0,
0x2, 0x0, 0x5, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0,
0xA, 0x0, 0x0, 0x0, 0xD, 0x0, 0x0, 0x0, 0xC, 0x0,
0x0, 0x0, 0xC4, 0x0, 0x5, 0x0, 0xA, 0x0, 0x0, 0x0,
0xF, 0x0, 0x0, 0x0, 0xD, 0x0, 0x0, 0x0, 0xE, 0x0,
0x0, 0x0, 0xC7, 0x0, 0x5, 0x0, 0xA, 0x0, 0x0, 0x0,
0x11, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x10, 0x0,
0x0, 0x0, 0x6F, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0,
0x12, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x3D, 0x0,
0x4, 0x0, 0xA, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0,
0xC, 0x0, 0x0, 0x0, 0xC7, 0x0, 0x5, 0x0, 0xA, 0x0,
0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0,
0x10, 0x0, 0x0, 0x0, 0x6F, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x15, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0,
0x50, 0x0, 0x5, 0x0, 0x7, 0x0, 0x0, 0x0, 0x16, 0x0,
0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x15, 0x0, 0x0, 0x0,
0x3E, 0x0, 0x3, 0x0, 0x9, 0x0, 0x0, 0x0, 0x16, 0x0,
0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x7, 0x0, 0x0, 0x0,
0x1F, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x85, 0x0,
0x5, 0x0, 0x7, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0,
0x1F, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x81, 0x0,
0x5, 0x0, 0x7, 0x0, 0x0, 0x0, 0x27, 0x0, 0x0, 0x0,
0x23, 0x0, 0x0, 0x0, 0x26, 0x0, 0x0, 0x0, 0x51, 0x0,
0x5, 0x0, 0x6, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0,
0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x51, 0x0,
0x5, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2A, 0x0, 0x0, 0x0,
0x27, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x50, 0x0,
0x7, 0x0, 0x17, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x0, 0x0,
0x29, 0x0, 0x0, 0x0, 0x2A, 0x0, 0x0, 0x0, 0x28, 0x0,
0x0, 0x0, 0x25, 0x0, 0x0, 0x0, 0x41, 0x0, 0x5, 0x0,
0x2C, 0x0, 0x0, 0x0, 0x2D, 0x0, 0x0, 0x0, 0x1D, 0x0,
0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x3, 0x0,
0x2D, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x0, 0x0, 0xFD, 0x0,
0x1, 0x0, 0x38, 0x0, 0x1, 0x0,
];
public byte[] TextMsdfFrag { get; } =
[
0x3, 0x2, 0x23, 0x7, 0x0, 0x0, 0x1, 0x0, 0xB, 0x0,
0x8, 0x0, 0x6C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x11, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x11, 0x0,
0x2, 0x0, 0x32, 0x0, 0x0, 0x0, 0xB, 0x0, 0x6, 0x0,
0x1, 0x0, 0x0, 0x0, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73,
0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x0, 0x0, 0x0, 0x0,
0xE, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0,
0x0, 0x0, 0xF, 0x0, 0x8, 0x0, 0x4, 0x0, 0x0, 0x0,
0x4, 0x0, 0x0, 0x0, 0x6D, 0x61, 0x69, 0x6E, 0x0, 0x0,
0x0, 0x0, 0x36, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0,
0x67, 0x0, 0x0, 0x0, 0x10, 0x0, 0x3, 0x0, 0x4, 0x0,
0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x3, 0x0, 0x3, 0x0,
0x2, 0x0, 0x0, 0x0, 0xC2, 0x1, 0x0, 0x0, 0x5, 0x0,
0x4, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6D, 0x61, 0x69, 0x6E,
0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x7, 0x0, 0xC, 0x0,
0x0, 0x0, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x6E, 0x28, 0x66,
0x31, 0x3B, 0x66, 0x31, 0x3B, 0x66, 0x31, 0x3B, 0x0, 0x0,
0x0, 0x0, 0x5, 0x0, 0x3, 0x0, 0x9, 0x0, 0x0, 0x0,
0x72, 0x0, 0x0, 0x0, 0x5, 0x0, 0x3, 0x0, 0xA, 0x0,
0x0, 0x0, 0x67, 0x0, 0x0, 0x0, 0x5, 0x0, 0x3, 0x0,
0xB, 0x0, 0x0, 0x0, 0x62, 0x0, 0x0, 0x0, 0x5, 0x0,
0x6, 0x0, 0xF, 0x0, 0x0, 0x0, 0x73, 0x63, 0x72, 0x65,
0x65, 0x6E, 0x50, 0x78, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x28,
0x0, 0x0, 0x5, 0x0, 0x5, 0x0, 0x1E, 0x0, 0x0, 0x0,
0x75, 0x6E, 0x69, 0x74, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x0,
0x0, 0x0, 0x5, 0x0, 0x3, 0x0, 0x1F, 0x0, 0x0, 0x0,
0x55, 0x42, 0x4F, 0x0, 0x6, 0x0, 0x5, 0x0, 0x1F, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x78, 0x52, 0x61,
0x6E, 0x67, 0x65, 0x0, 0x5, 0x0, 0x3, 0x0, 0x21, 0x0,
0x0, 0x0, 0x75, 0x62, 0x6F, 0x0, 0x5, 0x0, 0x4, 0x0,
0x2B, 0x0, 0x0, 0x0, 0x6D, 0x73, 0x64, 0x66, 0x0, 0x0,
0x0, 0x0, 0x5, 0x0, 0x6, 0x0, 0x32, 0x0, 0x0, 0x0,
0x73, 0x63, 0x72, 0x65, 0x65, 0x6E, 0x54, 0x65, 0x78, 0x53,
0x69, 0x7A, 0x65, 0x0, 0x0, 0x0, 0x5, 0x0, 0x5, 0x0,
0x36, 0x0, 0x0, 0x0, 0x69, 0x6E, 0x54, 0x65, 0x78, 0x43,
0x6F, 0x6F, 0x72, 0x64, 0x0, 0x0, 0x5, 0x0, 0x3, 0x0,
0x44, 0x0, 0x0, 0x0, 0x6D, 0x73, 0x64, 0x0, 0x5, 0x0,
0x3, 0x0, 0x4A, 0x0, 0x0, 0x0, 0x73, 0x64, 0x0, 0x0,
0x5, 0x0, 0x4, 0x0, 0x4B, 0x0, 0x0, 0x0, 0x70, 0x61,
0x72, 0x61, 0x6D, 0x0, 0x0, 0x0, 0x5, 0x0, 0x4, 0x0,
0x50, 0x0, 0x0, 0x0, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x0,
0x0, 0x0, 0x5, 0x0, 0x4, 0x0, 0x54, 0x0, 0x0, 0x0,
0x70, 0x61, 0x72, 0x61, 0x6D, 0x0, 0x0, 0x0, 0x5, 0x0,
0x7, 0x0, 0x59, 0x0, 0x0, 0x0, 0x73, 0x63, 0x72, 0x65,
0x65, 0x6E, 0x50, 0x78, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E,
0x63, 0x65, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x4, 0x0,
0x5E, 0x0, 0x0, 0x0, 0x6F, 0x70, 0x61, 0x63, 0x69, 0x74,
0x79, 0x0, 0x5, 0x0, 0x5, 0x0, 0x64, 0x0, 0x0, 0x0,
0x6F, 0x75, 0x74, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x0, 0x0,
0x0, 0x0, 0x5, 0x0, 0x4, 0x0, 0x67, 0x0, 0x0, 0x0,
0x69, 0x6E, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x0, 0x48, 0x0,
0x5, 0x0, 0x1F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47, 0x0,
0x3, 0x0, 0x1F, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0,
0x47, 0x0, 0x4, 0x0, 0x21, 0x0, 0x0, 0x0, 0x22, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0,
0x21, 0x0, 0x0, 0x0, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0x2B, 0x0, 0x0, 0x0,
0x22, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x47, 0x0,
0x4, 0x0, 0x2B, 0x0, 0x0, 0x0, 0x21, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0x36, 0x0,
0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x47, 0x0, 0x4, 0x0, 0x64, 0x0, 0x0, 0x0, 0x1E, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0,
0x67, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x1, 0x0,
0x0, 0x0, 0x13, 0x0, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0,
0x21, 0x0, 0x3, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x16, 0x0, 0x3, 0x0, 0x6, 0x0, 0x0, 0x0,
0x20, 0x0, 0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x7, 0x0,
0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0,
0x21, 0x0, 0x6, 0x0, 0x8, 0x0, 0x0, 0x0, 0x6, 0x0,
0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0,
0x7, 0x0, 0x0, 0x0, 0x21, 0x0, 0x3, 0x0, 0xE, 0x0,
0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x17, 0x0, 0x4, 0x0,
0x1C, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x1D, 0x0, 0x0, 0x0,
0x7, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x1E, 0x0,
0x3, 0x0, 0x1F, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0,
0x20, 0x0, 0x4, 0x0, 0x20, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x1F, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0,
0x20, 0x0, 0x0, 0x0, 0x21, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x15, 0x0, 0x4, 0x0, 0x22, 0x0, 0x0, 0x0,
0x20, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2B, 0x0,
0x4, 0x0, 0x22, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x24, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0,
0x19, 0x0, 0x9, 0x0, 0x28, 0x0, 0x0, 0x0, 0x6, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x0, 0x3, 0x0,
0x29, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x20, 0x0,
0x4, 0x0, 0x2A, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x29, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x2A, 0x0,
0x0, 0x0, 0x2B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x17, 0x0, 0x4, 0x0, 0x2E, 0x0, 0x0, 0x0, 0x22, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0,
0x6, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x0, 0x0, 0x0,
0x80, 0x3F, 0x2C, 0x0, 0x5, 0x0, 0x1C, 0x0, 0x0, 0x0,
0x34, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x0, 0x33, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x35, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0x35, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x3A, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F,
0x17, 0x0, 0x4, 0x0, 0x42, 0x0, 0x0, 0x0, 0x6, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x20, 0x0, 0x4, 0x0,
0x43, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x42, 0x0,
0x0, 0x0, 0x17, 0x0, 0x4, 0x0, 0x47, 0x0, 0x0, 0x0,
0x6, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x15, 0x0,
0x4, 0x0, 0x4C, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x4C, 0x0,
0x0, 0x0, 0x4D, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2B, 0x0, 0x4, 0x0, 0x4C, 0x0, 0x0, 0x0, 0x51, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0,
0x4C, 0x0, 0x0, 0x0, 0x55, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0,
0x61, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0,
0x4, 0x0, 0x63, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0,
0x47, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x63, 0x0,
0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0,
0x2C, 0x0, 0x7, 0x0, 0x47, 0x0, 0x0, 0x0, 0x65, 0x0,
0x0, 0x0, 0x61, 0x0, 0x0, 0x0, 0x61, 0x0, 0x0, 0x0,
0x61, 0x0, 0x0, 0x0, 0x61, 0x0, 0x0, 0x0, 0x20, 0x0,
0x4, 0x0, 0x66, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x47, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x66, 0x0,
0x0, 0x0, 0x67, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x36, 0x0, 0x5, 0x0, 0x2, 0x0, 0x0, 0x0, 0x4, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0,
0xF8, 0x0, 0x2, 0x0, 0x5, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0x43, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0,
0x7, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x7, 0x0,
0x0, 0x0, 0x4A, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0,
0x3B, 0x0, 0x4, 0x0, 0x7, 0x0, 0x0, 0x0, 0x4B, 0x0,
0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0,
0x7, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x7, 0x0,
0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x7, 0x0, 0x0, 0x0,
0x54, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0x7, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x0,
0x7, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x7, 0x0,
0x0, 0x0, 0x5E, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x29, 0x0, 0x0, 0x0, 0x45, 0x0,
0x0, 0x0, 0x2B, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0,
0x1C, 0x0, 0x0, 0x0, 0x46, 0x0, 0x0, 0x0, 0x36, 0x0,
0x0, 0x0, 0x57, 0x0, 0x5, 0x0, 0x47, 0x0, 0x0, 0x0,
0x48, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x0, 0x46, 0x0,
0x0, 0x0, 0x4F, 0x0, 0x8, 0x0, 0x42, 0x0, 0x0, 0x0,
0x49, 0x0, 0x0, 0x0, 0x48, 0x0, 0x0, 0x0, 0x48, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x2, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x44, 0x0,
0x0, 0x0, 0x49, 0x0, 0x0, 0x0, 0x41, 0x0, 0x5, 0x0,
0x7, 0x0, 0x0, 0x0, 0x4E, 0x0, 0x0, 0x0, 0x44, 0x0,
0x0, 0x0, 0x4D, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0,
0x6, 0x0, 0x0, 0x0, 0x4F, 0x0, 0x0, 0x0, 0x4E, 0x0,
0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x4B, 0x0, 0x0, 0x0,
0x4F, 0x0, 0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x7, 0x0,
0x0, 0x0, 0x52, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0,
0x51, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x53, 0x0, 0x0, 0x0, 0x52, 0x0, 0x0, 0x0,
0x3E, 0x0, 0x3, 0x0, 0x50, 0x0, 0x0, 0x0, 0x53, 0x0,
0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x7, 0x0, 0x0, 0x0,
0x56, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x55, 0x0,
0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0,
0x57, 0x0, 0x0, 0x0, 0x56, 0x0, 0x0, 0x0, 0x3E, 0x0,
0x3, 0x0, 0x54, 0x0, 0x0, 0x0, 0x57, 0x0, 0x0, 0x0,
0x39, 0x0, 0x7, 0x0, 0x6, 0x0, 0x0, 0x0, 0x58, 0x0,
0x0, 0x0, 0xC, 0x0, 0x0, 0x0, 0x4B, 0x0, 0x0, 0x0,
0x50, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x3E, 0x0,
0x3, 0x0, 0x4A, 0x0, 0x0, 0x0, 0x58, 0x0, 0x0, 0x0,
0x39, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x5A, 0x0,
0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0,
0x6, 0x0, 0x0, 0x0, 0x5B, 0x0, 0x0, 0x0, 0x4A, 0x0,
0x0, 0x0, 0x83, 0x0, 0x5, 0x0, 0x6, 0x0, 0x0, 0x0,
0x5C, 0x0, 0x0, 0x0, 0x5B, 0x0, 0x0, 0x0, 0x3A, 0x0,
0x0, 0x0, 0x85, 0x0, 0x5, 0x0, 0x6, 0x0, 0x0, 0x0,
0x5D, 0x0, 0x0, 0x0, 0x5A, 0x0, 0x0, 0x0, 0x5C, 0x0,
0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x59, 0x0, 0x0, 0x0,
0x5D, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x5F, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x0,
0x81, 0x0, 0x5, 0x0, 0x6, 0x0, 0x0, 0x0, 0x60, 0x0,
0x0, 0x0, 0x5F, 0x0, 0x0, 0x0, 0x3A, 0x0, 0x0, 0x0,
0xC, 0x0, 0x8, 0x0, 0x6, 0x0, 0x0, 0x0, 0x62, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x0, 0x0,
0x60, 0x0, 0x0, 0x0, 0x61, 0x0, 0x0, 0x0, 0x33, 0x0,
0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x5E, 0x0, 0x0, 0x0,
0x62, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x47, 0x0,
0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x69, 0x0,
0x0, 0x0, 0x5E, 0x0, 0x0, 0x0, 0x50, 0x0, 0x7, 0x0,
0x47, 0x0, 0x0, 0x0, 0x6A, 0x0, 0x0, 0x0, 0x69, 0x0,
0x0, 0x0, 0x69, 0x0, 0x0, 0x0, 0x69, 0x0, 0x0, 0x0,
0x69, 0x0, 0x0, 0x0, 0xC, 0x0, 0x8, 0x0, 0x47, 0x0,
0x0, 0x0, 0x6B, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x2E, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x68, 0x0,
0x0, 0x0, 0x6A, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x3, 0x0,
0x64, 0x0, 0x0, 0x0, 0x6B, 0x0, 0x0, 0x0, 0xFD, 0x0,
0x1, 0x0, 0x38, 0x0, 0x1, 0x0, 0x36, 0x0, 0x5, 0x0,
0x6, 0x0, 0x0, 0x0, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x37, 0x0, 0x3, 0x0,
0x7, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x37, 0x0,
0x3, 0x0, 0x7, 0x0, 0x0, 0x0, 0xA, 0x0, 0x0, 0x0,
0x37, 0x0, 0x3, 0x0, 0x7, 0x0, 0x0, 0x0, 0xB, 0x0,
0x0, 0x0, 0xF8, 0x0, 0x2, 0x0, 0xD, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x11, 0x0,
0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0,
0x6, 0x0, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0xA, 0x0,
0x0, 0x0, 0xC, 0x0, 0x7, 0x0, 0x6, 0x0, 0x0, 0x0,
0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x25, 0x0,
0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x14, 0x0,
0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0,
0x6, 0x0, 0x0, 0x0, 0x15, 0x0, 0x0, 0x0, 0xA, 0x0,
0x0, 0x0, 0xC, 0x0, 0x7, 0x0, 0x6, 0x0, 0x0, 0x0,
0x16, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x28, 0x0,
0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x15, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x17, 0x0,
0x0, 0x0, 0xB, 0x0, 0x0, 0x0, 0xC, 0x0, 0x7, 0x0,
0x6, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x1, 0x0,
0x0, 0x0, 0x25, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0,
0x17, 0x0, 0x0, 0x0, 0xC, 0x0, 0x7, 0x0, 0x6, 0x0,
0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x28, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x18, 0x0,
0x0, 0x0, 0xFE, 0x0, 0x2, 0x0, 0x19, 0x0, 0x0, 0x0,
0x38, 0x0, 0x1, 0x0, 0x36, 0x0, 0x5, 0x0, 0x6, 0x0,
0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xE, 0x0, 0x0, 0x0, 0xF8, 0x0, 0x2, 0x0, 0x10, 0x0,
0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x1D, 0x0, 0x0, 0x0,
0x1E, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0x1D, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x0,
0x7, 0x0, 0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x24, 0x0,
0x0, 0x0, 0x25, 0x0, 0x0, 0x0, 0x21, 0x0, 0x0, 0x0,
0x23, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x26, 0x0, 0x0, 0x0, 0x25, 0x0, 0x0, 0x0,
0x50, 0x0, 0x5, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x27, 0x0,
0x0, 0x0, 0x26, 0x0, 0x0, 0x0, 0x26, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x29, 0x0, 0x0, 0x0, 0x2C, 0x0,
0x0, 0x0, 0x2B, 0x0, 0x0, 0x0, 0x64, 0x0, 0x4, 0x0,
0x28, 0x0, 0x0, 0x0, 0x2D, 0x0, 0x0, 0x0, 0x2C, 0x0,
0x0, 0x0, 0x67, 0x0, 0x5, 0x0, 0x2E, 0x0, 0x0, 0x0,
0x2F, 0x0, 0x0, 0x0, 0x2D, 0x0, 0x0, 0x0, 0x23, 0x0,
0x0, 0x0, 0x6F, 0x0, 0x4, 0x0, 0x1C, 0x0, 0x0, 0x0,
0x30, 0x0, 0x0, 0x0, 0x2F, 0x0, 0x0, 0x0, 0x88, 0x0,
0x5, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0,
0x27, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x3E, 0x0,
0x3, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x37, 0x0,
0x0, 0x0, 0x36, 0x0, 0x0, 0x0, 0xD1, 0x0, 0x4, 0x0,
0x1C, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x37, 0x0,
0x0, 0x0, 0x88, 0x0, 0x5, 0x0, 0x1C, 0x0, 0x0, 0x0,
0x39, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x38, 0x0,
0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x32, 0x0, 0x0, 0x0,
0x39, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x1C, 0x0,
0x0, 0x0, 0x3B, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x3C, 0x0,
0x0, 0x0, 0x32, 0x0, 0x0, 0x0, 0x94, 0x0, 0x5, 0x0,
0x6, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x0, 0x0, 0x3C, 0x0, 0x0, 0x0, 0x85, 0x0, 0x5, 0x0,
0x6, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x0, 0x0, 0x3A, 0x0,
0x0, 0x0, 0x3D, 0x0, 0x0, 0x0, 0xC, 0x0, 0x7, 0x0,
0x6, 0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x1, 0x0,
0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x0, 0x0,
0x33, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x2, 0x0, 0x3F, 0x0,
0x0, 0x0, 0x38, 0x0, 0x1, 0x0,
];
public byte[] TextTransformVert { get; } =
[
0x3, 0x2, 0x23, 0x7, 0x0, 0x0, 0x1, 0x0, 0xB, 0x0,
0x8, 0x0, 0x2D, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x11, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0xB, 0x0,
0x6, 0x0, 0x1, 0x0, 0x0, 0x0, 0x47, 0x4C, 0x53, 0x4C,
0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x0, 0x0,
0x0, 0x0, 0xE, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0xF, 0x0, 0xB, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6D, 0x61, 0x69, 0x6E,
0x0, 0x0, 0x0, 0x0, 0xD, 0x0, 0x0, 0x0, 0x19, 0x0,
0x0, 0x0, 0x25, 0x0, 0x0, 0x0, 0x27, 0x0, 0x0, 0x0,
0x29, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x0, 0x0, 0x3, 0x0,
0x3, 0x0, 0x2, 0x0, 0x0, 0x0, 0xC2, 0x1, 0x0, 0x0,
0x5, 0x0, 0x4, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6D, 0x61,
0x69, 0x6E, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x6, 0x0,
0xB, 0x0, 0x0, 0x0, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72,
0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x0, 0x0, 0x0, 0x0,
0x6, 0x0, 0x6, 0x0, 0xB, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74,
0x69, 0x6F, 0x6E, 0x0, 0x6, 0x0, 0x7, 0x0, 0xB, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x67, 0x6C, 0x5F, 0x50,
0x6F, 0x69, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65, 0x0, 0x0,
0x0, 0x0, 0x6, 0x0, 0x7, 0x0, 0xB, 0x0, 0x0, 0x0,
0x2, 0x0, 0x0, 0x0, 0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69,
0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x0,
0x6, 0x0, 0x7, 0x0, 0xB, 0x0, 0x0, 0x0, 0x3, 0x0,
0x0, 0x0, 0x67, 0x6C, 0x5F, 0x43, 0x75, 0x6C, 0x6C, 0x44,
0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x0, 0x5, 0x0,
0x3, 0x0, 0xD, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x5, 0x0, 0x3, 0x0, 0x11, 0x0, 0x0, 0x0, 0x55, 0x42,
0x4F, 0x0, 0x6, 0x0, 0x7, 0x0, 0x11, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72,
0x6F, 0x6A, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x0, 0x0,
0x5, 0x0, 0x3, 0x0, 0x13, 0x0, 0x0, 0x0, 0x75, 0x62,
0x6F, 0x0, 0x5, 0x0, 0x4, 0x0, 0x19, 0x0, 0x0, 0x0,
0x69, 0x6E, 0x50, 0x6F, 0x73, 0x0, 0x0, 0x0, 0x5, 0x0,
0x5, 0x0, 0x25, 0x0, 0x0, 0x0, 0x6F, 0x75, 0x74, 0x54,
0x65, 0x78, 0x43, 0x6F, 0x6F, 0x72, 0x64, 0x0, 0x5, 0x0,
0x5, 0x0, 0x27, 0x0, 0x0, 0x0, 0x69, 0x6E, 0x54, 0x65,
0x78, 0x43, 0x6F, 0x6F, 0x72, 0x64, 0x0, 0x0, 0x5, 0x0,
0x5, 0x0, 0x29, 0x0, 0x0, 0x0, 0x6F, 0x75, 0x74, 0x43,
0x6F, 0x6C, 0x6F, 0x72, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0,
0x4, 0x0, 0x2B, 0x0, 0x0, 0x0, 0x69, 0x6E, 0x43, 0x6F,
0x6C, 0x6F, 0x72, 0x0, 0x48, 0x0, 0x5, 0x0, 0xB, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0xB, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0xB, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x3, 0x0, 0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0xB, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x4, 0x0, 0x0, 0x0, 0x47, 0x0, 0x3, 0x0, 0xB, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x48, 0x0, 0x4, 0x0,
0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0,
0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0x11, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x48, 0x0, 0x5, 0x0, 0x11, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x10, 0x0,
0x0, 0x0, 0x47, 0x0, 0x3, 0x0, 0x11, 0x0, 0x0, 0x0,
0x2, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0x13, 0x0,
0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x47, 0x0, 0x4, 0x0, 0x13, 0x0, 0x0, 0x0, 0x21, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0,
0x19, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0x25, 0x0, 0x0, 0x0,
0x1E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47, 0x0,
0x4, 0x0, 0x27, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0x29, 0x0,
0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x47, 0x0, 0x4, 0x0, 0x2B, 0x0, 0x0, 0x0, 0x1E, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x13, 0x0, 0x2, 0x0,
0x2, 0x0, 0x0, 0x0, 0x21, 0x0, 0x3, 0x0, 0x3, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x16, 0x0, 0x3, 0x0,
0x6, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x17, 0x0,
0x4, 0x0, 0x7, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0,
0x4, 0x0, 0x0, 0x0, 0x15, 0x0, 0x4, 0x0, 0x8, 0x0,
0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2B, 0x0, 0x4, 0x0, 0x8, 0x0, 0x0, 0x0, 0x9, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x4, 0x0,
0xA, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x9, 0x0,
0x0, 0x0, 0x1E, 0x0, 0x6, 0x0, 0xB, 0x0, 0x0, 0x0,
0x7, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0xA, 0x0,
0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x20, 0x0, 0x4, 0x0,
0xC, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0xB, 0x0,
0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0xC, 0x0, 0x0, 0x0,
0xD, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x15, 0x0,
0x4, 0x0, 0xE, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0xE, 0x0,
0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x18, 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x0, 0x7, 0x0,
0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x3, 0x0,
0x11, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x20, 0x0,
0x4, 0x0, 0x12, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0,
0x11, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x12, 0x0,
0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0,
0x20, 0x0, 0x4, 0x0, 0x14, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x17, 0x0, 0x4, 0x0,
0x17, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x3, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x18, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0x18, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x1B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3F,
0x20, 0x0, 0x4, 0x0, 0x21, 0x0, 0x0, 0x0, 0x3, 0x0,
0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x17, 0x0, 0x4, 0x0,
0x23, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x24, 0x0, 0x0, 0x0,
0x3, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0x24, 0x0, 0x0, 0x0, 0x25, 0x0, 0x0, 0x0,
0x3, 0x0, 0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x26, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0,
0x3B, 0x0, 0x4, 0x0, 0x26, 0x0, 0x0, 0x0, 0x27, 0x0,
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0,
0x21, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x3, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x2A, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0x2A, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x5, 0x0, 0x2, 0x0,
0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x3, 0x0, 0x0, 0x0, 0xF8, 0x0, 0x2, 0x0, 0x5, 0x0,
0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x14, 0x0, 0x0, 0x0,
0x15, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0xF, 0x0,
0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x0,
0x16, 0x0, 0x0, 0x0, 0x15, 0x0, 0x0, 0x0, 0x3D, 0x0,
0x4, 0x0, 0x17, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0x0,
0x19, 0x0, 0x0, 0x0, 0x51, 0x0, 0x5, 0x0, 0x6, 0x0,
0x0, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x51, 0x0, 0x5, 0x0, 0x6, 0x0,
0x0, 0x0, 0x1D, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x51, 0x0, 0x5, 0x0, 0x6, 0x0,
0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0x0,
0x2, 0x0, 0x0, 0x0, 0x50, 0x0, 0x7, 0x0, 0x7, 0x0,
0x0, 0x0, 0x1F, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x0,
0x1D, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x1B, 0x0,
0x0, 0x0, 0x91, 0x0, 0x5, 0x0, 0x7, 0x0, 0x0, 0x0,
0x20, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x1F, 0x0,
0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x21, 0x0, 0x0, 0x0,
0x22, 0x0, 0x0, 0x0, 0xD, 0x0, 0x0, 0x0, 0xF, 0x0,
0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x22, 0x0, 0x0, 0x0,
0x20, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x23, 0x0,
0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x27, 0x0, 0x0, 0x0,
0x3E, 0x0, 0x3, 0x0, 0x25, 0x0, 0x0, 0x0, 0x28, 0x0,
0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x7, 0x0, 0x0, 0x0,
0x2C, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x0, 0x0, 0x3E, 0x0,
0x3, 0x0, 0x29, 0x0, 0x0, 0x0, 0x2C, 0x0, 0x0, 0x0,
0xFD, 0x0, 0x1, 0x0, 0x38, 0x0, 0x1, 0x0,
];
public byte[] VideoYuv2RgbaFrag { get; } =
[
0x3, 0x2, 0x23, 0x7, 0x0, 0x0, 0x1, 0x0, 0xB, 0x0,
0x8, 0x0, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x11, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0xB, 0x0,
0x6, 0x0, 0x1, 0x0, 0x0, 0x0, 0x47, 0x4C, 0x53, 0x4C,
0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x0, 0x0,
0x0, 0x0, 0xE, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0xF, 0x0, 0x7, 0x0, 0x4, 0x0,
0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6D, 0x61, 0x69, 0x6E,
0x0, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x2E, 0x0,
0x0, 0x0, 0x10, 0x0, 0x3, 0x0, 0x4, 0x0, 0x0, 0x0,
0x7, 0x0, 0x0, 0x0, 0x3, 0x0, 0x3, 0x0, 0x2, 0x0,
0x0, 0x0, 0xC2, 0x1, 0x0, 0x0, 0x5, 0x0, 0x4, 0x0,
0x4, 0x0, 0x0, 0x0, 0x6D, 0x61, 0x69, 0x6E, 0x0, 0x0,
0x0, 0x0, 0x5, 0x0, 0x3, 0x0, 0x9, 0x0, 0x0, 0x0,
0x79, 0x75, 0x76, 0x0, 0x5, 0x0, 0x5, 0x0, 0xD, 0x0,
0x0, 0x0, 0x59, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72,
0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x5, 0x0, 0x11, 0x0,
0x0, 0x0, 0x54, 0x65, 0x78, 0x43, 0x6F, 0x6F, 0x72, 0x64,
0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x5, 0x0, 0x1A, 0x0,
0x0, 0x0, 0x55, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72,
0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x5, 0x0, 0x21, 0x0,
0x0, 0x0, 0x56, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72,
0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x5, 0x0, 0x2E, 0x0,
0x0, 0x0, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6F, 0x6C, 0x6F,
0x72, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0xD, 0x0,
0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0,
0x47, 0x0, 0x4, 0x0, 0xD, 0x0, 0x0, 0x0, 0x21, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0,
0x11, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0x1A, 0x0, 0x0, 0x0,
0x22, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x47, 0x0,
0x4, 0x0, 0x1A, 0x0, 0x0, 0x0, 0x21, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0, 0x21, 0x0,
0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0,
0x47, 0x0, 0x4, 0x0, 0x21, 0x0, 0x0, 0x0, 0x21, 0x0,
0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x47, 0x0, 0x4, 0x0,
0x2E, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x13, 0x0, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0,
0x21, 0x0, 0x3, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x16, 0x0, 0x3, 0x0, 0x6, 0x0, 0x0, 0x0,
0x20, 0x0, 0x0, 0x0, 0x17, 0x0, 0x4, 0x0, 0x7, 0x0,
0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0,
0x20, 0x0, 0x4, 0x0, 0x8, 0x0, 0x0, 0x0, 0x7, 0x0,
0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x19, 0x0, 0x9, 0x0,
0xA, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1B, 0x0, 0x3, 0x0, 0xB, 0x0, 0x0, 0x0,
0xA, 0x0, 0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0xC, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x0,
0x3B, 0x0, 0x4, 0x0, 0xC, 0x0, 0x0, 0x0, 0xD, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0x0, 0x4, 0x0,
0xF, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x10, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0x10, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x17, 0x0, 0x4, 0x0, 0x13, 0x0,
0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0,
0x15, 0x0, 0x4, 0x0, 0x15, 0x0, 0x0, 0x0, 0x20, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0,
0x15, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x18, 0x0, 0x0, 0x0,
0x7, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x3B, 0x0,
0x4, 0x0, 0xC, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x15, 0x0,
0x0, 0x0, 0x1F, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x3B, 0x0, 0x4, 0x0, 0xC, 0x0, 0x0, 0x0, 0x21, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0,
0x15, 0x0, 0x0, 0x0, 0x26, 0x0, 0x0, 0x0, 0x2, 0x0,
0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0,
0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xBD, 0x2B, 0x0,
0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xBF, 0x2C, 0x0, 0x6, 0x0, 0x7, 0x0,
0x0, 0x0, 0x2A, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0,
0x29, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x20, 0x0,
0x4, 0x0, 0x2D, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0,
0x13, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x2D, 0x0,
0x0, 0x0, 0x2E, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0,
0x2B, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x30, 0x0,
0x0, 0x0, 0xF4, 0xFD, 0x94, 0x3F, 0x2B, 0x0, 0x4, 0x0,
0x6, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0,
0x32, 0x0, 0x0, 0x0, 0x6, 0x81, 0xE5, 0x3F, 0x2C, 0x0,
0x6, 0x0, 0x7, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x0,
0x30, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0x32, 0x0,
0x0, 0x0, 0x20, 0x0, 0x4, 0x0, 0x35, 0x0, 0x0, 0x0,
0x3, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2B, 0x0,
0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0,
0xAC, 0x1C, 0x5A, 0xBE, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0xB0, 0x72, 0x8, 0xBF,
0x2C, 0x0, 0x6, 0x0, 0x7, 0x0, 0x0, 0x0, 0x3A, 0x0,
0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0,
0x39, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x3E, 0x0, 0x0, 0x0, 0x2, 0x2B, 0x7, 0x40,
0x2C, 0x0, 0x6, 0x0, 0x7, 0x0, 0x0, 0x0, 0x3F, 0x0,
0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x0, 0x0,
0x31, 0x0, 0x0, 0x0, 0x2B, 0x0, 0x4, 0x0, 0x6, 0x0,
0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3F,
0x2B, 0x0, 0x4, 0x0, 0x15, 0x0, 0x0, 0x0, 0x43, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x36, 0x0, 0x5, 0x0,
0x2, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0xF8, 0x0, 0x2, 0x0,
0x5, 0x0, 0x0, 0x0, 0x3B, 0x0, 0x4, 0x0, 0x8, 0x0,
0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0xB, 0x0, 0x0, 0x0, 0xE, 0x0,
0x0, 0x0, 0xD, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0,
0xF, 0x0, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x11, 0x0,
0x0, 0x0, 0x57, 0x0, 0x5, 0x0, 0x13, 0x0, 0x0, 0x0,
0x14, 0x0, 0x0, 0x0, 0xE, 0x0, 0x0, 0x0, 0x12, 0x0,
0x0, 0x0, 0x51, 0x0, 0x5, 0x0, 0x6, 0x0, 0x0, 0x0,
0x17, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x18, 0x0, 0x0, 0x0,
0x19, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x16, 0x0,
0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x19, 0x0, 0x0, 0x0,
0x17, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0xB, 0x0,
0x0, 0x0, 0x1B, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0xF, 0x0, 0x0, 0x0, 0x1C, 0x0,
0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x57, 0x0, 0x5, 0x0,
0x13, 0x0, 0x0, 0x0, 0x1D, 0x0, 0x0, 0x0, 0x1B, 0x0,
0x0, 0x0, 0x1C, 0x0, 0x0, 0x0, 0x51, 0x0, 0x5, 0x0,
0x6, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x1D, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x41, 0x0, 0x5, 0x0,
0x18, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x9, 0x0,
0x0, 0x0, 0x1F, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x3, 0x0,
0x20, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0x3D, 0x0,
0x4, 0x0, 0xB, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0,
0x21, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0xF, 0x0,
0x0, 0x0, 0x23, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0,
0x57, 0x0, 0x5, 0x0, 0x13, 0x0, 0x0, 0x0, 0x24, 0x0,
0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0,
0x51, 0x0, 0x5, 0x0, 0x6, 0x0, 0x0, 0x0, 0x25, 0x0,
0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x41, 0x0, 0x5, 0x0, 0x18, 0x0, 0x0, 0x0, 0x27, 0x0,
0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x26, 0x0, 0x0, 0x0,
0x3E, 0x0, 0x3, 0x0, 0x27, 0x0, 0x0, 0x0, 0x25, 0x0,
0x0, 0x0, 0x3D, 0x0, 0x4, 0x0, 0x7, 0x0, 0x0, 0x0,
0x2B, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x81, 0x0,
0x5, 0x0, 0x7, 0x0, 0x0, 0x0, 0x2C, 0x0, 0x0, 0x0,
0x2B, 0x0, 0x0, 0x0, 0x2A, 0x0, 0x0, 0x0, 0x3E, 0x0,
0x3, 0x0, 0x9, 0x0, 0x0, 0x0, 0x2C, 0x0, 0x0, 0x0,
0x3D, 0x0, 0x4, 0x0, 0x7, 0x0, 0x0, 0x0, 0x2F, 0x0,
0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x94, 0x0, 0x5, 0x0,
0x6, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x2F, 0x0,
0x0, 0x0, 0x33, 0x0, 0x0, 0x0, 0x41, 0x0, 0x5, 0x0,
0x35, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x0, 0x2E, 0x0,
0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x3, 0x0,
0x36, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x3D, 0x0,
0x4, 0x0, 0x7, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x0,
0x9, 0x0, 0x0, 0x0, 0x94, 0x0, 0x5, 0x0, 0x6, 0x0,
0x0, 0x0, 0x3B, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x0,
0x3A, 0x0, 0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x35, 0x0,
0x0, 0x0, 0x3C, 0x0, 0x0, 0x0, 0x2E, 0x0, 0x0, 0x0,
0x1F, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x3C, 0x0,
0x0, 0x0, 0x3B, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x4, 0x0,
0x7, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x0, 0x0, 0x9, 0x0,
0x0, 0x0, 0x94, 0x0, 0x5, 0x0, 0x6, 0x0, 0x0, 0x0,
0x40, 0x0, 0x0, 0x0, 0x3D, 0x0, 0x0, 0x0, 0x3F, 0x0,
0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x35, 0x0, 0x0, 0x0,
0x41, 0x0, 0x0, 0x0, 0x2E, 0x0, 0x0, 0x0, 0x26, 0x0,
0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x41, 0x0, 0x0, 0x0,
0x40, 0x0, 0x0, 0x0, 0x41, 0x0, 0x5, 0x0, 0x35, 0x0,
0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x2E, 0x0, 0x0, 0x0,
0x43, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x3, 0x0, 0x44, 0x0,
0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0xFD, 0x0, 0x1, 0x0,
0x38, 0x0, 0x1, 0x0,
];
}

@@ -0,0 +1,31 @@
using System.Collections.Concurrent;
namespace Nerfed.Runtime.Graphics;
internal class FencePool
{
private GraphicsDevice GraphicsDevice;
private ConcurrentQueue<Fence> Fences = new ConcurrentQueue<Fence>();
public FencePool(GraphicsDevice graphicsDevice)
{
GraphicsDevice = graphicsDevice;
}
public Fence Obtain()
{
if (Fences.TryDequeue(out Fence fence))
{
return fence;
}
else
{
return new Fence(GraphicsDevice);
}
}
public void Return(Fence fence)
{
Fences.Enqueue(fence);
}
}

@@ -0,0 +1,16 @@
namespace Nerfed.Runtime.Graphics;
public enum HorizontalAlignment
{
Left,
Center,
Right
}
public enum VerticalAlignment
{
Baseline,
Top,
Middle,
Bottom
}

@@ -0,0 +1,123 @@
using System.Runtime.InteropServices;
using WellspringCS;
namespace Nerfed.Runtime.Graphics;
public unsafe class Font : GraphicsResource
{
public Texture Texture { get; }
public float PixelsPerEm { get; }
public float DistanceRange { get; }
internal IntPtr Handle { get; }
private byte* StringBytes;
private int StringBytesLength;
/// <summary>
/// Loads a TTF or OTF font from a path for use in MSDF rendering.
/// Note that there must be an msdf-atlas-gen JSON and image file alongside.
/// </summary>
/// <returns></returns>
public static Font Load(GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, string fontPath)
{
FileStream fontFileStream = new FileStream(fontPath, FileMode.Open, FileAccess.Read);
void* fontFileByteBuffer = NativeMemory.Alloc((nuint)fontFileStream.Length);
Span<byte> fontFileByteSpan = new Span<byte>(fontFileByteBuffer, (int)fontFileStream.Length);
fontFileStream.ReadExactly(fontFileByteSpan);
fontFileStream.Close();
FileStream atlasFileStream = new FileStream(Path.ChangeExtension(fontPath, ".json"), FileMode.Open, FileAccess.Read);
void* atlasFileByteBuffer = NativeMemory.Alloc((nuint)atlasFileStream.Length);
Span<byte> atlasFileByteSpan = new Span<byte>(atlasFileByteBuffer, (int)atlasFileStream.Length);
atlasFileStream.ReadExactly(atlasFileByteSpan);
atlasFileStream.Close();
IntPtr handle = Wellspring.Wellspring_CreateFont(
(IntPtr)fontFileByteBuffer,
(uint)fontFileByteSpan.Length,
(IntPtr)atlasFileByteBuffer,
(uint)atlasFileByteSpan.Length,
out float pixelsPerEm,
out float distanceRange
);
string imagePath = Path.ChangeExtension(fontPath, ".png");
ImageUtils.ImageInfoFromFile(imagePath, out uint width, out uint height, out uint sizeInBytes);
ResourceUploader uploader = new ResourceUploader(graphicsDevice);
Texture texture = uploader.CreateTexture2DFromCompressed(imagePath);
uploader.Upload();
uploader.Dispose();
NativeMemory.Free(fontFileByteBuffer);
NativeMemory.Free(atlasFileByteBuffer);
return new Font(graphicsDevice, handle, texture, pixelsPerEm, distanceRange);
}
private Font(GraphicsDevice device, IntPtr handle, Texture texture, float pixelsPerEm, float distanceRange) : base(device)
{
Handle = handle;
Texture = texture;
PixelsPerEm = pixelsPerEm;
DistanceRange = distanceRange;
StringBytesLength = 32;
StringBytes = (byte*)NativeMemory.Alloc((nuint)StringBytesLength);
}
public unsafe bool TextBounds(
string text,
int pixelSize,
HorizontalAlignment horizontalAlignment,
VerticalAlignment verticalAlignment,
out Wellspring.Rectangle rectangle
)
{
int byteCount = System.Text.Encoding.UTF8.GetByteCount(text);
if (StringBytesLength < byteCount)
{
StringBytes = (byte*)NativeMemory.Realloc(StringBytes, (nuint)byteCount);
}
fixed (char* chars = text)
{
System.Text.Encoding.UTF8.GetBytes(chars, text.Length, StringBytes, byteCount);
byte result = Wellspring.Wellspring_TextBounds(
Handle,
pixelSize,
(Wellspring.HorizontalAlignment)horizontalAlignment,
(Wellspring.VerticalAlignment)verticalAlignment,
(IntPtr)StringBytes,
(uint)byteCount,
out rectangle
);
if (result == 0)
{
Log.Warning("Could not decode string: " + text);
return false;
}
}
return true;
}
protected override void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
{
Texture.Dispose();
}
Wellspring.Wellspring_DestroyFont(Handle);
}
base.Dispose(disposing);
}
}

@@ -0,0 +1,26 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Nerfed.Runtime.Graphics;
[StructLayout(LayoutKind.Sequential)]
public struct FontVertex : IVertexType
{
public Vector3 Position;
public Vector2 TexCoord;
public Color Color;
public static VertexElementFormat[] Formats { get; } =
[
VertexElementFormat.Vector3,
VertexElementFormat.Vector2,
VertexElementFormat.Color
];
public static uint[] Offsets { get; } =
[
0,
12,
20
];
}

@@ -0,0 +1,191 @@
using System.Numerics;
using System.Runtime.InteropServices;
using WellspringCS;
namespace Nerfed.Runtime.Graphics;
public unsafe class TextBatch : GraphicsResource
{
public const int INITIAL_CHAR_COUNT = 64;
public const int INITIAL_VERTEX_COUNT = INITIAL_CHAR_COUNT * 4;
public const int INITIAL_INDEX_COUNT = INITIAL_CHAR_COUNT * 6;
public IntPtr Handle { get; }
public Buffer VertexBuffer { get; protected set; } = null;
public Buffer IndexBuffer { get; protected set; } = null;
public uint PrimitiveCount { get; protected set; }
public Font CurrentFont { get; private set; }
private readonly GraphicsDevice graphicsDevice;
private readonly int stringBytesLength;
private TransferBuffer TransferBuffer;
private byte* stringBytes;
public TextBatch(GraphicsDevice device) : base(device)
{
graphicsDevice = device;
Handle = Wellspring.Wellspring_CreateTextBatch();
stringBytesLength = 128;
stringBytes = (byte*)NativeMemory.Alloc((nuint)stringBytesLength);
VertexBuffer = Buffer.Create<FontVertex>(graphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT);
IndexBuffer = Buffer.Create<uint>(graphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT);
TransferBuffer = TransferBuffer.Create<byte>(
graphicsDevice,
TransferBufferUsage.Upload,
VertexBuffer.Size + IndexBuffer.Size
);
}
// Call this to initialize or reset the batch.
public void Start(Font font)
{
Wellspring.Wellspring_StartTextBatch(Handle, font.Handle);
CurrentFont = font;
PrimitiveCount = 0;
}
// Add text with size and color to the batch
public unsafe bool Add(
string text,
int pixelSize,
Color color,
HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment verticalAlignment = VerticalAlignment.Baseline
)
{
int byteCount = System.Text.Encoding.UTF8.GetByteCount(text);
if (stringBytesLength < byteCount)
{
stringBytes = (byte*)NativeMemory.Realloc(stringBytes, (nuint)byteCount);
}
fixed (char* chars = text)
{
System.Text.Encoding.UTF8.GetBytes(chars, text.Length, stringBytes, byteCount);
byte result = Wellspring.Wellspring_AddToTextBatch(
Handle,
pixelSize,
new Wellspring.Color { R = color.R, G = color.G, B = color.B, A = color.A },
(Wellspring.HorizontalAlignment)horizontalAlignment,
(Wellspring.VerticalAlignment)verticalAlignment,
(IntPtr)stringBytes,
(uint)byteCount
);
if (result == 0)
{
Log.Warning("Could not decode string: " + text);
return false;
}
}
return true;
}
// Call this after you have made all the Add calls you want, but before beginning a render pass.
public unsafe void UploadBufferData(CommandBuffer commandBuffer)
{
Wellspring.Wellspring_GetBufferData(
Handle,
out uint vertexCount,
out IntPtr vertexDataPointer,
out uint vertexDataLengthInBytes,
out IntPtr indexDataPointer,
out uint indexDataLengthInBytes
);
Span<byte> vertexSpan = new Span<byte>((void*)vertexDataPointer, (int)vertexDataLengthInBytes);
Span<byte> indexSpan = new Span<byte>((void*)indexDataPointer, (int)indexDataLengthInBytes);
bool newTransferBufferNeeded = false;
if (VertexBuffer.Size < vertexDataLengthInBytes)
{
VertexBuffer.Dispose();
VertexBuffer = new Buffer(graphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
newTransferBufferNeeded = true;
}
if (IndexBuffer.Size < indexDataLengthInBytes)
{
IndexBuffer.Dispose();
IndexBuffer = new Buffer(graphicsDevice, BufferUsageFlags.Index, vertexDataLengthInBytes);
newTransferBufferNeeded = true;
}
if (newTransferBufferNeeded)
{
TransferBuffer.Dispose();
TransferBuffer = new TransferBuffer(
graphicsDevice,
TransferBufferUsage.Upload,
VertexBuffer.Size + IndexBuffer.Size
);
}
if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
{
TransferBuffer.SetData(vertexSpan, true);
TransferBuffer.SetData(indexSpan, (uint)vertexSpan.Length, false);
CopyPass copyPass = commandBuffer.BeginCopyPass();
copyPass.UploadToBuffer(
new TransferBufferLocation(TransferBuffer),
new BufferRegion(VertexBuffer, 0, (uint)vertexSpan.Length),
true
);
copyPass.UploadToBuffer(
new TransferBufferLocation(TransferBuffer, (uint)vertexSpan.Length),
new BufferRegion(IndexBuffer, 0, (uint)indexSpan.Length),
true
);
commandBuffer.EndCopyPass(copyPass);
}
PrimitiveCount = vertexCount / 2;
}
// Call this AFTER binding your text pipeline!
public void Render(CommandBuffer commandBuffer, RenderPass renderPass, Matrix4x4 transformMatrix)
{
commandBuffer.PushVertexUniformData(transformMatrix);
commandBuffer.PushFragmentUniformData(CurrentFont.DistanceRange);
renderPass.BindFragmentSampler(
new TextureSamplerBinding(
CurrentFont.Texture,
graphicsDevice.LinearSampler
)
);
renderPass.BindVertexBuffer(VertexBuffer);
renderPass.BindIndexBuffer(IndexBuffer, IndexElementSize.ThirtyTwo);
renderPass.DrawIndexedPrimitives(
0,
0,
PrimitiveCount
);
}
protected override void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
{
VertexBuffer.Dispose();
IndexBuffer.Dispose();
}
NativeMemory.Free(stringBytes);
Wellspring.Wellspring_DestroyTextBatch(Handle);
}
base.Dispose(disposing);
}
}

@@ -0,0 +1,466 @@
using System.Runtime.InteropServices;
using Nerfed.Runtime.Video;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// Manages all graphics-related concerns.
/// </summary>
public class GraphicsDevice : IDisposable
{
public IntPtr Handle { get; }
public BackendFlags Backend { get; }
public bool DebugMode { get; }
// Built-in video pipeline
internal GraphicsPipeline VideoPipeline { get; }
// Built-in text shader info
public Shader TextVertexShader;
public Shader TextFragmentShader;
public VertexInputState TextVertexInputState { get; }
// Built-in samplers
public Sampler PointSampler { get; }
public Sampler LinearSampler { get; }
public bool IsDisposed { get; private set; }
internal readonly RenderPassPool RenderPassPool = new RenderPassPool();
internal readonly ComputePassPool ComputePassPool = new ComputePassPool();
internal readonly CopyPassPool CopyPassPool = new CopyPassPool();
private readonly HashSet<GCHandle> resources = new HashSet<GCHandle>();
private readonly CommandBufferPool commandBufferPool;
private readonly FencePool fencePool;
internal GraphicsDevice(BackendFlags preferredBackends)
{
if (preferredBackends == BackendFlags.Invalid)
{
throw new System.Exception("Could not set graphics backend!");
}
bool debugMode = false;
#if DEBUG
debugMode = true;
#endif
Handle = Refresh.Refresh_CreateDevice((Refresh.BackendFlags)preferredBackends, Conversions.BoolToInt(debugMode));
DebugMode = debugMode;
// TODO: check for CreateDevice fail
Backend = (BackendFlags)Refresh.Refresh_GetBackend(Handle);
IEmbeddedShaders embeddedShaders;
switch (Backend)
{
case BackendFlags.Vulkan:
embeddedShaders = new EmbeddedShadersSpirV();
break;
case BackendFlags.D3D11:
throw new NotImplementedException("D3D11 embedded shaders");
break;
case BackendFlags.Metal:
throw new NotImplementedException("Metal embedded shaders");
break;
default: throw new ArgumentOutOfRangeException();
}
Shader fullscreenVertShader;
Shader textVertShader;
Shader textFragShader;
Shader videoFragShader;
using (MemoryStream fullscreenVertStream = new MemoryStream(embeddedShaders.FullscreenVert))
{
fullscreenVertShader = new Shader(
this,
fullscreenVertStream,
"main",
new ShaderCreateInfo
{
ShaderStage = ShaderStage.Vertex,
ShaderFormat = embeddedShaders.ShaderFormat
}
);
}
using (MemoryStream videoYuv2RgbaFragStream = new MemoryStream(embeddedShaders.VideoYuv2RgbaFrag))
{
videoFragShader = new Shader(
this,
videoYuv2RgbaFragStream,
"main",
new ShaderCreateInfo
{
ShaderStage = ShaderStage.Fragment,
ShaderFormat = embeddedShaders.ShaderFormat,
SamplerCount = 3
}
);
}
using (MemoryStream textTransformVertStream = new MemoryStream(embeddedShaders.TextTransformVert))
{
textVertShader = new Shader(
this,
textTransformVertStream,
"main",
new ShaderCreateInfo
{
ShaderStage = ShaderStage.Vertex,
ShaderFormat = embeddedShaders.ShaderFormat,
UniformBufferCount = 1
}
);
}
using (MemoryStream textMsdfFragStream = new MemoryStream(embeddedShaders.TextMsdfFrag))
{
textFragShader = new Shader(
this,
textMsdfFragStream,
"main",
new ShaderCreateInfo
{
ShaderStage = ShaderStage.Fragment,
ShaderFormat = embeddedShaders.ShaderFormat,
SamplerCount = 1,
UniformBufferCount = 1
}
);
}
VideoPipeline = new GraphicsPipeline(
this,
new GraphicsPipelineCreateInfo
{
AttachmentInfo = new GraphicsPipelineAttachmentInfo(
new ColorAttachmentDescription(
TextureFormat.R8G8B8A8,
ColorAttachmentBlendState.None
)
),
DepthStencilState = DepthStencilState.Disable,
VertexShader = fullscreenVertShader,
FragmentShader = videoFragShader,
VertexInputState = VertexInputState.Empty,
RasterizerState = RasterizerState.CCW_CullNone,
PrimitiveType = PrimitiveType.TriangleList,
MultisampleState = MultisampleState.None
}
);
TextVertexShader = textVertShader;
TextFragmentShader = textFragShader;
TextVertexInputState = VertexInputState.CreateSingleBinding<FontVertex>();
PointSampler = new Sampler(this, SamplerCreateInfo.PointClamp);
LinearSampler = new Sampler(this, SamplerCreateInfo.LinearClamp);
fencePool = new FencePool(this);
commandBufferPool = new CommandBufferPool(this);
}
/// <summary>
/// Prepares a window so that frames can be presented to it.
/// </summary>
/// <param name="swapchainComposition">The desired composition of the swapchain. Ignore this unless you are using HDR or tonemapping.</param>
/// <param name="presentMode">The desired presentation mode for the window. Roughly equivalent to V-Sync.</param>
/// <returns>True if successfully claimed.</returns>
public bool ClaimWindow(
Window window,
SwapchainComposition swapchainComposition,
PresentMode presentMode
)
{
if (window.Claimed)
{
Log.Error("Window already claimed!");
return false;
}
bool success = Conversions.IntToBool(
Refresh.Refresh_ClaimWindow(
Handle,
window.Handle,
(Refresh.SwapchainComposition)swapchainComposition,
(Refresh.PresentMode)presentMode
)
);
if (success)
{
window.Claimed = true;
window.SwapchainComposition = swapchainComposition;
window.SwapchainFormat = GetSwapchainFormat(window);
if (window.SwapchainTexture == null)
{
window.SwapchainTexture = new Texture(this, window.SwapchainFormat);
}
}
return success;
}
/// <summary>
/// Unclaims a window, making it unavailable for presenting and freeing associated resources.
/// </summary>
public void UnclaimWindow(Window window)
{
if (window.Claimed)
{
Refresh.Refresh_UnclaimWindow(
Handle,
window.Handle
);
window.Claimed = false;
// The swapchain texture doesn't actually have a permanent texture reference, so we zero the handle before disposing.
window.SwapchainTexture.Handle = IntPtr.Zero;
window.SwapchainTexture.Dispose();
window.SwapchainTexture = null;
}
}
/// <summary>
/// Changes the present mode of a claimed window. Does nothing if the window is not claimed.
/// </summary>
public bool SetSwapchainParameters(
Window window,
SwapchainComposition swapchainComposition,
PresentMode presentMode
)
{
if (!window.Claimed)
{
Log.Error("Cannot set present mode on unclaimed window!");
return false;
}
bool success = Conversions.IntToBool(
Refresh.Refresh_SetSwapchainParameters(
Handle,
window.Handle,
(Refresh.SwapchainComposition)swapchainComposition,
(Refresh.PresentMode)presentMode
)
);
if (success)
{
window.SwapchainComposition = swapchainComposition;
window.SwapchainFormat = GetSwapchainFormat(window);
if (window.SwapchainTexture != null)
{
window.SwapchainTexture.Format = window.SwapchainFormat;
}
}
return success;
}
/// <summary>
/// Acquires a command buffer.
/// This is the start of your rendering process.
/// </summary>
/// <returns></returns>
public CommandBuffer AcquireCommandBuffer()
{
CommandBuffer commandBuffer = commandBufferPool.Obtain();
commandBuffer.SetHandle(Refresh.Refresh_AcquireCommandBuffer(Handle));
#if DEBUG
commandBuffer.ResetStateTracking();
#endif
return commandBuffer;
}
/// <summary>
/// Submits a command buffer to the GPU for processing.
/// </summary>
public void Submit(CommandBuffer commandBuffer)
{
#if DEBUG
if (commandBuffer.Submitted)
{
throw new System.InvalidOperationException("Command buffer already submitted!");
}
#endif
Refresh.Refresh_Submit(
commandBuffer.Handle
);
commandBufferPool.Return(commandBuffer);
#if DEBUG
commandBuffer.Submitted = true;
#endif
}
/// <summary>
/// Submits a command buffer to the GPU for processing and acquires a fence associated with the submission.
/// </summary>
/// <returns></returns>
public Fence SubmitAndAcquireFence(CommandBuffer commandBuffer)
{
IntPtr fenceHandle = Refresh.Refresh_SubmitAndAcquireFence(
commandBuffer.Handle
);
Fence fence = fencePool.Obtain();
fence.SetHandle(fenceHandle);
return fence;
}
/// <summary>
/// Wait for the graphics device to become idle.
/// </summary>
public void Wait()
{
Refresh.Refresh_Wait(Handle);
}
/// <summary>
/// Waits for the given fence to become signaled.
/// </summary>
public unsafe void WaitForFence(Fence fence)
{
IntPtr fenceHandle = fence.Handle;
Refresh.Refresh_WaitForFences(
Handle,
1,
&fenceHandle,
1
);
}
/// <summary>
/// Wait for one or more fences to become signaled.
/// </summary>
/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param>
public unsafe void WaitForFences(Span<Fence> fences, bool waitAll)
{
IntPtr* handlePtr = stackalloc nint[fences.Length];
for (int i = 0; i < fences.Length; i += 1)
{
handlePtr[i] = fences[i].Handle;
}
Refresh.Refresh_WaitForFences(
Handle,
Conversions.BoolToInt(waitAll),
handlePtr,
(uint)fences.Length
);
}
/// <summary>
/// Returns true if the fence is signaled, indicating that the associated command buffer has finished processing.
/// </summary>
/// <exception cref="InvalidOperationException">Throws if the fence query indicates that the graphics device has been lost.</exception>
public bool QueryFence(Fence fence)
{
int result = Refresh.Refresh_QueryFence(Handle, fence.Handle);
if (result < 0)
{
throw new InvalidOperationException("The graphics device has been lost.");
}
return result != 0;
}
/// <summary>
/// Release reference to an acquired fence, enabling it to be reused.
/// </summary>
public void ReleaseFence(Fence fence)
{
Refresh.Refresh_ReleaseFence(Handle, fence.Handle);
fence.Handle = IntPtr.Zero;
fencePool.Return(fence);
}
private TextureFormat GetSwapchainFormat(Window window)
{
if (!window.Claimed)
{
throw new System.ArgumentException("Cannot get swapchain format of unclaimed window!");
}
return (TextureFormat)Refresh.Refresh_GetSwapchainTextureFormat(Handle, window.Handle);
}
internal void AddResourceReference(GCHandle resourceReference)
{
lock (resources)
{
resources.Add(resourceReference);
}
}
internal void RemoveResourceReference(GCHandle resourceReference)
{
lock (resources)
{
resources.Remove(resourceReference);
}
}
private void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
{
lock (resources)
{
// Dispose video players first to avoid race condition on threaded decoding
foreach (GCHandle resource in resources)
{
if (resource.Target is VideoPlayer player)
{
player.Dispose();
}
}
// Dispose everything else
foreach (GCHandle resource in resources)
{
if (resource.Target is IDisposable disposable)
{
disposable.Dispose();
}
}
resources.Clear();
}
}
Refresh.Refresh_DestroyDevice(Handle);
IsDisposed = true;
}
}
~GraphicsDevice()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: false);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

@@ -0,0 +1,53 @@
using System;
using System.Runtime.InteropServices;
namespace Nerfed.Runtime.Graphics;
public abstract class GraphicsResource : IDisposable
{
public GraphicsDevice Device { get; }
private GCHandle SelfReference;
public bool IsDisposed { get; private set; }
protected GraphicsResource(GraphicsDevice device)
{
Device = device;
SelfReference = GCHandle.Alloc(this, GCHandleType.Weak);
Device.AddResourceReference(SelfReference);
}
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
{
Device.RemoveResourceReference(SelfReference);
SelfReference.Free();
}
IsDisposed = true;
}
}
~GraphicsResource()
{
#if DEBUG
// If you see this log message, you leaked a graphics resource without disposing it!
// We'll try to clean it up for you but you really should fix this.
Log.Warning($"A resource of type {GetType().Name} was not Disposed.");
#endif
Dispose(false);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

@@ -0,0 +1,10 @@
namespace Nerfed.Runtime.Graphics;
internal interface IEmbeddedShaders
{
ShaderFormat ShaderFormat { get; }
byte[] FullscreenVert { get; }
byte[] TextMsdfFrag { get; }
byte[] TextTransformVert { get; }
byte[] VideoYuv2RgbaFrag { get; }
}

@@ -0,0 +1,17 @@
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// Can be defined on your struct type to enable simplified vertex input state definition.
/// </summary>
public interface IVertexType
{
/// <summary>
/// An ordered list of the types in your vertex struct.
/// </summary>
static abstract VertexElementFormat[] Formats { get; }
/// <summary>
/// An ordered list of the offsets in your vertex struct.
/// </summary>
static abstract uint[] Offsets { get; }
}

@@ -0,0 +1,455 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
public static class ImageUtils
{
/// <summary>
/// Gets pointer to pixel data from compressed image byte data.
///
/// The returned pointer must be freed by calling FreePixelData.
/// </summary>
public static unsafe byte* GetPixelDataFromBytes(
Span<byte> data,
out uint width,
out uint height,
out uint sizeInBytes
) {
fixed (byte* ptr = data)
{
byte* pixelData =
Refresh.Refresh_Image_Load(
ptr,
data.Length,
out int w,
out int h,
out int len
);
width = (uint) w;
height = (uint) h;
sizeInBytes = (uint) len;
return pixelData;
}
}
/// <summary>
/// Gets pointer to pixel data from a compressed image stream.
///
/// The returned pointer must be freed by calling FreePixelData.
/// </summary>
public static unsafe byte* GetPixelDataFromStream(
Stream stream,
out uint width,
out uint height,
out uint sizeInBytes
) {
long length = stream.Length;
void* buffer = NativeMemory.Alloc((nuint) length);
Span<byte> span = new Span<byte>(buffer, (int) length);
stream.ReadExactly(span);
byte* pixelData = GetPixelDataFromBytes(span, out width, out height, out sizeInBytes);
NativeMemory.Free(buffer);
return pixelData;
}
/// <summary>
/// Gets pointer to pixel data from a compressed image file.
///
/// The returned pointer must be freed by calling FreePixelData.
/// </summary>
public static unsafe byte* GetPixelDataFromFile(
string path,
out uint width,
out uint height,
out uint sizeInBytes
) {
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
return GetPixelDataFromStream(fileStream, out width, out height, out sizeInBytes);
}
/// <summary>
/// Get metadata from compressed image bytes.
/// </summary>
public static unsafe bool ImageInfoFromBytes(
Span<byte> data,
out uint width,
out uint height,
out uint sizeInBytes
) {
fixed (byte* ptr = data)
{
int result =
Refresh.Refresh_Image_Info(
ptr,
data.Length,
out int w,
out int h,
out int len
);
width = (uint) w;
height = (uint) h;
sizeInBytes = (uint) len;
return Conversions.IntToBool(result);
}
}
/// <summary>
/// Get metadata from a compressed image stream.
/// </summary>
public static unsafe bool ImageInfoFromStream(
Stream stream,
out uint width,
out uint height,
out uint sizeInBytes
) {
long length = stream.Length;
void* buffer = NativeMemory.Alloc((nuint) length);
Span<byte> span = new Span<byte>(buffer, (int) length);
stream.ReadExactly(span);
bool result = ImageInfoFromBytes(span, out width, out height, out sizeInBytes);
NativeMemory.Free(buffer);
return result;
}
/// <summary>
/// Get metadata from a compressed image file.
/// </summary>
public static bool ImageInfoFromFile(
string path,
out uint width,
out uint height,
out uint sizeInBytes
) {
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
return ImageInfoFromStream(fileStream, out width, out height, out sizeInBytes);
}
/// <summary>
/// Frees pixel data obtained from GetPixelData methods.
/// </summary>
public unsafe static void FreePixelData(byte* pixels)
{
Refresh.Refresh_Image_Free(pixels);
}
/// <summary>
/// Saves pixel data contained in a TransferBuffer to a PNG file.
/// </summary>
public static unsafe void SavePNG(
string path,
TransferBuffer transferBuffer,
uint bufferOffsetInBytes,
int width,
int height,
bool bgra
) {
int sizeInBytes = width * height * 4;
byte* pixelsPtr = (byte*) NativeMemory.Alloc((nuint) sizeInBytes);
Span<byte> pixelsSpan = new Span<byte>(pixelsPtr, sizeInBytes);
transferBuffer.GetData(pixelsSpan, bufferOffsetInBytes);
if (bgra)
{
// if data is bgra, we have to swap the R and B channels
byte* rgbaPtr = (byte*) NativeMemory.Alloc((nuint) sizeInBytes);
Span<byte> rgbaSpan = new Span<byte>(rgbaPtr, sizeInBytes);
for (int i = 0; i < sizeInBytes; i += 4)
{
rgbaSpan[i] = pixelsSpan[i + 2];
rgbaSpan[i + 1] = pixelsSpan[i + 1];
rgbaSpan[i + 2] = pixelsSpan[i];
rgbaSpan[i + 3] = pixelsSpan[i + 3];
}
NativeMemory.Free(pixelsPtr);
pixelsPtr = rgbaPtr;
}
Refresh.Refresh_Image_SavePNG(path, pixelsPtr, width, height);
NativeMemory.Free(pixelsPtr);
}
// DDS loading extension, based on MojoDDS
// Taken from https://github.com/FNA-XNA/FNA/blob/1e49f868f595f62bc6385db45949a03186a7cd7f/src/Graphics/Texture.cs#L194
public static void ParseDDS(
BinaryReader reader,
out TextureFormat format,
out int width,
out int height,
out int levels,
out bool isCube
) {
// A whole bunch of magic numbers, yay DDS!
const uint DDS_MAGIC = 0x20534444;
const uint DDS_HEADERSIZE = 124;
const uint DDS_PIXFMTSIZE = 32;
const uint DDSD_HEIGHT = 0x2;
const uint DDSD_WIDTH = 0x4;
const uint DDSD_PITCH = 0x8;
const uint DDSD_LINEARSIZE = 0x80000;
const uint DDSD_REQ = (
DDSD_HEIGHT | DDSD_WIDTH
);
const uint DDSCAPS_MIPMAP = 0x400000;
const uint DDSCAPS_TEXTURE = 0x1000;
const uint DDSCAPS2_CUBEMAP = 0x200;
const uint DDPF_FOURCC = 0x4;
const uint DDPF_RGB = 0x40;
const uint FOURCC_DXT1 = 0x31545844;
const uint FOURCC_DXT3 = 0x33545844;
const uint FOURCC_DXT5 = 0x35545844;
const uint FOURCC_DX10 = 0x30315844;
const uint pitchAndLinear = (
DDSD_PITCH | DDSD_LINEARSIZE
);
// File should start with 'DDS '
if (reader.ReadUInt32() != DDS_MAGIC)
{
throw new NotSupportedException("Not a DDS!");
}
// Texture info
uint size = reader.ReadUInt32();
if (size != DDS_HEADERSIZE)
{
throw new NotSupportedException("Invalid DDS header!");
}
uint flags = reader.ReadUInt32();
if ((flags & DDSD_REQ) != DDSD_REQ)
{
throw new NotSupportedException("Invalid DDS flags!");
}
if ((flags & pitchAndLinear) == pitchAndLinear)
{
throw new NotSupportedException("Invalid DDS flags!");
}
height = reader.ReadInt32();
width = reader.ReadInt32();
reader.ReadUInt32(); // dwPitchOrLinearSize, unused
reader.ReadUInt32(); // dwDepth, unused
levels = reader.ReadInt32();
// "Reserved"
reader.ReadBytes(4 * 11);
// Format info
uint formatSize = reader.ReadUInt32();
if (formatSize != DDS_PIXFMTSIZE)
{
throw new NotSupportedException("Bogus PIXFMTSIZE!");
}
uint formatFlags = reader.ReadUInt32();
uint formatFourCC = reader.ReadUInt32();
uint formatRGBBitCount = reader.ReadUInt32();
uint formatRBitMask = reader.ReadUInt32();
uint formatGBitMask = reader.ReadUInt32();
uint formatBBitMask = reader.ReadUInt32();
uint formatABitMask = reader.ReadUInt32();
// dwCaps "stuff"
uint caps = reader.ReadUInt32();
if ((caps & DDSCAPS_TEXTURE) == 0)
{
throw new NotSupportedException("Not a texture!");
}
isCube = false;
uint caps2 = reader.ReadUInt32();
if (caps2 != 0)
{
if ((caps2 & DDSCAPS2_CUBEMAP) == DDSCAPS2_CUBEMAP)
{
isCube = true;
}
else
{
throw new NotSupportedException("Invalid caps2!");
}
}
reader.ReadUInt32(); // dwCaps3, unused
reader.ReadUInt32(); // dwCaps4, unused
// "Reserved"
reader.ReadUInt32();
// Mipmap sanity check
if ((caps & DDSCAPS_MIPMAP) != DDSCAPS_MIPMAP)
{
levels = 1;
}
// Determine texture format
if ((formatFlags & DDPF_FOURCC) == DDPF_FOURCC)
{
switch (formatFourCC)
{
case 0x71: // D3DFMT_A16B16G16R16F
format = TextureFormat.R16G16B16A16_SFLOAT;
break;
case 0x74: // D3DFMT_A32B32G32R32F
format = TextureFormat.R32G32B32A32_SFLOAT;
break;
case FOURCC_DXT1:
format = TextureFormat.BC1;
break;
case FOURCC_DXT3:
format = TextureFormat.BC2;
break;
case FOURCC_DXT5:
format = TextureFormat.BC3;
break;
case FOURCC_DX10:
// If the fourCC is DX10, there is an extra header with additional format information.
uint dxgiFormat = reader.ReadUInt32();
// These values are taken from the DXGI_FORMAT enum.
switch (dxgiFormat)
{
case 2:
format = TextureFormat.R32G32B32A32_SFLOAT;
break;
case 10:
format = TextureFormat.R16G16B16A16_SFLOAT;
break;
case 71:
format = TextureFormat.BC1;
break;
case 74:
format = TextureFormat.BC2;
break;
case 77:
format = TextureFormat.BC3;
break;
case 98:
format = TextureFormat.BC7;
break;
default:
throw new NotSupportedException(
"Unsupported DDS texture format"
);
}
uint resourceDimension = reader.ReadUInt32();
// These values are taken from the D3D10_RESOURCE_DIMENSION enum.
switch (resourceDimension)
{
case 0: // Unknown
case 1: // Buffer
throw new NotSupportedException(
"Unsupported DDS texture format"
);
default:
break;
}
/*
* This flag seemingly only indicates if the texture is a cube map.
* This is already determined above. Cool!
*/
uint miscFlag = reader.ReadUInt32();
/*
* Indicates the number of elements in the texture array.
* We don't support texture arrays so just throw if it's greater than 1.
*/
uint arraySize = reader.ReadUInt32();
if (arraySize > 1)
{
throw new NotSupportedException(
"Unsupported DDS texture format"
);
}
reader.ReadUInt32(); // reserved
break;
default:
throw new NotSupportedException(
"Unsupported DDS texture format"
);
}
}
else if ((formatFlags & DDPF_RGB) == DDPF_RGB)
{
if ( formatRGBBitCount != 32 ||
formatRBitMask != 0x00FF0000 ||
formatGBitMask != 0x0000FF00 ||
formatBBitMask != 0x000000FF ||
formatABitMask != 0xFF000000 )
{
throw new NotSupportedException(
"Unsupported DDS texture format"
);
}
format = TextureFormat.B8G8R8A8;
}
else
{
throw new NotSupportedException(
"Unsupported DDS texture format"
);
}
}
public static int CalculateDDSLevelSize(
int width,
int height,
TextureFormat format
) {
if (format == TextureFormat.R8G8B8A8)
{
return (((width * 32) + 7) / 8) * height;
}
else if (format == TextureFormat.R16G16B16A16_SFLOAT)
{
return (((width * 64) + 7) / 8) * height;
}
else if (format == TextureFormat.R32G32B32A32_SFLOAT)
{
return (((width * 128) + 7) / 8) * height;
}
else
{
int blockSize = 16;
if (format == TextureFormat.BC1)
{
blockSize = 8;
}
width = System.Math.Max(width, 1);
height = System.Math.Max(height, 1);
return (
((width + 3) / 4) *
((height + 3) / 4) *
blockSize
);
}
}
}

@@ -0,0 +1,161 @@
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
using System.Numerics;
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x and z components use 5 bits, and the y component uses 6 bits.
/// </summary>
public struct Alpha8 : IPackedVector<byte>, IEquatable<Alpha8>, IPackedVector
{
#region Public Properties
/// <summary>
/// Gets and sets the packed value.
/// </summary>
public byte PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private byte packedValue;
#endregion
#region Public Constructor
/// <summary>
/// Creates a new instance of Alpha8.
/// </summary>
/// <param name="alpha">The alpha component</param>
public Alpha8(float alpha)
{
packedValue = Pack(alpha);
}
#endregion
#region Public Methods
/// <summary>
/// Gets the packed vector in float format.
/// </summary>
/// <returns>The packed vector in Vector3 format</returns>
public float ToAlpha()
{
return (float) (packedValue / 255.0f);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed vector from a Vector4.
/// </summary>
/// <param name="vector">Vector containing the components.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.W);
}
/// <summary>
/// Gets the packed vector in Vector4 format.
/// </summary>
/// <returns>The packed vector in Vector4 format</returns>
Vector4 IPackedVector.ToVector4()
{
return new Vector4(
0.0f,
0.0f,
0.0f,
(float) (packedValue / 255.0f)
);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Alpha8) && Equals((Alpha8) obj);
}
/// <summary>
/// Compares another Bgra5551 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Bgra5551 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Alpha8 other)
{
return packedValue == other.packedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public static bool operator ==(Alpha8 lhs, Alpha8 rhs)
{
return lhs.packedValue == rhs.packedValue;
}
public static bool operator !=(Alpha8 lhs, Alpha8 rhs)
{
return lhs.packedValue != rhs.packedValue;
}
#endregion
#region Private Static Pack Method
private static byte Pack(float alpha)
{
return (byte) System.Math.Round(Math.Clamp(alpha, 0, 1) * 255.0f);
}
#endregion
}

@@ -0,0 +1,184 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x and z components use 5 bits, and the y component uses 6 bits.
/// </summary>
public struct Bgr565 : IPackedVector<ushort>, IEquatable<Bgr565>, IPackedVector
{
#region Public Properties
/// <summary>
/// Gets and sets the packed value.
/// </summary>
public ushort PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ushort packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Creates a new instance of Bgr565.
/// </summary>
/// <param name="x">The x component</param>
/// <param name="y">The y component</param>
/// <param name="z">The z component</param>
public Bgr565(float x, float y, float z)
{
packedValue = Pack(x, y, z);
}
/// <summary>
/// Creates a new instance of Bgr565.
/// </summary>
/// <param name="vector">
/// Vector containing the components for the packed vector.
/// </param>
public Bgr565(Vector3 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z);
}
#endregion
#region Public Methods
/// <summary>
/// Gets the packed vector in Vector3 format.
/// </summary>
/// <returns>The packed vector in Vector3 format</returns>
public Vector3 ToVector3()
{
return new Vector3(
(packedValue >> 11) / 31.0f,
((packedValue >> 5) & 0x3F) / 63.0f,
(packedValue & 0x1F) / 31.0f
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed vector from a Vector4.
/// </summary>
/// <param name="vector">Vector containing the components.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
Pack(vector.X, vector.Y, vector.Z);
}
/// <summary>
/// Gets the packed vector in Vector4 format.
/// </summary>
/// <returns>The packed vector in Vector4 format</returns>
Vector4 IPackedVector.ToVector4()
{
return new Vector4(ToVector3(), 1.0f);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Bgr565) && Equals((Bgr565) obj);
}
/// <summary>
/// Compares another Bgr565 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Bgr565 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Bgr565 other)
{
return packedValue == other.packedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public static bool operator ==(Bgr565 lhs, Bgr565 rhs)
{
return lhs.packedValue == rhs.packedValue;
}
public static bool operator !=(Bgr565 lhs, Bgr565 rhs)
{
return lhs.packedValue != rhs.packedValue;
}
#endregion
#region Private Static Pack Method
private static ushort Pack(float x, float y, float z)
{
return (ushort) (
(((ushort) System.Math.Round(Math.Clamp(x, 0, 1) * 31.0f)) << 11) |
(((ushort) System.Math.Round(Math.Clamp(y, 0, 1) * 63.0f)) << 5) |
((ushort) System.Math.Round(Math.Clamp(z, 0, 1) * 31.0f))
);
}
#endregion
}

@@ -0,0 +1,178 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing unsigned normalized values, ranging from 0 to 1, using
/// 4 bits each for x, y, z, and w.
/// </summary>
public struct Bgra4444 : IPackedVector<ushort>, IEquatable<Bgra4444>
{
#region Public Properties
/// <summary>
/// Gets and sets the packed value.
/// </summary>
public ushort PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ushort packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Creates a new instance of Bgra4444.
/// </summary>
/// <param name="x">The x component</param>
/// <param name="y">The y component</param>
/// <param name="z">The z component</param>
/// <param name="w">The w component</param>
public Bgra4444(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
/// <summary>
/// Creates a new instance of Bgra4444.
/// </summary>
/// <param name="vector">
/// Vector containing the components for the packed vector.
/// </param>
public Bgra4444(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Methods
/// <summary>
/// Gets the packed vector in Vector4 format.
/// </summary>
/// <returns>The packed vector in Vector4 format</returns>
public Vector4 ToVector4()
{
return new Vector4(
((packedValue >> 8) & 0x0F) / 15.0f,
((packedValue >> 4) & 0x0F) / 15.0f,
(packedValue & 0x0F) / 15.0f,
(packedValue >> 12) / 15.0f
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed vector from a Vector4.
/// </summary>
/// <param name="vector">Vector containing the components.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Bgra4444) && Equals((Bgra4444) obj);
}
/// <summary>
/// Compares another Bgra4444 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Bgra4444 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Bgra4444 other)
{
return packedValue == other.packedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public static bool operator ==(Bgra4444 lhs, Bgra4444 rhs)
{
return lhs.packedValue == rhs.packedValue;
}
public static bool operator !=(Bgra4444 lhs, Bgra4444 rhs)
{
return lhs.packedValue != rhs.packedValue;
}
#endregion
#region Private Static Pack Method
private static ushort Pack(float x, float y, float z, float w)
{
return (ushort) (
(((ushort) System.Math.Round(Math.Clamp(x, 0, 1) * 15.0f)) << 8) |
(((ushort) System.Math.Round(Math.Clamp(y, 0, 1) * 15.0f)) << 4) |
((ushort) System.Math.Round(Math.Clamp(z, 0, 1) * 15.0f)) |
(((ushort) System.Math.Round(Math.Clamp(w, 0, 1) * 15.0f)) << 12)
);
}
#endregion
}

@@ -0,0 +1,178 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x and z components use 5 bits, and the y component uses 6 bits.
/// </summary>
public struct Bgra5551 : IPackedVector<ushort>, IEquatable<Bgra5551>, IPackedVector
{
#region Public Properties
/// <summary>
/// Gets and sets the packed value.
/// </summary>
public ushort PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ushort packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Creates a new instance of Bgra5551.
/// </summary>
/// <param name="x">The x component</param>
/// <param name="y">The y component</param>
/// <param name="z">The z component</param>
/// <param name="w">The w component</param>
public Bgra5551(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
/// <summary>
/// Creates a new instance of Bgra5551.
/// </summary>
/// <param name="vector">
/// Vector containing the components for the packed vector.
/// </param>
public Bgra5551(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Methods
/// <summary>
/// Gets the packed vector in Vector4 format.
/// </summary>
/// <returns>The packed vector in Vector4 format</returns>
public Vector4 ToVector4()
{
return new Vector4(
((packedValue >> 10) & 0x1F) / 31.0f,
((packedValue >> 5) & 0x1F) / 31.0f,
(packedValue & 0x1F) / 31.0f,
(float) (packedValue >> 15)
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed vector from a Vector4.
/// </summary>
/// <param name="vector">Vector containing the components.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Bgra5551) && Equals((Bgra5551) obj);
}
/// <summary>
/// Compares another Bgra5551 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Bgra5551 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Bgra5551 other)
{
return packedValue == other.packedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public static bool operator ==(Bgra5551 lhs, Bgra5551 rhs)
{
return lhs.packedValue == rhs.packedValue;
}
public static bool operator !=(Bgra5551 lhs, Bgra5551 rhs)
{
return lhs.packedValue != rhs.packedValue;
}
#endregion
#region Private Static Pack Method
private static ushort Pack(float x, float y, float z, float w)
{
return (ushort) (
(((ushort) System.Math.Round(Math.Clamp(x, 0, 1) * 31.0f)) << 10) |
(((ushort) System.Math.Round(Math.Clamp(y, 0, 1) * 31.0f)) << 5) |
((ushort) System.Math.Round(Math.Clamp(z, 0, 1) * 31.0f)) |
((ushort) System.Math.Round(Math.Clamp(w, 0, 1)) << 15)
);
}
#endregion
}

@@ -0,0 +1,204 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing four 8-bit unsigned integer values, ranging from 0 to 255.
/// </summary>
public struct Byte4 : IPackedVector<uint>, IEquatable<Byte4>, IPackedVector
{
#region Public Properties
/// <summary>
/// Directly gets or sets the packed representation of the value.
/// </summary>
/// <value>The packed representation of the value.</value>
public uint PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private uint packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the Byte4 class.
/// </summary>
/// <param name="vector">
/// A vector containing the initial values for the components of the Byte4 structure.
/// </param>
public Byte4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Initializes a new instance of the Byte4 class.
/// </summary>
/// <param name="x">Initial value for the x component.</param>
/// <param name="y">Initial value for the y component.</param>
/// <param name="z">Initial value for the z component.</param>
/// <param name="w">Initial value for the w component.</param>
public Byte4(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
#endregion
#region Public Methods
/// <summary>
/// Expands the packed representation into a Vector4.
/// </summary>
/// <returns>The expanded vector.</returns>
public Vector4 ToVector4()
{
return new Vector4(
(packedValue & 0xFF),
((packedValue >> 8) & 0xFF),
((packedValue >> 16) & 0xFF),
(packedValue >> 24)
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed representation from a Vector4.
/// </summary>
/// <param name="vector">The vector to create the packed representation from.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares the current instance of a class to another instance to determine
/// whether they are different.
/// </summary>
/// <param name="a">The object to the left of the equality operator.</param>
/// <param name="b">The object to the right of the equality operator.</param>
/// <returns>True if the objects are different; false otherwise.</returns>
public static bool operator !=(Byte4 a, Byte4 b)
{
return a.packedValue != b.packedValue;
}
/// <summary>
/// Compares the current instance of a class to another instance to determine
/// whether they are the same.
/// </summary>
/// <param name="a">The object to the left of the equality operator.</param>
/// <param name="b">The object to the right of the equality operator.</param>
/// <returns>True if the objects are the same; false otherwise.</returns>
public static bool operator ==(Byte4 a, Byte4 b)
{
return a.packedValue == b.packedValue;
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a
/// specified object.
/// </summary>
/// <param name="obj">The object with which to make the comparison.</param>
/// <returns>
/// True if the current instance is equal to the specified object; false otherwise.
/// </returns>
public override bool Equals(object obj)
{
return (obj is Byte4) && Equals((Byte4) obj);
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a
/// specified object.
/// </summary>
/// <param name="other">The object with which to make the comparison.</param>
/// <returns>
/// True if the current instance is equal to the specified object; false otherwise.
/// </returns>
public bool Equals(Byte4 other)
{
return this == other;
}
/// <summary>
/// Gets the hash code for the current instance.
/// </summary>
/// <returns>Hash code for the instance.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
/// <summary>
/// Returns a string representation of the current instance.
/// </summary>
/// <returns>String that represents the object.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
#endregion
#region Private Static Pack Method
/// <summary>
/// Packs a vector into a uint.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The ulong containing the packed values.</returns>
static uint Pack(float x, float y, float z, float w)
{
return (uint) (
((uint) System.Math.Round(Math.Clamp(x, 0, 255))) |
((uint) System.Math.Round(Math.Clamp(y, 0, 255)) << 8) |
((uint) System.Math.Round(Math.Clamp(z, 0, 255)) << 16) |
((uint) System.Math.Round(Math.Clamp(w, 0, 255)) << 24)
);
}
#endregion
}

@@ -0,0 +1,113 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
public struct HalfSingle : IPackedVector<ushort>, IEquatable<HalfSingle>, IPackedVector
{
#region Public Properties
public ushort PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ushort packedValue;
#endregion
#region Public Constructors
public HalfSingle(float single)
{
packedValue = HalfTypeHelper.Convert(single);
}
#endregion
#region Public Methods
public float ToSingle()
{
return HalfTypeHelper.Convert(packedValue);
}
#endregion
#region IPackedVector Methods
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = HalfTypeHelper.Convert(vector.X);
}
Vector4 IPackedVector.ToVector4()
{
return new Vector4(ToSingle(), 0.0f, 0.0f, 1.0f);
}
#endregion
#region Public Static Operators and Override Methods
public override bool Equals(object obj)
{
return (obj is HalfSingle) && Equals((HalfSingle) obj);
}
public bool Equals(HalfSingle other)
{
return packedValue == other.packedValue;
}
public override string ToString()
{
return packedValue.ToString("X");
}
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public static bool operator ==(HalfSingle lhs, HalfSingle rhs)
{
return lhs.packedValue == rhs.packedValue;
}
public static bool operator !=(HalfSingle lhs, HalfSingle rhs)
{
return lhs.packedValue != rhs.packedValue;
}
#endregion
}

@@ -0,0 +1,138 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Runtime.InteropServices;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
internal static class HalfTypeHelper
{
#region Private Struct uif
[StructLayout(LayoutKind.Explicit)]
private struct uif
{
[FieldOffset(0)]
public float f;
[FieldOffset(0)]
public int i;
[FieldOffset(0)]
public uint u;
}
#endregion
#region Internal Static Methods
internal static ushort Convert(float f)
{
uif uif = new uif();
uif.f = f;
return Convert(uif.i);
}
internal static ushort Convert(int i)
{
int s = (i >> 16) & 0x00008000;
int e = ((i >> 23) & 0x000000ff) - (127 - 15);
int m = i & 0x007fffff;
if (e <= 0)
{
if (e < -10)
{
return (ushort) s;
}
m = m | 0x00800000;
int t = 14 - e;
int a = (1 << (t - 1)) - 1;
int b = (m >> t) & 1;
m = (m + a + b) >> t;
return (ushort) (s | m);
}
else if (e == 0xff - (127 - 15))
{
if (m == 0)
{
return (ushort) (s | 0x7c00);
}
else
{
m >>= 13;
return (ushort) (s | 0x7c00 | m | ((m == 0) ? 1 : 0));
}
}
else
{
m = m + 0x00000fff + ((m >> 13) & 1);
if ((m & 0x00800000) != 0)
{
m = 0;
e += 1;
}
if (e > 30)
{
return (ushort) (s | 0x7c00);
}
return (ushort) (s | (e << 10) | (m >> 13));
}
}
internal static float Convert(ushort value)
{
uint rst;
uint mantissa = (uint) (value & 1023);
uint exp = 0xfffffff2;
if ((value & -33792) == 0)
{
if (mantissa != 0)
{
while ((mantissa & 1024) == 0)
{
exp--;
mantissa = mantissa << 1;
}
mantissa &= 0xfffffbff;
rst = ((uint) ((((uint) value & 0x8000) << 16) | ((exp + 127) << 23))) | (mantissa << 13);
}
else
{
rst = (uint) ((value & 0x8000) << 16);
}
}
else
{
rst = (uint) (((((uint) value & 0x8000) << 16) | ((((((uint) value >> 10) & 0x1f) - 15) + 127) << 23)) | (mantissa << 13));
}
uif uif = new uif();
uif.u = rst;
return uif.f;
}
#endregion
}

@@ -0,0 +1,133 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
public struct HalfVector2 : IPackedVector<uint>, IPackedVector, IEquatable<HalfVector2>
{
#region Public Properties
public uint PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private uint packedValue;
#endregion
#region Public Constructors
public HalfVector2(float x, float y)
{
packedValue = PackHelper(x, y);
}
public HalfVector2(Vector2 vector)
{
packedValue = PackHelper(vector.X, vector.Y);
}
#endregion
#region Public Methods
public Vector2 ToVector2()
{
Vector2 vector;
vector.X = HalfTypeHelper.Convert((ushort) packedValue);
vector.Y = HalfTypeHelper.Convert((ushort) (packedValue >> 0x10));
return vector;
}
#endregion
#region IPackedVector Methods
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = PackHelper(vector.X, vector.Y);
}
Vector4 IPackedVector.ToVector4()
{
return new Vector4(ToVector2(), 0.0f, 1.0f);
}
#endregion
#region Public Static Operators and Override Methods
public override string ToString()
{
return packedValue.ToString("X");
}
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public override bool Equals(object obj)
{
return ((obj is HalfVector2) && Equals((HalfVector2) obj));
}
public bool Equals(HalfVector2 other)
{
return packedValue.Equals(other.packedValue);
}
public static bool operator ==(HalfVector2 a, HalfVector2 b)
{
return a.Equals(b);
}
public static bool operator !=(HalfVector2 a, HalfVector2 b)
{
return !a.Equals(b);
}
#endregion
#region Private Static Pack Method
private static uint PackHelper(float vectorX, float vectorY)
{
return (uint) (
HalfTypeHelper.Convert(vectorX) |
((uint) (HalfTypeHelper.Convert(vectorY) << 0x10))
);
}
#endregion
}

@@ -0,0 +1,204 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing four 16-bit floating-point values.
/// </summary>
public struct HalfVector4 : IPackedVector<ulong>, IPackedVector, IEquatable<HalfVector4>
{
#region Public Properties
/// <summary>
/// Directly gets or sets the packed representation of the value.
/// </summary>
/// <value>The packed representation of the value.</value>
public ulong PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ulong packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the HalfVector4 structure.
/// </summary>
/// <param name="x">Initial value for the x component.</param>
/// <param name="y">Initial value for the y component.</param>
/// <param name="z">Initial value for the z component.</param>
/// <param name="w">Initial value for the q component.</param>
public HalfVector4(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the HalfVector4 structure.
/// </summary>
/// <param name="vector">
/// A vector containing the initial values for the components of the HalfVector4
/// structure.
/// </param>
public HalfVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Methods
/// <summary>
/// Expands the packed representation into a Vector4.
/// </summary>
/// <returns>The expanded vector.</returns>
public Vector4 ToVector4()
{
return new Vector4(
HalfTypeHelper.Convert((ushort) packedValue),
HalfTypeHelper.Convert((ushort) (packedValue >> 0x10)),
HalfTypeHelper.Convert((ushort) (packedValue >> 0x20)),
HalfTypeHelper.Convert((ushort) (packedValue >> 0x30))
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed representation from a Vector4.
/// </summary>
/// <param name="vector">The vector to create the packed representation from.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Returns a string representation of the current instance.
/// </summary>
/// <returns>String that represents the object.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
/// <summary>
/// Gets the hash code for the current instance.
/// </summary>
/// <returns>Hash code for the instance.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a
/// specified object.
/// </summary>
/// <param name="obj">The object with which to make the comparison.</param>
/// <returns>
/// True if the current instance is equal to the specified object; false otherwise.
/// </returns>
public override bool Equals(object obj)
{
return ((obj is HalfVector4) && Equals((HalfVector4) obj));
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a
/// specified object.
/// </summary>
/// <param name="other">The object with which to make the comparison.</param>
/// <returns>
/// True if the current instance is equal to the specified object; false otherwise.
/// </returns>
public bool Equals(HalfVector4 other)
{
return packedValue.Equals(other.packedValue);
}
/// <summary>
/// Compares the current instance of a class to another instance to determine
/// whether they are the same.
/// </summary>
/// <param name="a">The object to the left of the equality operator.</param>
/// <param name="b">The object to the right of the equality operator.</param>
/// <returns>True if the objects are the same; false otherwise.</returns>
public static bool operator ==(HalfVector4 a, HalfVector4 b)
{
return a.Equals(b);
}
/// <summary>
/// Compares the current instance of a class to another instance to determine
/// whether they are different.
/// </summary>
/// <param name="a">The object to the left of the equality operator.</param>
/// <param name="b">The object to the right of the equality operator.</param>
/// <returns>True if the objects are different; false otherwise.</returns>
public static bool operator !=(HalfVector4 a, HalfVector4 b)
{
return !a.Equals(b);
}
#endregion
#region Private Static Pack Method
/// <summary>
/// Packs a vector into a ulong.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The ulong containing the packed values.</returns>
private static ulong Pack(float x, float y, float z, float w)
{
return (ulong) (
((ulong) HalfTypeHelper.Convert(x)) |
(((ulong) HalfTypeHelper.Convert(y) << 0x10)) |
(((ulong) HalfTypeHelper.Convert(z) << 0x20)) |
(((ulong) HalfTypeHelper.Convert(w) << 0x30))
);
}
#endregion
}

@@ -0,0 +1,38 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
using System.Numerics;
namespace Nerfed.Runtime.Graphics.PackedVector;
// http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.packedvector.ipackedvector.aspx
public interface IPackedVector
{
void PackFromVector4(Vector4 vector);
Vector4 ToVector4();
}
// PackedVector Generic interface
// http://msdn.microsoft.com/en-us/library/bb197661.aspx
public interface IPackedVector<TPacked> : IPackedVector
{
TPacked PackedValue
{
get;
set;
}
}

@@ -0,0 +1,141 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
public struct NormalizedByte2 : IPackedVector<ushort>, IEquatable<NormalizedByte2>
{
#region Public Properties
public ushort PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ushort packedValue;
#endregion
#region Public Constructors
public NormalizedByte2(Vector2 vector)
{
packedValue = Pack(vector.X, vector.Y);
}
public NormalizedByte2(float x, float y)
{
packedValue = Pack(x, y);
}
#endregion
#region Public Methods
public Vector2 ToVector2()
{
return new Vector2(
((sbyte) (packedValue & 0xFF)) / 127.0f,
((sbyte) ((packedValue >> 8) & 0xFF)) / 127.0f
);
}
#endregion
#region IPackedVector Methods
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y);
}
Vector4 IPackedVector.ToVector4()
{
return new Vector4(ToVector2(), 0.0f, 1.0f);
}
#endregion
#region Public Static Operators and Override Methods
public static bool operator !=(NormalizedByte2 a, NormalizedByte2 b)
{
return a.packedValue != b.packedValue;
}
public static bool operator ==(NormalizedByte2 a, NormalizedByte2 b)
{
return a.packedValue == b.packedValue;
}
public override bool Equals(object obj)
{
return (obj is NormalizedByte2) && Equals((NormalizedByte2) obj);
}
public bool Equals(NormalizedByte2 other)
{
return packedValue == other.packedValue;
}
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public override string ToString()
{
return packedValue.ToString("X");
}
#endregion
#region Private Static Pack Method
private static ushort Pack(float x, float y)
{
int byte2 = (
((ushort)
System.Math.Round(Math.Clamp(x, -1.0f, 1.0f) * 127.0f)
)
) & 0x00FF;
int byte1 = (
((ushort)
System.Math.Round(Math.Clamp(y, -1.0f, 1.0f) * 127.0f)
) << 8
) & 0xFF00;
return (ushort) (byte2 | byte1);
}
#endregion
}

@@ -0,0 +1,146 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
public struct NormalizedByte4 : IPackedVector<uint>, IEquatable<NormalizedByte4>
{
#region Public Properties
public uint PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private uint packedValue;
#endregion
#region Public Constructors
public NormalizedByte4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
public NormalizedByte4(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
#endregion
#region Public Methods
public Vector4 ToVector4()
{
return new Vector4(
((sbyte) (packedValue & 0xFF)) / 127.0f,
((sbyte) ((packedValue >> 8) & 0xFF)) / 127.0f,
((sbyte) ((packedValue >> 16) & 0xFF)) / 127.0f,
((sbyte) ((packedValue >> 24) & 0xFF)) / 127.0f
);
}
#endregion
#region IPackedVector Methods
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
public static bool operator !=(NormalizedByte4 a, NormalizedByte4 b)
{
return a.packedValue != b.packedValue;
}
public static bool operator ==(NormalizedByte4 a, NormalizedByte4 b)
{
return a.packedValue == b.packedValue;
}
public override bool Equals(object obj)
{
return (obj is NormalizedByte4) && Equals((NormalizedByte4) obj);
}
public bool Equals(NormalizedByte4 other)
{
return packedValue == other.packedValue;
}
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public override string ToString()
{
return packedValue.ToString("X");
}
#endregion
#region Private Static Pack Method
private static uint Pack(float x, float y, float z, float w)
{
uint byte4 = (
(uint) System.Math.Round(Math.Clamp(x, -1.0f, 1.0f) * 127.0f)
) & 0x000000FF;
uint byte3 = (
(
(uint) System.Math.Round(Math.Clamp(y, -1.0f, 1.0f) * 127.0f)
) << 8
) & 0x0000FF00;
uint byte2 = (
(
(uint) System.Math.Round(Math.Clamp(z, -1.0f, 1.0f) * 127.0f)
) << 16
) & 0x00FF0000;
uint byte1 = (
(
(uint) System.Math.Round(Math.Clamp(w, -1.0f, 1.0f) * 127.0f)
) << 24
) & 0xFF000000;
return byte4 | byte3 | byte2 | byte1;
}
#endregion
}

@@ -0,0 +1,150 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
public struct NormalizedShort2 : IPackedVector<uint>, IEquatable<NormalizedShort2>
{
#region Public Properties
public uint PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private uint packedValue;
#endregion
#region Public Constructors
public NormalizedShort2(Vector2 vector)
{
packedValue = Pack(vector.X, vector.Y);
}
public NormalizedShort2(float x, float y)
{
packedValue = Pack(x, y);
}
#endregion
#region Public Methods
public Vector2 ToVector2()
{
const float maxVal = 0x7FFF;
return new Vector2(
(short) (packedValue & 0xFFFF) / maxVal,
(short) (packedValue >> 0x10) / maxVal
);
}
#endregion
#region IPackedVector Methods
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y);
}
Vector4 IPackedVector.ToVector4()
{
return new Vector4(ToVector2(), 0.0f, 1.0f);
}
#endregion
#region Public Static Operators and Override Methods
public static bool operator !=(NormalizedShort2 a, NormalizedShort2 b)
{
return !a.Equals(b);
}
public static bool operator ==(NormalizedShort2 a, NormalizedShort2 b)
{
return a.Equals(b);
}
public override bool Equals(object obj)
{
return (obj is NormalizedShort2) && Equals((NormalizedShort2) obj);
}
public bool Equals(NormalizedShort2 other)
{
return packedValue.Equals(other.packedValue);
}
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public override string ToString()
{
return packedValue.ToString("X");
}
#endregion
#region Private Static Pack Method
private static uint Pack(float x, float y)
{
const float max = 0x7FFF;
const float min = -max;
uint word2 = (uint) (
(int) Math.Clamp(
(float) System.Math.Round(x * max),
min,
max
) & 0xFFFF
);
uint word1 = (uint) ((
(int) Math.Clamp(
(float) System.Math.Round(y * max),
min,
max
) & 0xFFFF
) << 0x10);
return (word2 | word1);
}
#endregion
}

@@ -0,0 +1,161 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
public struct NormalizedShort4 : IPackedVector<ulong>, IEquatable<NormalizedShort4>
{
#region Public Properties
public ulong PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ulong packedValue;
#endregion
#region Public Constructors
public NormalizedShort4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
public NormalizedShort4(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
#endregion
#region Public Methods
public Vector4 ToVector4()
{
const float maxVal = 0x7FFF;
return new Vector4(
((short) (packedValue & 0xFFFF)) / maxVal,
((short) ((packedValue >> 0x10) & 0xFFFF)) / maxVal,
((short) ((packedValue >> 0x20) & 0xFFFF)) / maxVal,
((short) ((packedValue >> 0x30) & 0xFFFF)) / maxVal
);
}
#endregion
#region IPackedVector Methods
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
public static bool operator !=(NormalizedShort4 a, NormalizedShort4 b)
{
return !a.Equals(b);
}
public static bool operator ==(NormalizedShort4 a, NormalizedShort4 b)
{
return a.Equals(b);
}
public override bool Equals(object obj)
{
return (obj is NormalizedShort4) && Equals((NormalizedShort4) obj);
}
public bool Equals(NormalizedShort4 other)
{
return packedValue.Equals(other.packedValue);
}
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public override string ToString()
{
return packedValue.ToString("X");
}
#endregion
#region Private Static Pack Method
private static ulong Pack(float x, float y, float z, float w)
{
const float max = 0x7FFF;
const float min = -max;
ulong word4 = (
(ulong) Math.Clamp(
(float) System.Math.Round(x * max),
min,
max
) & 0xFFFF
);
ulong word3 = (
(ulong) Math.Clamp(
(float) System.Math.Round(y * max),
min,
max
) & 0xFFFF
) << 0x10;
ulong word2 = (
(ulong) Math.Clamp(
(float) System.Math.Round(z * max),
min,
max
) & 0xFFFF
) << 0x20;
ulong word1 = (
(ulong) Math.Clamp(
(float) System.Math.Round(w * max),
min,
max
) & 0xFFFF
) << 0x30;
return (word4 | word3 | word2 | word1);
}
#endregion
}

@@ -0,0 +1,182 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x and z components use 5 bits, and the y component uses 6 bits.
/// </summary>
public struct Rg32 : IPackedVector<uint>, IEquatable<Rg32>, IPackedVector
{
#region Public Properties
/// <summary>
/// Gets and sets the packed value.
/// </summary>
public uint PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private uint packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Creates a new instance of Rg32.
/// </summary>
/// <param name="x">The x component</param>
/// <param name="y">The y component</param>
public Rg32(float x, float y)
{
packedValue = Pack(x, y);
}
/// <summary>
/// Creates a new instance of Rg32.
/// </summary>
/// <param name="vector">
/// Vector containing the components for the packed vector.
/// </param>
public Rg32(Vector2 vector)
{
packedValue = Pack(vector.X, vector.Y);
}
#endregion
#region Public Methods
/// <summary>
/// Gets the packed vector in Vector2 format.
/// </summary>
/// <returns>The packed vector in Vector2 format</returns>
public Vector2 ToVector2()
{
return new Vector2(
(packedValue & 0xFFFF) / 65535.0f,
(packedValue >> 16) / 65535.0f
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed vector from a Vector4.
/// </summary>
/// <param name="vector">Vector containing the components.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y);
}
/// <summary>
/// Gets the packed vector in Vector4 format.
/// </summary>
/// <returns>The packed vector in Vector4 format</returns>
Vector4 IPackedVector.ToVector4()
{
return new Vector4(ToVector2(), 0.0f, 1.0f);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Rg32) && Equals((Rg32) obj);
}
/// <summary>
/// Compares another Rg32 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Rg32 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Rg32 other)
{
return packedValue == other.packedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public static bool operator ==(Rg32 lhs, Rg32 rhs)
{
return lhs.packedValue == rhs.packedValue;
}
public static bool operator !=(Rg32 lhs, Rg32 rhs)
{
return lhs.packedValue != rhs.packedValue;
}
#endregion
#region Private Static Pack Method
private static uint Pack(float x, float y)
{
return (uint) (
((uint) System.Math.Round(Math.Clamp(x, 0, 1) * 65535.0f)) |
(((uint) System.Math.Round(Math.Clamp(y, 0, 1) * 65535.0f)) << 16)
);
}
#endregion
}

@@ -0,0 +1,178 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x and z components use 5 bits, and the y component uses 6 bits.
/// </summary>
public struct Rgba1010102 : IPackedVector<uint>, IEquatable<Rgba1010102>, IPackedVector
{
#region Public Properties
/// <summary>
/// Gets and sets the packed value.
/// </summary>
public uint PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private uint packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Creates a new instance of Rgba1010102.
/// </summary>
/// <param name="x">The x component</param>
/// <param name="y">The y component</param>
/// <param name="z">The z component</param>
/// <param name="w">The w component</param>
public Rgba1010102(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
/// <summary>
/// Creates a new instance of Rgba1010102.
/// </summary>
/// <param name="vector">
/// Vector containing the components for the packed vector.
/// </param>
public Rgba1010102(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Methods
/// <summary>
/// Gets the packed vector in Vector4 format.
/// </summary>
/// <returns>The packed vector in Vector4 format</returns>
public Vector4 ToVector4()
{
return new Vector4(
(packedValue & 0x03FF) / 1023.0f,
((packedValue >> 10) & 0x03FF) / 1023.0f,
((packedValue >> 20) & 0x03FF) / 1023.0f,
(packedValue >> 30) / 3.0f
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed vector from a Vector4.
/// </summary>
/// <param name="vector">Vector containing the components.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Rgba1010102) && Equals((Rgba1010102) obj);
}
/// <summary>
/// Compares another Rgba1010102 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Rgba1010102 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Rgba1010102 other)
{
return packedValue == other.packedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public static bool operator ==(Rgba1010102 lhs, Rgba1010102 rhs)
{
return lhs.packedValue == rhs.packedValue;
}
public static bool operator !=(Rgba1010102 lhs, Rgba1010102 rhs)
{
return lhs.packedValue != rhs.packedValue;
}
#endregion
#region Private Static Pack Method
private static uint Pack(float x, float y, float z, float w)
{
return (uint) (
((uint) System.Math.Round(Math.Clamp(x, 0, 1) * 1023.0f)) |
((uint) System.Math.Round(Math.Clamp(y, 0, 1) * 1023.0f) << 10) |
((uint) System.Math.Round(Math.Clamp(z, 0, 1) * 1023.0f) << 20) |
((uint) System.Math.Round(Math.Clamp(w, 0, 1) * 3.0f) << 30)
);
}
#endregion
}

@@ -0,0 +1,178 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x and z components use 5 bits, and the y component uses 6 bits.
/// </summary>
public struct Rgba64 : IPackedVector<ulong>, IEquatable<Rgba64>, IPackedVector
{
#region Public Properties
/// <summary>
/// Gets and sets the packed value.
/// </summary>
public ulong PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ulong packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Creates a new instance of Rgba64.
/// </summary>
/// <param name="x">The x component</param>
/// <param name="y">The y component</param>
/// <param name="z">The z component</param>
/// <param name="w">The w component</param>
public Rgba64(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
/// <summary>
/// Creates a new instance of Rgba64.
/// </summary>
/// <param name="vector">
/// Vector containing the components for the packed vector.
/// </param>
public Rgba64(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Methods
/// <summary>
/// Gets the packed vector in Vector4 format.
/// </summary>
/// <returns>The packed vector in Vector4 format</returns>
public Vector4 ToVector4()
{
return new Vector4(
(packedValue & 0xFFFF) / 65535.0f,
((packedValue >> 16) & 0xFFFF) / 65535.0f,
((packedValue >> 32) & 0xFFFF) / 65535.0f,
(packedValue >> 48) / 65535.0f
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed vector from a Vector4.
/// </summary>
/// <param name="vector">Vector containing the components.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Rgba64) && Equals((Rgba64) obj);
}
/// <summary>
/// Compares another Rgba64 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Rgba64 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Rgba64 other)
{
return packedValue == other.packedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public static bool operator ==(Rgba64 lhs, Rgba64 rhs)
{
return lhs.packedValue == rhs.packedValue;
}
public static bool operator !=(Rgba64 lhs, Rgba64 rhs)
{
return lhs.packedValue != rhs.packedValue;
}
#endregion
#region Private Static Pack Method
private static ulong Pack(float x, float y, float z, float w)
{
return (ulong) (
((ulong) System.Math.Round(Math.Clamp(x, 0, 1) * 65535.0f)) |
(((ulong) System.Math.Round(Math.Clamp(y, 0, 1) * 65535.0f)) << 16) |
(((ulong) System.Math.Round(Math.Clamp(z, 0, 1) * 65535.0f)) << 32) |
(((ulong) System.Math.Round(Math.Clamp(w, 0, 1) * 65535.0f)) << 48)
);
}
#endregion
}

@@ -0,0 +1,134 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
public struct Short2 : IPackedVector<uint>, IEquatable<Short2>
{
#region Public Properties
public uint PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private uint packedValue;
#endregion
#region Public Constructors
public Short2(Vector2 vector)
{
packedValue = Pack(vector.X, vector.Y);
}
public Short2(float x, float y)
{
packedValue = Pack(x, y);
}
#endregion
#region Public Methods
public Vector2 ToVector2()
{
return new Vector2(
(short) (packedValue & 0xFFFF),
(short) (packedValue >> 16)
);
}
#endregion
#region IPackedVector Methods
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y);
}
Vector4 IPackedVector.ToVector4()
{
return new Vector4(ToVector2(), 0.0f, 1.0f);
}
#endregion
#region Public Static Operators and Override Methods
public static bool operator !=(Short2 a, Short2 b)
{
return a.packedValue != b.packedValue;
}
public static bool operator ==(Short2 a, Short2 b)
{
return a.packedValue == b.packedValue;
}
public override bool Equals(object obj)
{
return (obj is Short2) && Equals((Short2) obj);
}
public bool Equals(Short2 other)
{
return this == other;
}
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
public override string ToString()
{
return packedValue.ToString("X");
}
#endregion
#region Private Static Pack Method
private static uint Pack(float x, float y)
{
return (uint) (
((int) System.Math.Round(Math.Clamp(x, -32768, 32767)) & 0x0000FFFF) |
(((int) System.Math.Round(Math.Clamp(y, -32768, 32767))) << 16)
);
}
#endregion
}

@@ -0,0 +1,204 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Numerics;
#endregion
namespace Nerfed.Runtime.Graphics.PackedVector;
/// <summary>
/// Packed vector type containing four 16-bit signed integer values.
/// </summary>
public struct Short4 : IPackedVector<ulong>, IEquatable<Short4>
{
#region Public Properties
/// <summary>
/// Directly gets or sets the packed representation of the value.
/// </summary>
/// <value>The packed representation of the value.</value>
public ulong PackedValue
{
get
{
return packedValue;
}
set
{
packedValue = value;
}
}
#endregion
#region Private Variables
private ulong packedValue;
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the Short4 class.
/// </summary>
/// <param name="vector">
/// A vector containing the initial values for the components of the Short4 structure.
/// </param>
public Short4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Initializes a new instance of the Short4 class.
/// </summary>
/// <param name="x">Initial value for the x component.</param>
/// <param name="y">Initial value for the y component.</param>
/// <param name="z">Initial value for the z component.</param>
/// <param name="w">Initial value for the w component.</param>
public Short4(float x, float y, float z, float w)
{
packedValue = Pack(x, y, z, w);
}
#endregion
#region Public Methods
/// <summary>
/// Expands the packed representation into a Vector4.
/// </summary>
/// <returns>The expanded vector.</returns>
public Vector4 ToVector4()
{
return new Vector4(
(short) (packedValue & 0xFFFF),
(short) ((packedValue >> 16) & 0xFFFF),
(short) ((packedValue >> 32) & 0xFFFF),
(short) (packedValue >> 48)
);
}
#endregion
#region IPackedVector Methods
/// <summary>
/// Sets the packed representation from a Vector4.
/// </summary>
/// <param name="vector">The vector to create the packed representation from.</param>
void IPackedVector.PackFromVector4(Vector4 vector)
{
packedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares the current instance of a class to another instance to determine
/// whether they are different.
/// </summary>
/// <param name="a">The object to the left of the equality operator.</param>
/// <param name="b">The object to the right of the equality operator.</param>
/// <returns>True if the objects are different; false otherwise.</returns>
public static bool operator !=(Short4 a, Short4 b)
{
return a.PackedValue != b.PackedValue;
}
/// <summary>
/// Compares the current instance of a class to another instance to determine
/// whether they are the same.
/// </summary>
/// <param name="a">The object to the left of the equality operator.</param>
/// <param name="b">The object to the right of the equality operator.</param>
/// <returns>True if the objects are the same; false otherwise.</returns>
public static bool operator ==(Short4 a, Short4 b)
{
return a.PackedValue == b.PackedValue;
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a
/// specified object.
/// </summary>
/// <param name="obj">The object with which to make the comparison.</param>
/// <returns>
/// True if the current instance is equal to the specified object; false otherwise.
/// </returns>
public override bool Equals(object obj)
{
return (obj is Short4) && Equals((Short4) obj);
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a
/// specified object.
/// </summary>
/// <param name="other">The object with which to make the comparison.</param>
/// <returns>
/// True if the current instance is equal to the specified object; false otherwise.
/// </returns>
public bool Equals(Short4 other)
{
return this == other;
}
/// <summary>
/// Gets the hash code for the current instance.
/// </summary>
/// <returns>Hash code for the instance.</returns>
public override int GetHashCode()
{
return packedValue.GetHashCode();
}
/// <summary>
/// Returns a string representation of the current instance.
/// </summary>
/// <returns>String that represents the object.</returns>
public override string ToString()
{
return packedValue.ToString("X");
}
#endregion
#region Private Static Pack Method
/// <summary>
/// Packs a vector into a ulong.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The ulong containing the packed values.</returns>
static ulong Pack(float x, float y, float z, float w)
{
return (ulong) (
((long) System.Math.Round(Math.Clamp(x, -32768, 32767)) & 0xFFFF) |
(((long) System.Math.Round(Math.Clamp(y, -32768, 32767)) << 16) & 0xFFFF0000) |
(((long) System.Math.Round(Math.Clamp(z, -32768, 32767)) << 32) & 0xFFFF00000000) |
((long) System.Math.Round(Math.Clamp(w, -32768, 32767)) << 48)
);
}
#endregion
}

@@ -0,0 +1,30 @@
using System;
using System.Threading;
namespace Nerfed.Runtime.Graphics;
public abstract class RefreshResource : GraphicsResource
{
public IntPtr Handle { get => handle; internal set => handle = value; }
private IntPtr handle;
protected abstract Action<IntPtr, IntPtr> ReleaseFunction { get; }
protected RefreshResource(GraphicsDevice device) : base(device)
{
}
protected override void Dispose(bool disposing)
{
if (!IsDisposed)
{
// Atomically call release function in case this is called from the finalizer thread
IntPtr toDispose = Interlocked.Exchange(ref handle, IntPtr.Zero);
if (toDispose != IntPtr.Zero)
{
ReleaseFunction(Device.Handle, toDispose);
}
}
base.Dispose(disposing);
}
}

File diff suppressed because it is too large Load Diff

@@ -0,0 +1,495 @@
using System;
using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// Render passes are begun in command buffers and are used to apply render state and issue draw calls.
/// Render passes are pooled and should not be referenced after calling EndRenderPass.
/// </summary>
public class RenderPass
{
public nint Handle { get; private set; }
#if DEBUG
internal uint colorAttachmentCount;
internal SampleCount colorAttachmentSampleCount;
internal TextureFormat colorFormatOne;
internal TextureFormat colorFormatTwo;
internal TextureFormat colorFormatThree;
internal TextureFormat colorFormatFour;
internal bool hasDepthStencilAttachment;
internal SampleCount depthStencilAttachmentSampleCount;
internal TextureFormat depthStencilFormat;
GraphicsPipeline currentGraphicsPipeline;
#endif
internal void SetHandle(nint handle)
{
Handle = handle;
}
/// <summary>
/// Binds a graphics pipeline so that rendering work may be performed.
/// </summary>
/// <param name="graphicsPipeline">The graphics pipeline to bind.</param>
public void BindGraphicsPipeline(GraphicsPipeline graphicsPipeline)
{
#if DEBUG
AssertRenderPassPipelineFormatMatch(graphicsPipeline);
if (colorAttachmentCount > 0)
{
if (graphicsPipeline.SampleCount != colorAttachmentSampleCount)
{
throw new System.InvalidOperationException(
$"Sample count mismatch! Graphics pipeline sample count: {graphicsPipeline.SampleCount}, Color attachment sample count: {colorAttachmentSampleCount}"
);
}
}
if (hasDepthStencilAttachment)
{
if (graphicsPipeline.SampleCount != depthStencilAttachmentSampleCount)
{
throw new System.InvalidOperationException(
$"Sample count mismatch! Graphics pipeline sample count: {graphicsPipeline.SampleCount}, Depth stencil attachment sample count: {depthStencilAttachmentSampleCount}"
);
}
}
#endif
Refresh.Refresh_BindGraphicsPipeline(
Handle,
graphicsPipeline.Handle
);
#if DEBUG
currentGraphicsPipeline = graphicsPipeline;
#endif
}
/// <summary>
/// Sets the viewport.
/// </summary>
public void SetViewport(in Viewport viewport)
{
Refresh.Refresh_SetViewport(
Handle,
viewport.ToRefresh()
);
}
/// <summary>
/// Sets the scissor area.
/// </summary>
public void SetScissor(in Rect scissor)
{
#if DEBUG
if (scissor.X < 0 || scissor.Y < 0 || scissor.W <= 0 || scissor.H <= 0)
{
throw new System.ArgumentOutOfRangeException("Scissor position cannot be negative and dimensions must be positive!");
}
#endif
Refresh.Refresh_SetScissor(
Handle,
scissor.ToRefresh()
);
}
/// <summary>
/// Binds vertex buffers to be used by subsequent draw calls.
/// </summary>
/// <param name="bufferBinding">Buffer to bind and associated offset.</param>
/// <param name="firstBinding">The index of the first vertex input binding whose state is updated by the command.</param>
public unsafe void BindVertexBuffer(
in BufferBinding bufferBinding,
uint firstBinding = 0
)
{
#if DEBUG
AssertGraphicsPipelineBound();
#endif
Refresh.BufferBinding refreshBufferBinding = bufferBinding.ToRefresh();
Refresh.Refresh_BindVertexBuffers(
Handle,
firstBinding,
&refreshBufferBinding,
1
);
}
/// <summary>
/// Binds an index buffer to be used by subsequent draw calls.
/// </summary>
/// <param name="indexBuffer">The index buffer to bind.</param>
/// <param name="indexElementSize">The size in bytes of the index buffer elements.</param>
/// <param name="offset">The offset index for the buffer.</param>
public void BindIndexBuffer(
BufferBinding bufferBinding,
IndexElementSize indexElementSize
)
{
#if DEBUG
AssertGraphicsPipelineBound();
#endif
Refresh.Refresh_BindIndexBuffer(
Handle,
bufferBinding.ToRefresh(),
(Refresh.IndexElementSize)indexElementSize
);
}
/// <summary>
/// Binds samplers to be used by the vertex shader.
/// </summary>
/// <param name="textureSamplerBindings">The texture-sampler to bind.</param>
public unsafe void BindVertexSampler(
in TextureSamplerBinding textureSamplerBinding,
uint slot = 0
)
{
#if DEBUG
AssertGraphicsPipelineBound();
AssertTextureSamplerBindingNonNull(textureSamplerBinding);
AssertTextureHasSamplerFlag(textureSamplerBinding.Texture);
#endif
Refresh.TextureSamplerBinding refreshTextureSamplerBinding = textureSamplerBinding.ToRefresh();
Refresh.Refresh_BindVertexSamplers(
Handle,
slot,
&refreshTextureSamplerBinding,
1
);
}
public unsafe void BindVertexStorageTexture(
in TextureSlice textureSlice,
uint slot = 0
)
{
#if DEBUG
AssertGraphicsPipelineBound();
AssertTextureNonNull(textureSlice.Texture);
AssertTextureHasGraphicsStorageFlag(textureSlice.Texture);
#endif
Refresh.TextureSlice refreshTextureSlice = textureSlice.ToRefresh();
Refresh.Refresh_BindVertexStorageTextures(
Handle,
slot,
&refreshTextureSlice,
1
);
}
public unsafe void BindVertexStorageBuffer(
Buffer buffer,
uint slot = 0
)
{
#if DEBUG
AssertGraphicsPipelineBound();
AssertBufferNonNull(buffer);
AssertBufferHasGraphicsStorageFlag(buffer);
#endif
IntPtr bufferHandle = buffer.Handle;
Refresh.Refresh_BindVertexStorageBuffers(
Handle,
slot,
&bufferHandle,
1
);
}
public unsafe void BindFragmentSampler(
in TextureSamplerBinding textureSamplerBinding,
uint slot = 0
)
{
#if DEBUG
AssertGraphicsPipelineBound();
AssertTextureSamplerBindingNonNull(textureSamplerBinding);
AssertTextureHasSamplerFlag(textureSamplerBinding.Texture);
#endif
Refresh.TextureSamplerBinding refreshTextureSamplerBinding = textureSamplerBinding.ToRefresh();
Refresh.Refresh_BindFragmentSamplers(
Handle,
slot,
&refreshTextureSamplerBinding,
1
);
}
public unsafe void BindFragmentStorageTexture(
in TextureSlice textureSlice,
uint slot = 0
)
{
#if DEBUG
AssertGraphicsPipelineBound();
AssertTextureNonNull(textureSlice.Texture);
AssertTextureHasGraphicsStorageFlag(textureSlice.Texture);
#endif
Refresh.TextureSlice refreshTextureSlice = textureSlice.ToRefresh();
Refresh.Refresh_BindFragmentStorageTextures(
Handle,
slot,
&refreshTextureSlice,
1
);
}
public unsafe void BindFragmentStorageBuffer(
Buffer buffer,
uint slot = 0
)
{
#if DEBUG
AssertGraphicsPipelineBound();
AssertBufferNonNull(buffer);
AssertBufferHasGraphicsStorageFlag(buffer);
#endif
IntPtr bufferHandle = buffer.Handle;
Refresh.Refresh_BindFragmentStorageBuffers(
Handle,
slot,
&bufferHandle,
1
);
}
/// <summary>
/// Draws using a vertex buffer and an index buffer, and an optional instance count.
/// </summary>
/// <param name="baseVertex">The starting index offset for the vertex buffer.</param>
/// <param name="startIndex">The starting index offset for the index buffer.</param>
/// <param name="primitiveCount">The number of primitives to draw.</param>
/// <param name="instanceCount">The number of instances to draw.</param>
public void DrawIndexedPrimitives(
uint baseVertex,
uint startIndex,
uint primitiveCount,
uint instanceCount = 1
)
{
#if DEBUG
AssertGraphicsPipelineBound();
#endif
Refresh.Refresh_DrawIndexedPrimitives(
Handle,
baseVertex,
startIndex,
primitiveCount,
instanceCount
);
}
/// <summary>
/// Draws using a vertex buffer and an index buffer.
/// </summary>
/// <param name="baseVertex">The starting index offset for the vertex buffer.</param>
/// <param name="startIndex">The starting index offset for the index buffer.</param>
/// <param name="primitiveCount">The number of primitives to draw.</param>
public void DrawPrimitives(
uint vertexStart,
uint primitiveCount
)
{
#if DEBUG
AssertGraphicsPipelineBound();
#endif
Refresh.Refresh_DrawPrimitives(
Handle,
vertexStart,
primitiveCount
);
}
/// <summary>
/// Similar to DrawPrimitives, but parameters are set from a buffer.
/// The buffer must have the Indirect usage flag set.
/// </summary>
/// <param name="buffer">The draw parameters buffer.</param>
/// <param name="offsetInBytes">The offset to start reading from the draw parameters buffer.</param>
/// <param name="drawCount">The number of draw parameter sets that should be read from the buffer.</param>
/// <param name="stride">The byte stride between sets of draw parameters.</param>
public void DrawPrimitivesIndirect(
Buffer buffer,
uint offsetInBytes,
uint drawCount,
uint stride
)
{
#if DEBUG
AssertGraphicsPipelineBound();
#endif
Refresh.Refresh_DrawPrimitivesIndirect(
Handle,
buffer.Handle,
offsetInBytes,
drawCount,
stride
);
}
/// <summary>
/// Similar to DrawIndexedPrimitives, but parameters are set from a buffer.
/// The buffer must have the Indirect usage flag set.
/// </summary>
/// <param name="buffer">The draw parameters buffer.</param>
/// <param name="offsetInBytes">The offset to start reading from the draw parameters buffer.</param>
/// <param name="drawCount">The number of draw parameter sets that should be read from the buffer.</param>
/// <param name="stride">The byte stride between sets of draw parameters.</param>
public void DrawIndexedPrimitivesIndirect(
Buffer buffer,
uint offsetInBytes,
uint drawCount,
uint stride
)
{
#if DEBUG
AssertGraphicsPipelineBound();
#endif
Refresh.Refresh_DrawIndexedPrimitivesIndirect(
Handle,
buffer.Handle,
offsetInBytes,
drawCount,
stride
);
}
#if DEBUG
private void AssertRenderPassPipelineFormatMatch(GraphicsPipeline graphicsPipeline)
{
for (int i = 0; i < graphicsPipeline.AttachmentInfo.ColorAttachmentDescriptions.Length; i += 1)
{
TextureFormat format;
if (i == 0)
{
format = colorFormatOne;
}
else if (i == 1)
{
format = colorFormatTwo;
}
else if (i == 2)
{
format = colorFormatThree;
}
else
{
format = colorFormatFour;
}
TextureFormat pipelineFormat = graphicsPipeline.AttachmentInfo.ColorAttachmentDescriptions[i].Format;
if (pipelineFormat != format)
{
throw new System.InvalidOperationException(
$"Color texture format mismatch! Pipeline expects {pipelineFormat}, render pass attachment is {format}"
);
}
}
if (graphicsPipeline.AttachmentInfo.HasDepthStencilAttachment)
{
TextureFormat pipelineDepthFormat = graphicsPipeline.AttachmentInfo.DepthStencilFormat;
if (!hasDepthStencilAttachment)
{
throw new System.InvalidOperationException("Pipeline expects depth attachment!");
}
if (pipelineDepthFormat != depthStencilFormat)
{
throw new System.InvalidOperationException(
$"Depth texture format mismatch! Pipeline expects {pipelineDepthFormat}, render pass attachment is {depthStencilFormat}"
);
}
}
}
private void AssertGraphicsPipelineBound(string message = "No graphics pipeline is bound!")
{
if (currentGraphicsPipeline == null)
{
throw new System.InvalidOperationException(message);
}
}
private void AssertTextureNonNull(in TextureSlice textureSlice)
{
if (textureSlice.Texture == null)
{
throw new NullReferenceException("Texture must not be null!");
}
}
private void AssertTextureSamplerBindingNonNull(in TextureSamplerBinding binding)
{
if (binding.Texture == null || binding.Texture.Handle == IntPtr.Zero)
{
throw new NullReferenceException("Texture binding must not be null!");
}
if (binding.Sampler == null || binding.Sampler.Handle == IntPtr.Zero)
{
throw new NullReferenceException("Sampler binding must not be null!");
}
}
private void AssertTextureHasSamplerFlag(Texture texture)
{
if ((texture.UsageFlags & TextureUsageFlags.Sampler) == 0)
{
throw new System.ArgumentException("The bound Texture's UsageFlags must include TextureUsageFlags.Sampler!");
}
}
private void AssertTextureHasGraphicsStorageFlag(Texture texture)
{
if ((texture.UsageFlags & TextureUsageFlags.GraphicsStorage) == 0)
{
throw new System.ArgumentException("The bound Texture's UsageFlags must include TextureUsageFlags.GraphicsStorage!");
}
}
private void AssertBufferNonNull(Buffer buffer)
{
if (buffer == null || buffer.Handle == IntPtr.Zero)
{
throw new System.NullReferenceException("Buffer must not be null!");
}
}
private void AssertBufferHasGraphicsStorageFlag(Buffer buffer)
{
if ((buffer.UsageFlags & BufferUsageFlags.GraphicsStorage) == 0)
{
throw new System.ArgumentException("The bound Buffer's UsageFlags must include BufferUsageFlag.GraphicsStorage!");
}
}
#endif
}

@@ -0,0 +1,25 @@
using System.Collections.Concurrent;
namespace Nerfed.Runtime.Graphics;
internal class RenderPassPool
{
private ConcurrentQueue<RenderPass> RenderPasses = new ConcurrentQueue<RenderPass>();
public RenderPass Obtain()
{
if (RenderPasses.TryDequeue(out RenderPass renderPass))
{
return renderPass;
}
else
{
return new RenderPass();
}
}
public void Return(RenderPass renderPass)
{
RenderPasses.Enqueue(renderPass);
}
}

@@ -0,0 +1,376 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
namespace Nerfed.Runtime.Graphics;
// FIXME: can we map the transfer buffer instead of maintaining a local pointer
/// <summary>
/// A convenience structure for creating resources and uploading data to them.
///
/// Note that Upload or UploadAndWait must be called after the Create methods for the data to actually be uploaded.
///
/// Note that this structure does not magically keep memory usage down -
/// you may want to stagger uploads over multiple submissions to minimize memory usage.
/// </summary>
public unsafe class ResourceUploader : GraphicsResource
{
TransferBuffer BufferTransferBuffer;
TransferBuffer TextureTransferBuffer;
byte* bufferData;
uint bufferDataOffset = 0;
uint bufferDataSize = 1024;
byte* textureData;
uint textureDataOffset = 0;
uint textureDataSize = 1024;
List<(uint, BufferRegion, bool)> BufferUploads = new List<(uint, BufferRegion, bool)>();
List<(uint, TextureRegion, bool)> TextureUploads = new List<(uint, TextureRegion, bool)>();
public ResourceUploader(GraphicsDevice device) : base(device)
{
bufferData = (byte*) NativeMemory.Alloc(bufferDataSize);
textureData = (byte*) NativeMemory.Alloc(textureDataSize);
}
// Buffers
/// <summary>
/// Creates a Buffer with data to be uploaded.
/// </summary>
public Buffer CreateBuffer<T>(Span<T> data, BufferUsageFlags usageFlags) where T : unmanaged
{
uint lengthInBytes = (uint) (Marshal.SizeOf<T>() * data.Length);
Buffer buffer = new Buffer(Device, usageFlags, lengthInBytes);
SetBufferData(buffer, 0, data, false);
return buffer;
}
/// <summary>
/// Prepares upload of data into a Buffer.
/// </summary>
public void SetBufferData<T>(Buffer buffer, uint bufferOffsetInElements, Span<T> data, bool cycle) where T : unmanaged
{
uint elementSize = (uint) Marshal.SizeOf<T>();
uint offsetInBytes = elementSize * bufferOffsetInElements;
uint lengthInBytes = (uint) (elementSize * data.Length);
uint resourceOffset;
fixed (void* spanPtr = data)
{
resourceOffset = CopyBufferData(spanPtr, lengthInBytes);
}
BufferRegion bufferRegion = new BufferRegion(buffer, offsetInBytes, lengthInBytes);
BufferUploads.Add((resourceOffset, bufferRegion, cycle));
}
// Textures
public Texture CreateTexture2D<T>(Span<T> pixelData, uint width, uint height) where T : unmanaged
{
Texture texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler);
SetTextureData(texture, pixelData, false);
return texture;
}
/// <summary>
/// Creates a 2D Texture from compressed image data to be uploaded.
/// </summary>
public Texture CreateTexture2DFromCompressed(Span<byte> compressedImageData)
{
ImageUtils.ImageInfoFromBytes(compressedImageData, out uint width, out uint height, out uint _);
Texture texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler);
SetTextureDataFromCompressed(texture, compressedImageData);
return texture;
}
/// <summary>
/// Creates a 2D Texture from a compressed image stream to be uploaded.
/// </summary>
public Texture CreateTexture2DFromCompressed(Stream compressedImageStream)
{
long length = compressedImageStream.Length;
void* buffer = NativeMemory.Alloc((nuint) length);
Span<byte> span = new Span<byte>(buffer, (int) length);
compressedImageStream.ReadExactly(span);
Texture texture = CreateTexture2DFromCompressed(span);
NativeMemory.Free(buffer);
return texture;
}
/// <summary>
/// Creates a 2D Texture from a compressed image file to be uploaded.
/// </summary>
public Texture CreateTexture2DFromCompressed(string compressedImageFilePath)
{
FileStream fileStream = new FileStream(compressedImageFilePath, FileMode.Open, FileAccess.Read);
return CreateTexture2DFromCompressed(fileStream);
}
/// <summary>
/// Creates a texture from a DDS stream.
/// </summary>
public Texture CreateTextureFromDDS(Stream stream)
{
using BinaryReader reader = new BinaryReader(stream);
Texture texture;
int faces;
ImageUtils.ParseDDS(reader, out TextureFormat format, out int width, out int height, out int levels, out bool isCube);
if (isCube)
{
texture = Texture.CreateTextureCube(Device, (uint) width, format, TextureUsageFlags.Sampler, (uint) levels);
faces = 6;
}
else
{
texture = Texture.CreateTexture2D(Device, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, (uint) levels);
faces = 1;
}
for (int face = 0; face < faces; face += 1)
{
for (int level = 0; level < levels; level += 1)
{
int levelWidth = width >> level;
int levelHeight = height >> level;
int levelSize = ImageUtils.CalculateDDSLevelSize(levelWidth, levelHeight, format);
void* byteBuffer = NativeMemory.Alloc((nuint) levelSize);
Span<byte> byteSpan = new Span<byte>(byteBuffer, levelSize);
stream.ReadExactly(byteSpan);
TextureRegion textureRegion = new TextureRegion
{
TextureSlice = new TextureSlice
{
Texture = texture,
Layer = (uint) face,
MipLevel = (uint) level
},
X = 0,
Y = 0,
Z = 0,
Width = (uint) levelWidth,
Height = (uint) levelHeight,
Depth = 1
};
SetTextureData(textureRegion, byteSpan, false);
NativeMemory.Free(byteBuffer);
}
}
return texture;
}
/// <summary>
/// Creates a texture from a DDS file.
/// </summary>
public Texture CreateTextureFromDDS(string path)
{
FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
return CreateTextureFromDDS(stream);
}
public void SetTextureDataFromCompressed(TextureRegion textureRegion, Span<byte> compressedImageData)
{
byte* pixelData = ImageUtils.GetPixelDataFromBytes(compressedImageData, out uint _, out uint _, out uint sizeInBytes);
Span<byte> pixelSpan = new Span<byte>((void*) pixelData, (int) sizeInBytes);
SetTextureData(textureRegion, pixelSpan, false);
ImageUtils.FreePixelData(pixelData);
}
public void SetTextureDataFromCompressed(TextureRegion textureRegion, Stream compressedImageStream)
{
long length = compressedImageStream.Length;
void* buffer = NativeMemory.Alloc((nuint) length);
Span<byte> span = new Span<byte>(buffer, (int) length);
compressedImageStream.ReadExactly(span);
SetTextureDataFromCompressed(textureRegion, span);
NativeMemory.Free(buffer);
}
public void SetTextureDataFromCompressed(TextureRegion textureRegion, string compressedImageFilePath)
{
FileStream fileStream = new FileStream(compressedImageFilePath, FileMode.Open, FileAccess.Read);
SetTextureDataFromCompressed(textureRegion, fileStream);
}
/// <summary>
/// Prepares upload of pixel data into a TextureSlice.
/// </summary>
public void SetTextureData<T>(TextureRegion textureRegion, Span<T> data, bool cycle) where T : unmanaged
{
int elementSize = Marshal.SizeOf<T>();
uint dataLengthInBytes = (uint) (elementSize * data.Length);
uint resourceOffset;
fixed (T* dataPtr = data)
{
resourceOffset = CopyTextureData(dataPtr, dataLengthInBytes, Texture.BytesPerPixel(textureRegion.TextureSlice.Texture.Format));
}
TextureUploads.Add((resourceOffset, textureRegion, cycle));
}
// Upload
/// <summary>
/// Uploads all the data corresponding to the created resources.
/// </summary>
public void Upload()
{
CopyToTransferBuffer();
CommandBuffer commandBuffer = Device.AcquireCommandBuffer();
RecordUploadCommands(commandBuffer);
Device.Submit(commandBuffer);
}
/// <summary>
/// Uploads and then blocks until the upload is finished.
/// This is useful for keeping memory usage down during threaded upload.
/// </summary>
public void UploadAndWait()
{
CopyToTransferBuffer();
CommandBuffer commandBuffer = Device.AcquireCommandBuffer();
RecordUploadCommands(commandBuffer);
Fence fence = Device.SubmitAndAcquireFence(commandBuffer);
Device.WaitForFence(fence);
Device.ReleaseFence(fence);
}
// Helper methods
private void CopyToTransferBuffer()
{
if (BufferUploads.Count > 0)
{
if (BufferTransferBuffer == null || BufferTransferBuffer.Size < bufferDataSize)
{
BufferTransferBuffer?.Dispose();
BufferTransferBuffer = new TransferBuffer(Device, TransferBufferUsage.Upload, bufferDataSize);
}
Span<byte> dataSpan = new Span<byte>(bufferData, (int) bufferDataSize);
BufferTransferBuffer.SetData(dataSpan, true);
}
if (TextureUploads.Count > 0)
{
if (TextureTransferBuffer == null || TextureTransferBuffer.Size < textureDataSize)
{
TextureTransferBuffer?.Dispose();
TextureTransferBuffer = new TransferBuffer(Device, TransferBufferUsage.Upload, textureDataSize);
}
Span<byte> dataSpan = new Span<byte>(textureData, (int) textureDataSize);
TextureTransferBuffer.SetData(dataSpan, true);
}
}
private void RecordUploadCommands(CommandBuffer commandBuffer)
{
CopyPass copyPass = commandBuffer.BeginCopyPass();
foreach ((uint transferOffset, BufferRegion bufferRegion, bool option) in BufferUploads)
{
copyPass.UploadToBuffer(
new TransferBufferLocation(BufferTransferBuffer, transferOffset),
bufferRegion,
option
);
}
foreach ((uint transferOffset, TextureRegion textureRegion, bool option) in TextureUploads)
{
copyPass.UploadToTexture(
new TextureTransferInfo(TextureTransferBuffer, transferOffset),
textureRegion,
option
);
}
commandBuffer.EndCopyPass(copyPass);
BufferUploads.Clear();
TextureUploads.Clear();
bufferDataOffset = 0;
}
private uint CopyBufferData(void* ptr, uint lengthInBytes)
{
if (bufferDataOffset + lengthInBytes >= bufferDataSize)
{
bufferDataSize = bufferDataOffset + lengthInBytes;
bufferData = (byte*) NativeMemory.Realloc(bufferData, bufferDataSize);
}
uint resourceOffset = bufferDataOffset;
NativeMemory.Copy(ptr, bufferData + bufferDataOffset, lengthInBytes);
bufferDataOffset += lengthInBytes;
return resourceOffset;
}
private uint CopyTextureData(void* ptr, uint lengthInBytes, uint alignment)
{
textureDataOffset = RoundToAlignment(textureDataOffset, alignment);
if (textureDataOffset + lengthInBytes >= textureDataSize)
{
textureDataSize = textureDataOffset + lengthInBytes;
textureData = (byte*) NativeMemory.Realloc(textureData, textureDataSize);
}
uint resourceOffset = textureDataOffset;
NativeMemory.Copy(ptr, textureData + textureDataOffset, lengthInBytes);
textureDataOffset += lengthInBytes;
return resourceOffset;
}
private uint RoundToAlignment(uint value, uint alignment)
{
return alignment * ((value + alignment - 1) / alignment);
}
// Dispose
/// <summary>
/// It is valid to immediately call Dispose after calling Upload.
/// </summary>
protected override void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
{
BufferTransferBuffer?.Dispose();
TextureTransferBuffer?.Dispose();
}
NativeMemory.Free(bufferData);
}
base.Dispose(disposing);
}
}

@@ -0,0 +1,88 @@
using System;
using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// A data container that can be efficiently used by the GPU.
/// </summary>
public class Buffer : RefreshResource
{
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseBuffer;
public BufferUsageFlags UsageFlags { get; }
/// <summary>
/// Size in bytes.
/// </summary>
public uint Size { get; }
private string name;
public string Name
{
get => name;
set
{
if (Device.DebugMode)
{
Refresh.Refresh_SetBufferName(
Device.Handle,
Handle,
value
);
}
name = value;
}
}
/// <summary>
/// Creates a buffer of appropriate size given a type and element count.
/// </summary>
/// <typeparam name="T">The type that the buffer will contain.</typeparam>
/// <param name="device">The GraphicsDevice.</param>
/// <param name="usageFlags">Specifies how the buffer will be used.</param>
/// <param name="elementCount">How many elements of type T the buffer will contain.</param>
/// <returns></returns>
public unsafe static Buffer Create<T>(
GraphicsDevice device,
BufferUsageFlags usageFlags,
uint elementCount
) where T : unmanaged
{
return new Buffer(
device,
usageFlags,
(uint) Marshal.SizeOf<T>() * elementCount
);
}
/// <summary>
/// Creates a buffer.
/// </summary>
/// <param name="device">An initialized GraphicsDevice.</param>
/// <param name="usageFlags">Specifies how the buffer will be used.</param>
/// <param name="sizeInBytes">The length of the array. Cannot be resized.</param>
public Buffer(
GraphicsDevice device,
BufferUsageFlags usageFlags,
uint sizeInBytes
) : base(device)
{
Handle = Refresh.Refresh_CreateBuffer(
device.Handle,
(Refresh.BufferUsageFlags) usageFlags,
sizeInBytes
);
UsageFlags = usageFlags;
Size = sizeInBytes;
name = "";
}
public static implicit operator BufferBinding(Buffer b)
{
return new BufferBinding(b, 0);
}
}

@@ -0,0 +1,91 @@
using RefreshCS;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// Compute pipelines perform arbitrary parallel processing on input data.
/// </summary>
public class ComputePipeline : RefreshResource
{
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseComputePipeline;
public uint ReadOnlyStorageTextureCount { get; }
public uint ReadOnlyStorageBufferCount { get; }
public uint ReadWriteStorageTextureCount { get; }
public uint ReadWriteStorageBufferCount { get; }
public uint UniformBufferCount { get; }
public ComputePipeline(
GraphicsDevice device,
string filePath,
string entryPointName,
in ComputePipelineCreateInfo computePipelineCreateInfo
) : base(device)
{
using FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
Handle = CreateFromStream(device, stream, entryPointName, computePipelineCreateInfo);
ReadOnlyStorageTextureCount = computePipelineCreateInfo.ReadOnlyStorageTextureCount;
ReadOnlyStorageBufferCount = computePipelineCreateInfo.ReadOnlyStorageBufferCount;
ReadWriteStorageTextureCount = computePipelineCreateInfo.ReadWriteStorageTextureCount;
ReadWriteStorageBufferCount = computePipelineCreateInfo.ReadWriteStorageBufferCount;
UniformBufferCount = computePipelineCreateInfo.UniformBufferCount;
}
public ComputePipeline(
GraphicsDevice device,
Stream stream,
string entryPointName,
in ComputePipelineCreateInfo computePipelineCreateInfo
) : base(device)
{
Handle = CreateFromStream(device, stream, entryPointName, computePipelineCreateInfo);
ReadOnlyStorageTextureCount = computePipelineCreateInfo.ReadOnlyStorageTextureCount;
ReadOnlyStorageBufferCount = computePipelineCreateInfo.ReadOnlyStorageBufferCount;
ReadWriteStorageTextureCount = computePipelineCreateInfo.ReadWriteStorageTextureCount;
ReadWriteStorageBufferCount = computePipelineCreateInfo.ReadWriteStorageBufferCount;
UniformBufferCount = computePipelineCreateInfo.UniformBufferCount;
}
private static unsafe nint CreateFromStream(
GraphicsDevice device,
Stream stream,
string entryPointName,
in ComputePipelineCreateInfo computePipelineCreateInfo
) {
byte* bytecodeBuffer = (byte*) NativeMemory.Alloc((nuint) stream.Length);
Span<byte> bytecodeSpan = new Span<byte>(bytecodeBuffer, (int) stream.Length);
stream.ReadExactly(bytecodeSpan);
Refresh.ComputePipelineCreateInfo refreshPipelineCreateInfo;
refreshPipelineCreateInfo.Code = bytecodeBuffer;
refreshPipelineCreateInfo.CodeSize = (nuint) stream.Length;
refreshPipelineCreateInfo.EntryPointName = entryPointName;
refreshPipelineCreateInfo.Format = (Refresh.ShaderFormat) computePipelineCreateInfo.ShaderFormat;
refreshPipelineCreateInfo.ReadOnlyStorageTextureCount = computePipelineCreateInfo.ReadOnlyStorageTextureCount;
refreshPipelineCreateInfo.ReadOnlyStorageBufferCount = computePipelineCreateInfo.ReadOnlyStorageBufferCount;
refreshPipelineCreateInfo.ReadWriteStorageTextureCount = computePipelineCreateInfo.ReadWriteStorageTextureCount;
refreshPipelineCreateInfo.ReadWriteStorageBufferCount = computePipelineCreateInfo.ReadWriteStorageBufferCount;
refreshPipelineCreateInfo.UniformBufferCount = computePipelineCreateInfo.UniformBufferCount;
refreshPipelineCreateInfo.ThreadCountX = computePipelineCreateInfo.ThreadCountX;
refreshPipelineCreateInfo.ThreadCountY = computePipelineCreateInfo.ThreadCountY;
refreshPipelineCreateInfo.ThreadCountZ = computePipelineCreateInfo.ThreadCountZ;
IntPtr computePipelineHandle = Refresh.Refresh_CreateComputePipeline(
device.Handle,
refreshPipelineCreateInfo
);
if (computePipelineHandle == nint.Zero)
{
throw new Exception("Could not create compute pipeline!");
}
NativeMemory.Free(bytecodeBuffer);
return computePipelineHandle;
}
}

@@ -0,0 +1,25 @@
using System;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// Fences allow you to track the status of a submitted command buffer. <br/>
/// You should only acquire a Fence if you will need to track the command buffer. <br/>
/// You should make sure to call GraphicsDevice.ReleaseFence when done with a Fence to avoid memory growth. <br/>
/// The Fence object itself is basically just a wrapper for the Refresh_Fence. <br/>
/// The internal handle is replaced so that we can pool Fence objects to manage garbage.
/// </summary>
public class Fence : RefreshResource
{
protected override Action<nint, nint> ReleaseFunction => Refresh.Refresh_ReleaseFence;
internal Fence(GraphicsDevice device) : base(device)
{
}
internal void SetHandle(nint handle)
{
Handle = handle;
}
}

@@ -0,0 +1,99 @@
using System;
using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// Graphics pipelines encapsulate all of the render state in a single object. <br/>
/// These pipelines are bound before draw calls are issued.
/// </summary>
public class GraphicsPipeline : RefreshResource
{
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseGraphicsPipeline;
public Shader VertexShader;
public Shader FragmentShader;
public SampleCount SampleCount { get; }
#if DEBUG
internal GraphicsPipelineAttachmentInfo AttachmentInfo { get; }
#endif
public unsafe GraphicsPipeline(
GraphicsDevice device,
in GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
) : base(device)
{
Refresh.GraphicsPipelineCreateInfo refreshGraphicsPipelineCreateInfo;
Refresh.VertexAttribute* vertexAttributes = (Refresh.VertexAttribute*) NativeMemory.Alloc(
(nuint) (graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length * Marshal.SizeOf<Refresh.VertexAttribute>())
);
for (int i = 0; i < graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length; i += 1)
{
vertexAttributes[i] = graphicsPipelineCreateInfo.VertexInputState.VertexAttributes[i].ToRefresh();
}
Refresh.VertexBinding* vertexBindings = (Refresh.VertexBinding*) NativeMemory.Alloc(
(nuint) (graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length * Marshal.SizeOf<Refresh.VertexBinding>())
);
for (int i = 0; i < graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length; i += 1)
{
vertexBindings[i] = graphicsPipelineCreateInfo.VertexInputState.VertexBindings[i].ToRefresh();
}
Refresh.ColorAttachmentDescription* colorAttachmentDescriptions = stackalloc Refresh.ColorAttachmentDescription[
graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length
];
for (int i = 0; i < graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length; i += 1)
{
colorAttachmentDescriptions[i].Format = (Refresh.TextureFormat) graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions[i].Format;
colorAttachmentDescriptions[i].BlendState = graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions[i].BlendState.ToRefresh();
}
refreshGraphicsPipelineCreateInfo.VertexShader = graphicsPipelineCreateInfo.VertexShader.Handle;
refreshGraphicsPipelineCreateInfo.FragmentShader = graphicsPipelineCreateInfo.FragmentShader.Handle;
refreshGraphicsPipelineCreateInfo.VertexInputState.VertexAttributes = vertexAttributes;
refreshGraphicsPipelineCreateInfo.VertexInputState.VertexAttributeCount = (uint) graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length;
refreshGraphicsPipelineCreateInfo.VertexInputState.VertexBindings = vertexBindings;
refreshGraphicsPipelineCreateInfo.VertexInputState.VertexBindingCount = (uint) graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length;
refreshGraphicsPipelineCreateInfo.PrimitiveType = (Refresh.PrimitiveType) graphicsPipelineCreateInfo.PrimitiveType;
refreshGraphicsPipelineCreateInfo.RasterizerState = graphicsPipelineCreateInfo.RasterizerState.ToRefresh();
refreshGraphicsPipelineCreateInfo.MultisampleState = graphicsPipelineCreateInfo.MultisampleState.ToRefresh();
refreshGraphicsPipelineCreateInfo.DepthStencilState = graphicsPipelineCreateInfo.DepthStencilState.ToRefresh();
refreshGraphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentCount = (uint) graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length;
refreshGraphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions = colorAttachmentDescriptions;
refreshGraphicsPipelineCreateInfo.AttachmentInfo.DepthStencilFormat = (Refresh.TextureFormat) graphicsPipelineCreateInfo.AttachmentInfo.DepthStencilFormat;
refreshGraphicsPipelineCreateInfo.AttachmentInfo.HasDepthStencilAttachment = Conversions.BoolToInt(graphicsPipelineCreateInfo.AttachmentInfo.HasDepthStencilAttachment);
refreshGraphicsPipelineCreateInfo.BlendConstants[0] = graphicsPipelineCreateInfo.BlendConstants.R;
refreshGraphicsPipelineCreateInfo.BlendConstants[1] = graphicsPipelineCreateInfo.BlendConstants.G;
refreshGraphicsPipelineCreateInfo.BlendConstants[2] = graphicsPipelineCreateInfo.BlendConstants.B;
refreshGraphicsPipelineCreateInfo.BlendConstants[3] = graphicsPipelineCreateInfo.BlendConstants.A;
Handle = Refresh.Refresh_CreateGraphicsPipeline(device.Handle, refreshGraphicsPipelineCreateInfo);
if (Handle == IntPtr.Zero)
{
throw new Exception("Could not create graphics pipeline!");
}
NativeMemory.Free(vertexAttributes);
NativeMemory.Free(vertexBindings);
VertexShader = graphicsPipelineCreateInfo.VertexShader;
FragmentShader = graphicsPipelineCreateInfo.FragmentShader;
SampleCount = graphicsPipelineCreateInfo.MultisampleState.MultisampleCount;
#if DEBUG
AttachmentInfo = graphicsPipelineCreateInfo.AttachmentInfo;
#endif
}
}

@@ -0,0 +1,23 @@
using System;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// Specifies how a texture will be sampled in a shader.
/// </summary>
public class Sampler : RefreshResource
{
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseSampler;
public Sampler(
GraphicsDevice device,
in SamplerCreateInfo samplerCreateInfo
) : base(device)
{
Handle = Refresh.Refresh_CreateSampler(
device.Handle,
samplerCreateInfo.ToRefresh()
);
}
}

@@ -0,0 +1,91 @@
using RefreshCS;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// Shaders are used to create graphics pipelines.
/// Graphics pipelines take a vertex shader and a fragment shader.
/// </summary>
public class Shader : RefreshResource
{
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseShader;
public uint SamplerCount { get; }
public uint StorageTextureCount { get; }
public uint StorageBufferCount { get; }
public uint UniformBufferCount { get; }
public unsafe Shader(
GraphicsDevice device,
string filePath,
string entryPointName,
in ShaderCreateInfo shaderCreateInfo
) : base(device)
{
using FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
Handle = CreateFromStream(
device,
stream,
entryPointName,
shaderCreateInfo
);
SamplerCount = shaderCreateInfo.SamplerCount;
StorageTextureCount = shaderCreateInfo.StorageTextureCount;
StorageBufferCount = shaderCreateInfo.StorageBufferCount;
UniformBufferCount = shaderCreateInfo.UniformBufferCount;
}
public unsafe Shader(
GraphicsDevice device,
Stream stream,
string entryPointName,
in ShaderCreateInfo shaderCreateInfo
) : base(device)
{
Handle = CreateFromStream(
device,
stream,
entryPointName,
shaderCreateInfo
);
SamplerCount = shaderCreateInfo.SamplerCount;
StorageTextureCount = shaderCreateInfo.StorageTextureCount;
StorageBufferCount = shaderCreateInfo.StorageBufferCount;
UniformBufferCount = shaderCreateInfo.UniformBufferCount;
}
private static unsafe IntPtr CreateFromStream(
GraphicsDevice device,
Stream stream,
string entryPointName,
in ShaderCreateInfo shaderCreateInfo
) {
void* bytecodeBuffer = NativeMemory.Alloc((nuint) stream.Length);
Span<byte> bytecodeSpan = new Span<byte>(bytecodeBuffer, (int) stream.Length);
stream.ReadExactly(bytecodeSpan);
Refresh.ShaderCreateInfo refreshShaderCreateInfo;
refreshShaderCreateInfo.CodeSize = (nuint) stream.Length;
refreshShaderCreateInfo.Code = (byte*) bytecodeBuffer;
refreshShaderCreateInfo.EntryPointName = entryPointName;
refreshShaderCreateInfo.Stage = (Refresh.ShaderStage) shaderCreateInfo.ShaderStage;
refreshShaderCreateInfo.Format = (Refresh.ShaderFormat) shaderCreateInfo.ShaderFormat;
refreshShaderCreateInfo.SamplerCount = shaderCreateInfo.SamplerCount;
refreshShaderCreateInfo.StorageTextureCount = shaderCreateInfo.StorageTextureCount;
refreshShaderCreateInfo.StorageBufferCount = shaderCreateInfo.StorageBufferCount;
refreshShaderCreateInfo.UniformBufferCount = shaderCreateInfo.UniformBufferCount;
IntPtr shaderModule = Refresh.Refresh_CreateShader(
device.Handle,
refreshShaderCreateInfo
);
NativeMemory.Free(bytecodeBuffer);
return shaderModule;
}
}

@@ -0,0 +1,326 @@
using System;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// A multi-dimensional data container that can be efficiently used by the GPU.
/// </summary>
public class Texture : RefreshResource
{
public uint Width { get; internal set; }
public uint Height { get; internal set; }
public uint Depth { get; }
public TextureFormat Format { get; internal set; }
public bool IsCube { get; }
public uint LayerCount { get; }
public uint LevelCount { get; }
public SampleCount SampleCount { get; }
public TextureUsageFlags UsageFlags { get; }
public uint Size { get; }
private string name;
public string Name
{
get => name;
set
{
if (Device.DebugMode)
{
Refresh.Refresh_SetTextureName(
Device.Handle,
Handle,
value
);
}
name = value;
}
}
// FIXME: this allocates a delegate instance
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseTexture;
/// <summary>
/// Creates a 2D texture.
/// </summary>
/// <param name="device">An initialized GraphicsDevice.</param>
/// <param name="width">The width of the texture.</param>
/// <param name="height">The height of the texture.</param>
/// <param name="format">The format of the texture.</param>
/// <param name="usageFlags">Specifies how the texture will be used.</param>
/// <param name="levelCount">Specifies the number of mip levels.</param>
public static Texture CreateTexture2D(
GraphicsDevice device,
uint width,
uint height,
TextureFormat format,
TextureUsageFlags usageFlags,
uint levelCount = 1,
SampleCount sampleCount = SampleCount.One
) {
TextureCreateInfo textureCreateInfo = new TextureCreateInfo
{
Width = width,
Height = height,
Depth = 1,
IsCube = false,
LayerCount = 1,
LevelCount = levelCount,
SampleCount = sampleCount,
Format = format,
UsageFlags = usageFlags
};
return new Texture(device, textureCreateInfo);
}
/// <summary>
/// Creates a 2D texture array.
/// </summary>
/// <param name="device">An initialized GraphicsDevice.</param>
/// <param name="width">The width of the texture.</param>
/// <param name="height">The height of the texture.</param>
/// <param name="layerCount">The layer count of the texture.</param>
/// <param name="format">The format of the texture.</param>
/// <param name="usageFlags">Specifies how the texture will be used.</param>
/// <param name="levelCount">Specifies the number of mip levels.</param>
public static Texture CreateTexture2DArray(
GraphicsDevice device,
uint width,
uint height,
uint layerCount,
TextureFormat format,
TextureUsageFlags usageFlags,
uint levelCount = 1
) {
TextureCreateInfo textureCreateInfo = new TextureCreateInfo
{
Width = width,
Height = height,
Depth = 1,
IsCube = false,
LayerCount = layerCount,
LevelCount = levelCount,
Format = format,
UsageFlags = usageFlags
};
return new Texture(device, textureCreateInfo);
}
/// <summary>
/// Creates a 3D texture.
/// Note that the width, height and depth all form one slice and cannot be subdivided in a texture slice.
/// </summary>
public static Texture CreateTexture3D(
GraphicsDevice device,
uint width,
uint height,
uint depth,
TextureFormat format,
TextureUsageFlags usageFlags,
uint levelCount = 1
) {
TextureCreateInfo textureCreateInfo = new TextureCreateInfo
{
Width = width,
Height = height,
Depth = depth,
IsCube = false,
LayerCount = 1,
LevelCount = levelCount,
Format = format,
UsageFlags = usageFlags
};
return new Texture(device, textureCreateInfo);
}
/// <summary>
/// Creates a cube texture.
/// </summary>
/// <param name="device">An initialized GraphicsDevice.</param>
/// <param name="size">The length of one side of the cube.</param>
/// <param name="format">The format of the texture.</param>
/// <param name="usageFlags">Specifies how the texture will be used.</param>
/// <param name="levelCount">Specifies the number of mip levels.</param>
public static Texture CreateTextureCube(
GraphicsDevice device,
uint size,
TextureFormat format,
TextureUsageFlags usageFlags,
uint levelCount = 1
) {
TextureCreateInfo textureCreateInfo = new TextureCreateInfo
{
Width = size,
Height = size,
Depth = 1,
IsCube = true,
LayerCount = 6,
LevelCount = levelCount,
Format = format,
UsageFlags = usageFlags
};
return new Texture(device, textureCreateInfo);
}
/// <summary>
/// Creates a new texture using a TextureCreateInfo struct.
/// </summary>
/// <param name="device">An initialized GraphicsDevice.</param>
/// <param name="textureCreateInfo">The parameters to use when creating the texture.</param>
public Texture(
GraphicsDevice device,
in TextureCreateInfo textureCreateInfo
) : base(device)
{
Handle = Refresh.Refresh_CreateTexture(
device.Handle,
textureCreateInfo.ToRefresh()
);
Format = textureCreateInfo.Format;
Width = textureCreateInfo.Width;
Height = textureCreateInfo.Height;
Depth = textureCreateInfo.Depth;
IsCube = textureCreateInfo.IsCube;
LayerCount = textureCreateInfo.LayerCount;
LevelCount = textureCreateInfo.LevelCount;
SampleCount = textureCreateInfo.SampleCount;
UsageFlags = textureCreateInfo.UsageFlags;
Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format);
name = "";
}
// Used by Window. Swapchain texture handles are managed by the driver backend.
internal Texture(
GraphicsDevice device,
TextureFormat format
) : base(device)
{
Handle = IntPtr.Zero;
Format = format;
Width = 0;
Height = 0;
Depth = 1;
IsCube = false;
LevelCount = 1;
SampleCount = SampleCount.One;
UsageFlags = TextureUsageFlags.ColorTarget;
Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format);
}
public static uint BytesPerPixel(TextureFormat format)
{
switch (format)
{
case TextureFormat.R8:
case TextureFormat.R8_UINT:
return 1;
case TextureFormat.R5G6B5:
case TextureFormat.B4G4R4A4:
case TextureFormat.A1R5G5B5:
case TextureFormat.R16_SFLOAT:
case TextureFormat.R8G8_SNORM:
case TextureFormat.R8G8_UINT:
case TextureFormat.R16_UINT:
case TextureFormat.D16_UNORM:
return 2;
case TextureFormat.R8G8B8A8:
case TextureFormat.B8G8R8A8:
case TextureFormat.R32_SFLOAT:
case TextureFormat.R16G16:
case TextureFormat.R16G16_SFLOAT:
case TextureFormat.R8G8B8A8_SNORM:
case TextureFormat.A2R10G10B10:
case TextureFormat.R8G8B8A8_UINT:
case TextureFormat.R16G16_UINT:
case TextureFormat.D24_UNORM_S8_UINT:
case TextureFormat.D32_SFLOAT:
return 4;
case TextureFormat.D32_SFLOAT_S8_UINT:
return 5;
case TextureFormat.R16G16B16A16_SFLOAT:
case TextureFormat.R16G16B16A16:
case TextureFormat.R32G32_SFLOAT:
case TextureFormat.R16G16B16A16_UINT:
case TextureFormat.BC1:
return 8;
case TextureFormat.R32G32B32A32_SFLOAT:
case TextureFormat.BC2:
case TextureFormat.BC3:
case TextureFormat.BC7:
return 16;
default:
Log.Error("Texture format not recognized!");
return 0;
}
}
public static uint TexelSize(TextureFormat format)
{
switch (format)
{
case TextureFormat.BC2:
case TextureFormat.BC3:
case TextureFormat.BC7:
return 16;
case TextureFormat.BC1:
return 8;
default:
return 1;
}
}
public static uint BlockSizeSquared(TextureFormat format)
{
switch (format)
{
case TextureFormat.BC1:
case TextureFormat.BC2:
case TextureFormat.BC3:
case TextureFormat.BC7:
return 16;
case TextureFormat.R8G8B8A8:
case TextureFormat.B8G8R8A8:
case TextureFormat.R5G6B5:
case TextureFormat.A1R5G5B5:
case TextureFormat.B4G4R4A4:
case TextureFormat.A2R10G10B10:
case TextureFormat.R16G16:
case TextureFormat.R16G16B16A16:
case TextureFormat.R8:
case TextureFormat.R8G8_SNORM:
case TextureFormat.R8G8B8A8_SNORM:
case TextureFormat.R16_SFLOAT:
case TextureFormat.R16G16_SFLOAT:
case TextureFormat.R16G16B16A16_SFLOAT:
case TextureFormat.R32_SFLOAT:
case TextureFormat.R32G32_SFLOAT:
case TextureFormat.R32G32B32A32_SFLOAT:
case TextureFormat.R8_UINT:
case TextureFormat.R8G8_UINT:
case TextureFormat.R8G8B8A8_UINT:
case TextureFormat.R16_UINT:
case TextureFormat.R16G16_UINT:
case TextureFormat.R16G16B16A16_UINT:
case TextureFormat.D16_UNORM:
case TextureFormat.D24_UNORM:
case TextureFormat.D24_UNORM_S8_UINT:
case TextureFormat.D32_SFLOAT:
case TextureFormat.D32_SFLOAT_S8_UINT:
return 1;
default:
Log.Error("Texture format not recognized!");
return 0;
}
}
public static implicit operator TextureSlice(Texture t) => new TextureSlice(t);
public static implicit operator TextureRegion(Texture t) => new TextureRegion(t);
}

@@ -0,0 +1,209 @@
using System;
using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// A data container that can efficiently transfer data to and from the GPU.
/// </summary>
public unsafe class TransferBuffer : RefreshResource
{
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseTransferBuffer;
/// <summary>
/// Size in bytes.
/// </summary>
public uint Size { get; }
#if DEBUG
public bool Mapped { get; private set; }
#endif
/// <summary>
/// Creates a buffer of requested size given a type and element count.
/// </summary>
/// <typeparam name="T">The type that the buffer will contain.</typeparam>
/// <param name="device">The GraphicsDevice.</param>
/// <param name="elementCount">How many elements of type T the buffer will contain.</param>
/// <returns></returns>
public unsafe static TransferBuffer Create<T>(
GraphicsDevice device,
TransferBufferUsage usage,
uint elementCount
) where T : unmanaged
{
return new TransferBuffer(
device,
usage,
(uint) Marshal.SizeOf<T>() * elementCount
);
}
/// <summary>
/// Creates a TransferBuffer.
/// </summary>
/// <param name="device">An initialized GraphicsDevice.</param>
/// <param name="usage">Whether this will be used to upload buffers or textures.</param>
/// <param name="sizeInBytes">The length of the buffer. Cannot be resized.</param>
public TransferBuffer(
GraphicsDevice device,
TransferBufferUsage usage,
uint sizeInBytes
) : base(device)
{
Handle = Refresh.Refresh_CreateTransferBuffer(
device.Handle,
(Refresh.TransferBufferUsage) usage,
sizeInBytes
);
Size = sizeInBytes;
}
/// <summary>
/// Immediately copies data from a Span to the TransferBuffer.
/// Returns the length of the copy in bytes.
///
/// If cycle is set to true and this TransferBuffer was used in an Upload command,
/// that command will still use the correct data at the cost of increased memory usage.
///
/// If cycle is set to false, the data will be overwritten immediately,
/// which could cause a data race.
/// </summary>
public unsafe uint SetData<T>(
Span<T> source,
uint bufferOffsetInBytes,
bool cycle
) where T : unmanaged
{
int elementSize = Marshal.SizeOf<T>();
uint dataLengthInBytes = (uint) (elementSize * source.Length);
#if DEBUG
AssertBufferBoundsCheck(Size, bufferOffsetInBytes, dataLengthInBytes);
AssertNotMapped();
#endif
fixed (T* dataPtr = source)
{
Refresh.Refresh_SetTransferData(
Device.Handle,
(nint) dataPtr,
new Refresh.TransferBufferRegion
{
TransferBuffer = Handle,
Offset = bufferOffsetInBytes,
Size = dataLengthInBytes
},
Conversions.BoolToInt(cycle)
);
}
return dataLengthInBytes;
}
/// <summary>
/// Immediately copies data from a Span to the TransferBuffer.
/// Returns the length of the copy in bytes.
///
/// If cycle is set to true and this TransferBuffer was used in an Upload command,
/// that command will still use the corret data at the cost of increased memory usage.
///
/// If cycle is set to false, the data will be overwritten immediately,
/// which could cause a data race.
/// </summary>
public unsafe uint SetData<T>(
Span<T> source,
bool cycle
) where T : unmanaged
{
return SetData(source, 0, cycle);
}
/// <summary>
/// Immediately copies data from the TransferBuffer into a Span.
/// </summary>
public unsafe void GetData<T>(
Span<T> destination,
uint bufferOffsetInBytes = 0
) where T : unmanaged
{
int elementSize = Marshal.SizeOf<T>();
uint dataLengthInBytes = (uint) (elementSize * destination.Length);
#if DEBUG
AssertBufferBoundsCheck(Size, bufferOffsetInBytes, dataLengthInBytes);
AssertNotMapped();
#endif
fixed (T* dataPtr = destination)
{
Refresh.Refresh_GetTransferData(
Device.Handle,
new Refresh.TransferBufferRegion
{
TransferBuffer = Handle,
Offset = bufferOffsetInBytes,
Size = dataLengthInBytes
},
(nint) dataPtr
);
}
}
/// <summary>
/// Maps the transfer buffer into application address space.
/// You must call Unmap before encoding transfer commands.
/// </summary>
public unsafe void Map(bool cycle, out byte* data)
{
#if DEBUG
AssertNotMapped();
#endif
Refresh.Refresh_MapTransferBuffer(
Device.Handle,
Handle,
Conversions.BoolToInt(cycle),
out data
);
#if DEBUG
Mapped = true;
#endif
}
/// <summary>
/// Unmaps the transfer buffer.
/// The pointer given by Map is no longer valid.
/// </summary>
public void Unmap()
{
Refresh.Refresh_UnmapTransferBuffer(
Device.Handle,
Handle
);
#if DEBUG
Mapped = false;
#endif
}
#if DEBUG
private void AssertBufferBoundsCheck(uint bufferLengthInBytes, uint offsetInBytes, uint copyLengthInBytes)
{
if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes)
{
throw new InvalidOperationException($"Data overflow! Transfer buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}");
}
}
private void AssertNotMapped()
{
if (Mapped)
{
throw new InvalidOperationException("Transfer buffer must not be mapped!");
}
}
#endif
}

@@ -0,0 +1,9 @@
#version 450
layout(location = 0) out vec2 outTexCoord;
void main()
{
outTexCoord = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(outTexCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.0, 1.0);
}

@@ -0,0 +1,34 @@
#version 450
layout(location = 0) in vec2 inTexCoord;
layout(location = 1) in vec4 inColor;
layout(location = 0) out vec4 outColor;
layout(set = 2, binding = 0) uniform sampler2D msdf;
layout(set = 3, binding = 0) uniform UBO
{
float pxRange;
} ubo;
float median(float r, float g, float b)
{
return max(min(r, g), min(max(r, g), b));
}
float screenPxRange()
{
vec2 unitRange = vec2(ubo.pxRange)/vec2(textureSize(msdf, 0));
vec2 screenTexSize = vec2(1.0)/fwidth(inTexCoord);
return max(0.5*dot(unitRange, screenTexSize), 1.0);
}
void main()
{
vec3 msd = texture(msdf, inTexCoord).rgb;
float sd = median(msd.r, msd.g, msd.b);
float screenPxDistance = screenPxRange() * (sd - 0.5);
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
outColor = mix(vec4(0.0, 0.0, 0.0, 0.0), inColor, opacity);
}

@@ -0,0 +1,20 @@
#version 450
layout(location = 0) in vec3 inPos;
layout(location = 1) in vec2 inTexCoord;
layout(location = 2) in vec4 inColor;
layout(location = 0) out vec2 outTexCoord;
layout(location = 1) out vec4 outColor;
layout(set = 1, binding = 0) uniform UBO
{
mat4 ViewProjection;
} ubo;
void main()
{
gl_Position = ubo.ViewProjection * vec4(inPos, 1.0);
outTexCoord = inTexCoord;
outColor = inColor;
}

@@ -0,0 +1,38 @@
/*
* This effect is based on the YUV-to-RGBA GLSL shader found in SDL.
* Thus, it also released under the zlib license:
* http://libsdl.org/license.php
*/
#version 450
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 2, binding = 0) uniform sampler2D YSampler;
layout(set = 2, binding = 1) uniform sampler2D USampler;
layout(set = 2, binding = 2) uniform sampler2D VSampler;
/* More info about colorspace conversion:
* http://www.equasys.de/colorconversion.html
* http://www.equasys.de/colorformat.html
*/
const vec3 offset = vec3(-0.0625, -0.5, -0.5);
const vec3 Rcoeff = vec3(1.164, 0.000, 1.793);
const vec3 Gcoeff = vec3(1.164, -0.213, -0.533);
const vec3 Bcoeff = vec3(1.164, 2.112, 0.000);
void main()
{
vec3 yuv;
yuv.x = texture(YSampler, TexCoord).r;
yuv.y = texture(USampler, TexCoord).r;
yuv.z = texture(VSampler, TexCoord).r;
yuv += offset;
FragColor.r = dot(yuv, Rcoeff);
FragColor.g = dot(yuv, Gcoeff);
FragColor.b = dot(yuv, Bcoeff);
FragColor.a = 1.0;
}