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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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,
];
}

View File

@@ -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);
}
}

View File

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

View File

@@ -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);
}
}

View File

@@ -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
];
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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
);
}
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -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()
);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;
}