struct and enum conversions

sdl2_gpu
cosmonaut 2024-06-04 12:19:41 -07:00
parent 8b35b6b0b7
commit 4e8653fb1f
16 changed files with 1058 additions and 1062 deletions

@ -1 +1 @@
Subproject commit b3cf918f900a24d131b4eb130a87dd12b4e4ca8c
Subproject commit 2866dd2a7d4ffd4366bd4885e04c68cf4b485b55

View File

@ -21,7 +21,7 @@ namespace MoonWorks
{ VertexElementFormat.NormalizedShort4, (uint) Marshal.SizeOf<NormalizedShort4>() },
{ VertexElementFormat.Short2, (uint) Marshal.SizeOf<Short2>() },
{ VertexElementFormat.Short4, (uint) Marshal.SizeOf<Short4>() },
{ VertexElementFormat.UInt, (uint) Marshal.SizeOf<uint>() },
{ VertexElementFormat.Uint, (uint) Marshal.SizeOf<uint>() },
{ VertexElementFormat.Vector2, (uint) Marshal.SizeOf<Math.Float.Vector2>() },
{ VertexElementFormat.Vector3, (uint) Marshal.SizeOf<Math.Float.Vector3>() },
{ VertexElementFormat.Vector4, (uint) Marshal.SizeOf<Math.Float.Vector4>() }
@ -37,6 +37,16 @@ namespace MoonWorks
return b != 0;
}
public static int BoolToInt(bool b)
{
return b ? 1 : 0;
}
public static bool IntToBool(int b)
{
return b != 0;
}
public static uint VertexElementFormatSize(VertexElementFormat format)
{
return Sizes[format];

View File

@ -1,326 +0,0 @@
using System;
namespace MoonWorks
{
/// <summary>
/// Presentation mode for a window.
/// </summary>
public enum PresentMode
{
/// <summary>
/// Does not wait for v-blank to update the window. Can cause visible tearing.
/// </summary>
Immediate,
/// <summary>
/// Waits for v-blank and uses a queue to hold present requests.
/// Allows for low latency while preventing tearing.
/// May not be supported on non-Vulkan non-Linux systems or older hardware.
/// </summary>
Mailbox,
/// <summary>
/// Waits for v-blank and adds present requests to a queue.
/// Will probably cause latency.
/// Required to be supported by all compliant hardware.
/// </summary>
FIFO,
/// <summary>
/// Usually waits for v-blank, but if v-blank has passed since last update will update immediately.
/// May cause visible tearing.
/// </summary>
FIFORelaxed
}
}
/* Recreate all the enums in here so we don't need to explicitly
* reference the RefreshCS namespace when using MoonWorks.Graphics
*/
namespace MoonWorks.Graphics
{
public enum PrimitiveType
{
PointList,
LineList,
LineStrip,
TriangleList,
TriangleStrip
}
/// <summary>
/// Describes the operation that a render pass will use when loading a render target.
/// </summary>
public enum LoadOp
{
Load,
Clear,
DontCare
}
/// <summary>
/// Describes the operation that a render pass will use when storing a render target.
/// </summary>
public enum StoreOp
{
Store,
DontCare
}
[Flags]
public enum ClearOptionsFlags : uint
{
Color = 1,
Depth = 2,
Stencil = 4,
DepthStencil = Depth | Stencil,
All = Color | Depth | Stencil
}
public enum IndexElementSize
{
Sixteen,
ThirtyTwo
}
public enum TextureFormat
{
R8G8B8A8,
B8G8R8A8,
R5G6B5,
A1R5G5B5,
B4G4R4A4,
A2R10G10B10,
R16G16,
R16G16B16A16,
R8,
BC1,
BC2,
BC3,
BC7,
R8G8_SNORM,
R8G8B8A8_SNORM,
R16_SFLOAT,
R16G16_SFLOAT,
R16G16B16A16_SFLOAT,
R32_SFLOAT,
R32G32_SFLOAT,
R32G32B32A32_SFLOAT,
R8_UINT,
R8G8_UINT,
R8G8B8A8_UINT,
R16_UINT,
R16G16_UINT,
R16G16B16A16_UINT,
D16,
D32,
D16S8,
D32S8
}
[Flags]
public enum TextureUsageFlags : uint
{
Sampler = 1,
ColorTarget = 2,
DepthStencilTarget = 4,
Compute = 8
}
public enum SampleCount
{
One,
Two,
Four,
Eight
}
public enum CubeMapFace : uint
{
PositiveX,
NegativeX,
PositiveY,
NegativeY,
PositiveZ,
NegativeZ
}
[Flags]
public enum BufferUsageFlags : uint
{
Vertex = 1,
Index = 2,
Compute = 4,
Indirect = 8
}
public enum VertexElementFormat
{
UInt,
Float,
Vector2,
Vector3,
Vector4,
Color,
Byte4,
Short2,
Short4,
NormalizedShort2,
NormalizedShort4,
HalfVector2,
HalfVector4
}
public enum VertexInputRate
{
Vertex,
Instance
}
public enum FillMode
{
Fill,
Line
}
public enum CullMode
{
None,
Front,
Back
}
public enum FrontFace
{
CounterClockwise,
Clockwise
}
public enum CompareOp
{
Never,
Less,
Equal,
LessOrEqual,
Greater,
NotEqual,
GreaterOrEqual,
Always
}
public enum StencilOp
{
Keep,
Zero,
Replace,
IncrementAndClamp,
DecrementAndClamp,
Invert,
IncrementAndWrap,
DecrementAndWrap
}
public enum BlendOp
{
Add,
Subtract,
ReverseSubtract,
Min,
Max
}
public enum BlendFactor
{
Zero,
One,
SourceColor,
OneMinusSourceColor,
DestinationColor,
OneMinusDestinationColor,
SourceAlpha,
OneMinusSourceAlpha,
DestinationAlpha,
OneMinusDestinationAlpha,
ConstantColor,
OneMinusConstantColor,
SourceAlphaSaturate
}
[Flags]
public enum ColorComponentFlags : uint
{
R = 1,
G = 2,
B = 4,
A = 8,
RG = R | G,
RB = R | B,
RA = R | A,
GB = G | B,
GA = G | A,
BA = B | A,
RGB = R | G | B,
RGA = R | G | A,
GBA = G | B | A,
RGBA = R | G | B | A,
None = 0
}
public enum Filter
{
Nearest,
Linear
}
public enum SamplerMipmapMode
{
Nearest,
Linear
}
public enum SamplerAddressMode
{
Repeat,
MirroredRepeat,
ClampToEdge,
ClampToBorder
}
public enum BorderColor
{
FloatTransparentBlack,
IntTransparentBlack,
FloatOpaqueBlack,
IntOpaqueBlack,
FloatOpaqueWhite,
IntOpaqueWhite
}
public enum TransferUsage
{
Buffer,
Texture
}
public enum TransferOptions
{
Cycle,
Unsafe
}
public enum WriteOptions
{
Cycle,
Unsafe,
Safe
}
public enum Backend
{
Vulkan,
D3D11,
PS5,
Invalid
}
}

View File

@ -1,508 +0,0 @@
using RefreshCS;
using System.Runtime.InteropServices;
/* Recreate some structs in here so we don't need to explicitly
* reference the RefreshCS namespace when using MoonWorks.Graphics
*/
namespace MoonWorks.Graphics
{
[StructLayout(LayoutKind.Sequential)]
public struct DepthStencilValue
{
public float Depth;
public uint Stencil;
public DepthStencilValue(float depth, uint stencil)
{
Depth = depth;
Stencil = stencil;
}
// FIXME: can we do an unsafe cast somehow?
public Refresh.DepthStencilValue ToRefresh()
{
return new Refresh.DepthStencilValue
{
depth = Depth,
stencil = Stencil
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int X;
public int Y;
public int W;
public int H;
public Rect(int x, int y, int w, int h)
{
X = x;
Y = y;
W = w;
H = h;
}
public Rect(int w, int h)
{
X = 0;
Y = 0;
W = w;
H = h;
}
// FIXME: can we do an unsafe cast somehow?
public Refresh.Rect ToRefresh()
{
return new Refresh.Rect
{
x = X,
y = Y,
w = W,
h = H
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Viewport
{
public float X;
public float Y;
public float W;
public float H;
public float MinDepth;
public float MaxDepth;
public Viewport(float w, float h)
{
X = 0;
Y = 0;
W = w;
H = h;
MinDepth = 0;
MaxDepth = 1;
}
public Viewport(float x, float y, float w, float h)
{
X = x;
Y = y;
W = w;
H = h;
MinDepth = 0;
MaxDepth = 1;
}
public Viewport(float x, float y, float w, float h, float minDepth, float maxDepth)
{
X = x;
Y = y;
W = w;
H = h;
MinDepth = minDepth;
MaxDepth = maxDepth;
}
public Refresh.Viewport ToRefresh()
{
return new Refresh.Viewport
{
x = X,
y = Y,
w = W,
h = H,
minDepth = MinDepth,
maxDepth = MaxDepth
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct VertexBinding
{
public uint Binding;
public uint Stride;
public VertexInputRate InputRate;
public static VertexBinding Create<T>(uint binding = 0, VertexInputRate inputRate = VertexInputRate.Vertex) where T : unmanaged
{
return new VertexBinding
{
Binding = binding,
InputRate = inputRate,
Stride = (uint) Marshal.SizeOf<T>()
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct VertexAttribute
{
public uint Location;
public uint Binding;
public VertexElementFormat Format;
public uint Offset;
}
[StructLayout(LayoutKind.Sequential)]
public struct StencilOpState
{
public StencilOp FailOp;
public StencilOp PassOp;
public StencilOp DepthFailOp;
public CompareOp CompareOp;
public Refresh.StencilOpState ToRefresh()
{
return new Refresh.StencilOpState
{
failOp = (Refresh.StencilOp) FailOp,
passOp = (Refresh.StencilOp) PassOp,
depthFailOp = (Refresh.StencilOp) DepthFailOp,
compareOp = (Refresh.CompareOp) CompareOp
};
}
}
/// <summary>
/// Determines how a color texture will be read/written in a render pass.
/// </summary>
public struct ColorAttachmentInfo
{
public TextureSlice TextureSlice;
/// <summary>
/// If LoadOp is set to Clear, the texture slice will be cleared to this color.
/// </summary>
public Color ClearColor;
/// <summary>
/// Determines what is done with the texture slice memory
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single color. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp LoadOp;
/// <summary>
/// Determines what is done with the texture slice memory
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// </summary>
public StoreOp StoreOp;
/// <summary>
/// Specifies data dependency behavior. This option is ignored if LoadOp is Load. <br/>
///
/// Cycle:
/// If this texture slice has been used in commands that have not completed,
/// the implementation may prevent a dependency on those commands
/// at the cost of increased memory usage.
/// You may NOT assume that any of the previous texture (not slice!) data is retained.
/// This may prevent stalls when frequently reusing a texture slice in rendering. <br/>
///
/// SafeOverwrite:
/// Overwrites the data safely using a GPU memory barrier.
/// </summary>
public WriteOptions WriteOption;
public ColorAttachmentInfo(
TextureSlice textureSlice,
WriteOptions writeOption,
Color clearColor,
StoreOp storeOp = StoreOp.Store
) {
TextureSlice = textureSlice;
ClearColor = clearColor;
LoadOp = LoadOp.Clear;
StoreOp = storeOp;
WriteOption = writeOption;
}
public ColorAttachmentInfo(
TextureSlice textureSlice,
WriteOptions writeOption,
LoadOp loadOp = LoadOp.DontCare,
StoreOp storeOp = StoreOp.Store
) {
TextureSlice = textureSlice;
ClearColor = Color.White;
LoadOp = loadOp;
StoreOp = storeOp;
WriteOption = writeOption;
}
public Refresh.ColorAttachmentInfo ToRefresh()
{
return new Refresh.ColorAttachmentInfo
{
textureSlice = TextureSlice.ToRefreshTextureSlice(),
clearColor = new Refresh.Vec4
{
x = ClearColor.R / 255f,
y = ClearColor.G / 255f,
z = ClearColor.B / 255f,
w = ClearColor.A / 255f
},
loadOp = (Refresh.LoadOp) LoadOp,
storeOp = (Refresh.StoreOp) StoreOp,
writeOption = (Refresh.WriteOptions) WriteOption
};
}
}
/// <summary>
/// Determines how a depth/stencil texture will be read/written in a render pass.
/// </summary>
public struct DepthStencilAttachmentInfo
{
public TextureSlice TextureSlice;
/// <summary>
/// If LoadOp is set to Clear, the texture slice depth will be cleared to this depth value. <br/>
/// If StencilLoadOp is set to Clear, the texture slice stencil value will be cleared to this stencil value.
/// </summary>
public DepthStencilValue DepthStencilClearValue;
/// <summary>
/// Determines what is done with the texture slice depth values
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single depth value. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp LoadOp;
/// <summary>
/// Determines what is done with the texture slice depth values
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// This is usually a good option for depth textures that don't need to be reused.
/// </summary>
public StoreOp StoreOp;
/// <summary>
/// Determines what is done with the texture slice stencil values
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single stencil value. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp StencilLoadOp;
/// <summary>
/// Determines what is done with the texture slice stencil values
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// This is usually a good option for stencil textures that don't need to be reused.
/// </summary>
public StoreOp StencilStoreOp;
/// <summary>
/// Specifies data dependency behavior. This option is ignored if LoadOp or StencilLoadOp is Load. <br/>
///
/// Cycle:
/// If this texture slice has been used in commands that have not completed,
/// the implementation may prevent a dependency on those commands
/// at the cost of increased memory usage.
/// You may NOT assume that any of the previous texture (not slice!) data is retained.
/// This may prevent stalls when frequently reusing a texture slice in rendering. <br/>
///
/// SafeOverwrite:
/// Overwrites the data safely using a GPU memory barrier.
/// </summary>
public WriteOptions WriteOption;
public DepthStencilAttachmentInfo(
TextureSlice textureSlice,
WriteOptions writeOption,
DepthStencilValue clearValue,
StoreOp depthStoreOp = StoreOp.DontCare,
StoreOp stencilStoreOp = StoreOp.DontCare
){
TextureSlice = textureSlice;
DepthStencilClearValue = clearValue;
LoadOp = LoadOp.Clear;
StoreOp = depthStoreOp;
StencilLoadOp = LoadOp.Clear;
StencilStoreOp = stencilStoreOp;
WriteOption = writeOption;
}
public DepthStencilAttachmentInfo(
TextureSlice textureSlice,
WriteOptions writeOption,
LoadOp loadOp = LoadOp.DontCare,
StoreOp storeOp = StoreOp.DontCare,
LoadOp stencilLoadOp = LoadOp.DontCare,
StoreOp stencilStoreOp = StoreOp.DontCare
) {
TextureSlice = textureSlice;
DepthStencilClearValue = new DepthStencilValue();
LoadOp = loadOp;
StoreOp = storeOp;
StencilLoadOp = stencilLoadOp;
StencilStoreOp = stencilStoreOp;
WriteOption = writeOption;
}
public DepthStencilAttachmentInfo(
TextureSlice textureSlice,
WriteOptions writeOption,
DepthStencilValue clearValue,
LoadOp loadOp,
StoreOp storeOp,
LoadOp stencilLoadOp,
StoreOp stencilStoreOp
) {
TextureSlice = textureSlice;
DepthStencilClearValue = clearValue;
LoadOp = loadOp;
StoreOp = storeOp;
StencilLoadOp = stencilLoadOp;
StencilStoreOp = stencilStoreOp;
WriteOption = writeOption;
}
public Refresh.DepthStencilAttachmentInfo ToRefresh()
{
return new Refresh.DepthStencilAttachmentInfo
{
textureSlice = TextureSlice.ToRefreshTextureSlice(),
depthStencilClearValue = DepthStencilClearValue.ToRefresh(),
loadOp = (Refresh.LoadOp) LoadOp,
storeOp = (Refresh.StoreOp) StoreOp,
stencilLoadOp = (Refresh.LoadOp) StencilLoadOp,
stencilStoreOp = (Refresh.StoreOp) StencilStoreOp,
writeOption = (Refresh.WriteOptions) WriteOption
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct ColorAttachmentDescription
{
public TextureFormat Format;
public ColorAttachmentBlendState BlendState;
public ColorAttachmentDescription(
TextureFormat format,
ColorAttachmentBlendState blendState
) {
Format = format;
BlendState = blendState;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct IndirectDrawCommand
{
public uint VertexCount;
public uint InstanceCount;
public uint FirstVertex;
public uint FirstInstance;
public IndirectDrawCommand(
uint vertexCount,
uint instanceCount,
uint firstVertex,
uint firstInstance
) {
VertexCount = vertexCount;
InstanceCount = instanceCount;
FirstVertex = firstVertex;
FirstInstance = firstInstance;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct BufferCopy
{
public uint SrcOffset;
public uint DstOffset;
public uint Size;
public BufferCopy(
uint srcOffset,
uint dstOffset,
uint size
) {
SrcOffset = srcOffset;
DstOffset = dstOffset;
Size = size;
}
public Refresh.BufferCopy ToRefresh()
{
return new Refresh.BufferCopy
{
srcOffset = SrcOffset,
dstOffset = DstOffset,
size = Size
};
}
}
/// <summary>
/// Parameters for a copy between buffer and image.
/// </summary>
/// <param name="BufferOffset">The offset into the buffer.</param>
/// <param name="BufferStride">If 0, image data is assumed tightly packed.</param>
/// <param name="BufferImageHeight">If 0, image data is assumed tightly packed.</param>
[StructLayout(LayoutKind.Sequential)]
public readonly record struct BufferImageCopy(
uint BufferOffset,
uint BufferStride,
uint BufferImageHeight
) {
public Refresh.BufferImageCopy ToRefresh()
{
return new Refresh.BufferImageCopy
{
bufferOffset = BufferOffset,
bufferStride = BufferStride,
bufferImageHeight = BufferImageHeight
};
}
}
}

View File

@ -6,9 +6,9 @@ namespace MoonWorks.Graphics
/// <summary>
/// Compute pipelines perform arbitrary parallel processing on input data.
/// </summary>
public class ComputePipeline : RefreshResource
public class ComputePipeline : SDL_GpuResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyComputePipeline;
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroyComputePipeline;
public ComputeShaderInfo ComputeShaderInfo { get; }

View File

@ -10,9 +10,9 @@ namespace MoonWorks.Graphics
/// 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
public class Fence : SDL_GpuResource
{
protected override Action<nint, nint> QueueDestroyFunction => Refresh.Refresh_ReleaseFence;
protected override Action<nint, nint> ReleaseFunction => Refresh.Refresh_ReleaseFence;
internal Fence(GraphicsDevice device) : base(device)
{

View File

@ -7,9 +7,9 @@ namespace MoonWorks.Graphics
/// <summary>
/// GpuBuffers are generic data containers that can be used by the GPU.
/// </summary>
public class GpuBuffer : RefreshResource
public class GpuBuffer : SDL_GpuResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyGpuBuffer;
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroyGpuBuffer;
/// <summary>
/// Size in bytes.

View File

@ -8,9 +8,9 @@ namespace MoonWorks.Graphics
/// 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
public class GraphicsPipeline : SDL_GpuResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyGraphicsPipeline;
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroyGraphicsPipeline;
public GraphicsShaderInfo VertexShaderInfo { get; }
public GraphicsShaderInfo FragmentShaderInfo { get; }

View File

@ -6,9 +6,9 @@ namespace MoonWorks.Graphics
/// <summary>
/// A sampler specifies how a texture will be sampled in a shader.
/// </summary>
public class Sampler : RefreshResource
public class Sampler : SDL_GpuResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroySampler;
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroySampler;
public Sampler(
GraphicsDevice device,

View File

@ -0,0 +1,65 @@
using SDL2_gpuCS;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MoonWorks.Graphics
{
/// <summary>
/// Shader modules expect input in Refresh bytecode format.
/// </summary>
public class Shader : SDL_GpuResource
{
protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseShader;
public unsafe Shader(
GraphicsDevice device,
string filePath,
string entryPointName,
ShaderStage shaderStage,
ShaderFormat shaderFormat
) : base(device)
{
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
Handle = CreateFromStream(device, stream, entryPointName, shaderStage, shaderFormat);
}
public unsafe Shader(
GraphicsDevice device,
Stream stream,
string entryPointName,
ShaderStage shaderStage,
ShaderFormat shaderFormat
) : base(device)
{
Handle = CreateFromStream(device, stream, entryPointName, shaderStage, shaderFormat);
}
private static unsafe IntPtr CreateFromStream(
GraphicsDevice device,
Stream stream,
string entryPointName,
ShaderStage shaderStage,
ShaderFormat shaderFormat
) {
var bytecodeBuffer = NativeMemory.Alloc((nuint) stream.Length);
var bytecodeSpan = new Span<byte>(bytecodeBuffer, (int) stream.Length);
stream.ReadExactly(bytecodeSpan);
SDL_Gpu.ShaderCreateInfo shaderCreateInfo;
shaderCreateInfo.CodeSize = (nuint) stream.Length;
shaderCreateInfo.Code = (byte*) bytecodeBuffer;
shaderCreateInfo.EntryPointName = entryPointName;
shaderCreateInfo.Stage = (SDL_Gpu.ShaderStage) shaderStage;
shaderCreateInfo.Format = (SDL_Gpu.ShaderFormat) shaderFormat;
var shaderModule = SDL_Gpu.SDL_GpuCreateShader(
device.Handle,
shaderCreateInfo
);
NativeMemory.Free(bytecodeBuffer);
return shaderModule;
}
}
}

View File

@ -1,42 +0,0 @@
using RefreshCS;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MoonWorks.Graphics
{
/// <summary>
/// Shader modules expect input in Refresh bytecode format.
/// </summary>
public class ShaderModule : RefreshResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyShaderModule;
public unsafe ShaderModule(GraphicsDevice device, string filePath) : base(device)
{
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
Handle = CreateFromStream(device, stream);
}
public unsafe ShaderModule(GraphicsDevice device, Stream stream) : base(device)
{
Handle = CreateFromStream(device, stream);
}
private static unsafe IntPtr CreateFromStream(GraphicsDevice device, Stream stream)
{
var bytecodeBuffer = NativeMemory.Alloc((nuint) stream.Length);
var bytecodeSpan = new Span<byte>(bytecodeBuffer, (int) stream.Length);
stream.ReadExactly(bytecodeSpan);
Refresh.ShaderModuleCreateInfo shaderModuleCreateInfo;
shaderModuleCreateInfo.codeSize = (nuint) stream.Length;
shaderModuleCreateInfo.byteCode = (nint) bytecodeBuffer;
var shaderModule = Refresh.Refresh_CreateShaderModule(device.Handle, shaderModuleCreateInfo);
NativeMemory.Free(bytecodeBuffer);
return shaderModule;
}
}
}

View File

@ -7,7 +7,7 @@ namespace MoonWorks.Graphics
/// <summary>
/// A container for pixel data.
/// </summary>
public class Texture : RefreshResource
public class Texture : SDL_GpuResource
{
public uint Width { get; internal set; }
public uint Height { get; internal set; }
@ -41,7 +41,7 @@ namespace MoonWorks.Graphics
}
// FIXME: this allocates a delegate instance
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyTexture;
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroyTexture;
/// <summary>
/// Creates a 2D texture.

View File

@ -4,9 +4,9 @@ using RefreshCS;
namespace MoonWorks.Graphics
{
public unsafe class TransferBuffer : RefreshResource
public unsafe class TransferBuffer : SDL_GpuResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyTransferBuffer;
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroyTransferBuffer;
/// <summary>
/// Size in bytes.

View File

@ -1,17 +1,16 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace MoonWorks.Graphics;
public abstract class RefreshResource : GraphicsResource
public abstract class SDL_GpuResource : GraphicsResource
{
public IntPtr Handle { get => handle; internal set => handle = value; }
private IntPtr handle;
protected abstract Action<IntPtr, IntPtr> QueueDestroyFunction { get; }
protected abstract Action<IntPtr, IntPtr> ReleaseFunction { get; }
protected RefreshResource(GraphicsDevice device) : base(device)
protected SDL_GpuResource(GraphicsDevice device) : base(device)
{
}
@ -19,11 +18,11 @@ public abstract class RefreshResource : GraphicsResource
{
if (!IsDisposed)
{
// Atomically call destroy function in case this is called from the finalizer thread
// Atomically call release function in case this is called from the finalizer thread
var toDispose = Interlocked.Exchange(ref handle, IntPtr.Zero);
if (toDispose != IntPtr.Zero)
{
QueueDestroyFunction(Device.Handle, toDispose);
ReleaseFunction(Device.Handle, toDispose);
}
}
base.Dispose(disposing);

View File

@ -0,0 +1,799 @@
using System;
using System.Runtime.InteropServices;
using SDL2_gpuCS;
namespace MoonWorks.Graphics;
// Recreate certain types in here so we can hide the SDL_GpuCS namespace
public enum PrimitiveType
{
PointList,
LineList,
LineStrip,
TriangleList,
TriangleStrip
}
public enum LoadOp
{
Load,
Clear,
DontCare
}
public enum StoreOp
{
Store,
DontCare
}
public enum IndexElementSize
{
Sixteen,
ThirtyTwo
}
public enum TextureFormat
{
/* Unsigned Normalized Float Color Formats */
R8G8B8A8,
B8G8R8A8,
R5G6B5,
A1R5G5B5,
B4G4R4A4,
A2R10G10B10,
A2B10G10R10,
R16G16,
R16G16B16A16,
R8,
A8,
/* Compressed Unsigned Normalized Float Color Formats */
BC1,
BC2,
BC3,
BC7,
/* Signed Normalized Float Color Formats */
R8G8_SNORM,
R8G8B8A8_SNORM,
/* Signed Float Color Formats */
R16_SFLOAT,
R16G16_SFLOAT,
R16G16B16A16_SFLOAT,
R32_SFLOAT,
R32G32_SFLOAT,
R32G32B32A32_SFLOAT,
/* Unsigned Integer Color Formats */
R8_UINT,
R8G8_UINT,
R8G8B8A8_UINT,
R16_UINT,
R16G16_UINT,
R16G16B16A16_UINT,
/* SRGB Color Formats */
R8G8B8A8_SRGB,
B8G8R8A8_SRGB,
/* Compressed SRGB Color Formats */
BC3_SRGB,
BC7_SRGB,
/* Depth Formats */
D16_UNORM,
D24_UNORM,
D32_SFLOAT,
D24_UNORM_S8_UINT,
D32_SFLOAT_S8_UINT
}
[Flags]
public enum TextureUsageFlags
{
Sampler = 0x1,
ColorTarget = 0x2,
DepthStencil = 0x4,
GraphicsStorage = 0x8,
ComputeStorageRead = 0x20,
ComputeStorageWrite = 0x40
}
public enum TextureType
{
TwoD,
ThreeD,
Cube
}
public enum SampleCount
{
One,
Two,
Four,
Eight
}
public enum CubeMapFace
{
PositiveX,
NegativeX,
PositiveY,
NegativeY,
PositiveZ,
NegativeZ
}
[Flags]
public enum BufferUsageFlags
{
Vertex = 0x1,
Index = 0x2,
Indirect = 0x4,
GraphicsStorage = 0x8,
ComputeStorageRead = 0x20,
ComputeStorageWrite = 0x40
}
[Flags]
public enum TransferBufferMapFlags
{
Read = 0x1,
Write = 0x2
}
public enum ShaderStage
{
Vertex,
Fragment,
Compute
}
public enum ShaderFormat
{
Invalid,
SPIRV,
DXBC,
DXIL,
MSL,
METALLIB,
SECRET
}
public enum VertexElementFormat
{
Uint,
Float,
Vector2,
Vector3,
Vector4,
Color,
Byte4,
Short2,
Short4,
NormalizedShort2,
NormalizedShort4,
HalfVector2,
HalfVector4
}
public enum VertexInputRate
{
Vertex,
Instance
}
public enum FillMode
{
Fill,
Line
}
public enum CullMode
{
None,
Front,
Back
}
public enum FrontFace
{
CounterClockwise,
Clockwise
}
public enum CompareOp
{
Never,
Less,
Equal,
LessOrEqual,
Greater,
NotEqual,
GreaterOrEqual,
Always
}
public enum StencilOp
{
Keep,
Zero,
Replace,
IncrementAndClamp,
DecrementAndClamp,
Invert,
IncrementAndWrap,
DecrementAndWrap
}
public enum BlendOp
{
Add,
Subtract,
ReverseSubtract,
Min,
Max
}
public enum BlendFactor
{
Zero,
One,
SourceColor,
OneMinusSourceColor,
DestinationColor,
OneMinusDestinationColor,
SourceAlpha,
OneMinusSourceAlpha,
DestinationAlpha,
OneMinusDestinationAlpha,
ConstantColor,
OneMinusConstantColor,
SourceAlphaSaturate
}
[Flags]
public enum ColorComponentFlags
{
R = 0x1,
G = 0x2,
B = 0x4,
A = 0x8
}
public enum Filter
{
Nearest,
Linear
}
public enum SamplerMipmapMode
{
Nearest,
Linear
}
public enum SamplerAddressMode
{
Repeat,
MirroredRepeat,
ClampToEdge,
ClampToBorder
}
public enum BorderColor
{
FloatTransparentBlack,
IntTransparentBlack,
FloatOpaqueBlack,
IntOpaqueBlack,
FloatOpaqueWhite,
IntOpaqueWhite
}
public enum TransferUsage
{
Buffer,
Texture
}
public enum PresentMode
{
VSync,
Immediate,
Mailbox
}
public enum SwapchainComposition
{
SDR,
SDRLinear,
HDRExtendedLinear,
HDR10_ST2084
}
[Flags]
public enum BackendFlags
{
Invalid = 0x0,
Vulkan = 0x1,
D3D11 = 0x2,
Metal = 0x4,
All = Vulkan | D3D11 | Metal
}
[StructLayout(LayoutKind.Sequential)]
public struct DepthStencilValue
{
public float Depth;
public uint Stencil;
public DepthStencilValue(float depth, uint stencil)
{
Depth = depth;
Stencil = stencil;
}
// FIXME: can we do an unsafe cast somehow?
public SDL_Gpu.DepthStencilValue ToSDL()
{
return new SDL_Gpu.DepthStencilValue
{
Depth = Depth,
Stencil = Stencil
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int X;
public int Y;
public int W;
public int H;
public Rect(int x, int y, int w, int h)
{
X = x;
Y = y;
W = w;
H = h;
}
public Rect(int w, int h)
{
X = 0;
Y = 0;
W = w;
H = h;
}
// FIXME: can we do an unsafe cast somehow?
public SDL_Gpu.Rect ToRefresh()
{
return new SDL_Gpu.Rect
{
X = X,
Y = Y,
W = W,
H = H
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Viewport
{
public float X;
public float Y;
public float W;
public float H;
public float MinDepth;
public float MaxDepth;
public Viewport(float w, float h)
{
X = 0;
Y = 0;
W = w;
H = h;
MinDepth = 0;
MaxDepth = 1;
}
public Viewport(float x, float y, float w, float h)
{
X = x;
Y = y;
W = w;
H = h;
MinDepth = 0;
MaxDepth = 1;
}
public Viewport(float x, float y, float w, float h, float minDepth, float maxDepth)
{
X = x;
Y = y;
W = w;
H = h;
MinDepth = minDepth;
MaxDepth = maxDepth;
}
public SDL_Gpu.Viewport ToSDL()
{
return new SDL_Gpu.Viewport
{
X = X,
Y = Y,
W = W,
H = H,
MinDepth = MinDepth,
MaxDepth = MaxDepth
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct VertexBinding
{
public uint Binding;
public uint Stride;
public VertexInputRate InputRate;
public static VertexBinding Create<T>(uint binding = 0, VertexInputRate inputRate = VertexInputRate.Vertex) where T : unmanaged
{
return new VertexBinding
{
Binding = binding,
InputRate = inputRate,
Stride = (uint) Marshal.SizeOf<T>()
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct VertexAttribute
{
public uint Location;
public uint Binding;
public VertexElementFormat Format;
public uint Offset;
}
[StructLayout(LayoutKind.Sequential)]
public struct StencilOpState
{
public StencilOp FailOp;
public StencilOp PassOp;
public StencilOp DepthFailOp;
public CompareOp CompareOp;
public SDL_Gpu.StencilOpState ToSDL()
{
return new SDL_Gpu.StencilOpState
{
FailOp = (SDL_Gpu.StencilOp) FailOp,
PassOp = (SDL_Gpu.StencilOp) PassOp,
DepthFailOp = (SDL_Gpu.StencilOp) DepthFailOp,
CompareOp = (SDL_Gpu.CompareOp) CompareOp
};
}
}
/// <summary>
/// Determines how a color texture will be read/written in a render pass.
/// </summary>
public struct ColorAttachmentInfo
{
public TextureSlice TextureSlice;
/// <summary>
/// If LoadOp is set to Clear, the texture slice will be cleared to this color.
/// </summary>
public Color ClearColor;
/// <summary>
/// Determines what is done with the texture slice memory
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single color. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp LoadOp;
/// <summary>
/// Determines what is done with the texture slice memory
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// </summary>
public StoreOp StoreOp;
/// <summary>
/// If true, cycles the texture if it is bound.
/// </summary>
public bool Cycle;
public ColorAttachmentInfo(
TextureSlice textureSlice,
bool cycle,
Color clearColor,
StoreOp storeOp = StoreOp.Store
) {
TextureSlice = textureSlice;
ClearColor = clearColor;
LoadOp = LoadOp.Clear;
StoreOp = storeOp;
Cycle = cycle;
}
public ColorAttachmentInfo(
TextureSlice textureSlice,
bool cycle,
LoadOp loadOp = LoadOp.DontCare,
StoreOp storeOp = StoreOp.Store
) {
TextureSlice = textureSlice;
ClearColor = Color.White;
LoadOp = loadOp;
StoreOp = storeOp;
Cycle = cycle;
}
public SDL_Gpu.ColorAttachmentInfo ToSDL()
{
return new SDL_Gpu.ColorAttachmentInfo
{
TextureSlice = TextureSlice.ToSDL(),
ClearColor = new SDL_Gpu.Color
{
R = ClearColor.R / 255f,
G = ClearColor.G / 255f,
B = ClearColor.B / 255f,
A = ClearColor.A / 255f
},
LoadOp = (SDL_Gpu.LoadOp) LoadOp,
StoreOp = (SDL_Gpu.StoreOp) StoreOp,
Cycle = Conversions.BoolToInt(Cycle)
};
}
}
/// <summary>
/// Determines how a depth/stencil texture will be read/written in a render pass.
/// </summary>
public struct DepthStencilAttachmentInfo
{
public TextureSlice TextureSlice;
/// <summary>
/// If LoadOp is set to Clear, the texture slice depth will be cleared to this depth value. <br/>
/// If StencilLoadOp is set to Clear, the texture slice stencil value will be cleared to this stencil value.
/// </summary>
public DepthStencilValue DepthStencilClearValue;
/// <summary>
/// Determines what is done with the texture slice depth values
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single depth value. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp LoadOp;
/// <summary>
/// Determines what is done with the texture slice depth values
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// This is usually a good option for depth textures that don't need to be reused.
/// </summary>
public StoreOp StoreOp;
/// <summary>
/// Determines what is done with the texture slice stencil values
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single stencil value. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp StencilLoadOp;
/// <summary>
/// Determines what is done with the texture slice stencil values
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// This is usually a good option for stencil textures that don't need to be reused.
/// </summary>
public StoreOp StencilStoreOp;
/// <summary>
/// If true, cycles the texture if it is bound.
/// </summary>
public bool Cycle;
public DepthStencilAttachmentInfo(
TextureSlice textureSlice,
bool cycle,
DepthStencilValue clearValue,
StoreOp depthStoreOp = StoreOp.DontCare,
StoreOp stencilStoreOp = StoreOp.DontCare
){
TextureSlice = textureSlice;
DepthStencilClearValue = clearValue;
LoadOp = LoadOp.Clear;
StoreOp = depthStoreOp;
StencilLoadOp = LoadOp.Clear;
StencilStoreOp = stencilStoreOp;
Cycle = cycle;
}
public DepthStencilAttachmentInfo(
TextureSlice textureSlice,
bool cycle,
LoadOp loadOp = LoadOp.DontCare,
StoreOp storeOp = StoreOp.DontCare,
LoadOp stencilLoadOp = LoadOp.DontCare,
StoreOp stencilStoreOp = StoreOp.DontCare
) {
TextureSlice = textureSlice;
DepthStencilClearValue = new DepthStencilValue();
LoadOp = loadOp;
StoreOp = storeOp;
StencilLoadOp = stencilLoadOp;
StencilStoreOp = stencilStoreOp;
Cycle = cycle;
}
public DepthStencilAttachmentInfo(
TextureSlice textureSlice,
bool cycle,
DepthStencilValue clearValue,
LoadOp loadOp,
StoreOp storeOp,
LoadOp stencilLoadOp,
StoreOp stencilStoreOp
) {
TextureSlice = textureSlice;
DepthStencilClearValue = clearValue;
LoadOp = loadOp;
StoreOp = storeOp;
StencilLoadOp = stencilLoadOp;
StencilStoreOp = stencilStoreOp;
Cycle = cycle;
}
public SDL_Gpu.DepthStencilAttachmentInfo ToSDL()
{
return new SDL_Gpu.DepthStencilAttachmentInfo
{
TextureSlice = TextureSlice.ToSDL(),
DepthStencilClearValue = DepthStencilClearValue.ToSDL(),
LoadOp = (SDL_Gpu.LoadOp) LoadOp,
StoreOp = (SDL_Gpu.StoreOp) StoreOp,
StencilLoadOp = (SDL_Gpu.LoadOp) StencilLoadOp,
StencilStoreOp = (SDL_Gpu.StoreOp) StencilStoreOp,
Cycle = Conversions.BoolToInt(Cycle)
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct ColorAttachmentDescription
{
public TextureFormat Format;
public ColorAttachmentBlendState BlendState;
public ColorAttachmentDescription(
TextureFormat format,
ColorAttachmentBlendState blendState
) {
Format = format;
BlendState = blendState;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct IndirectDrawCommand
{
public uint VertexCount;
public uint InstanceCount;
public uint FirstVertex;
public uint FirstInstance;
public IndirectDrawCommand(
uint vertexCount,
uint instanceCount,
uint firstVertex,
uint firstInstance
) {
VertexCount = vertexCount;
InstanceCount = instanceCount;
FirstVertex = firstVertex;
FirstInstance = firstInstance;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct BufferCopy
{
public uint SrcOffset;
public uint DstOffset;
public uint Size;
public BufferCopy(
uint srcOffset,
uint dstOffset,
uint size
) {
SrcOffset = srcOffset;
DstOffset = dstOffset;
Size = size;
}
public SDL_Gpu.BufferCopy ToRefresh()
{
return new SDL_Gpu.BufferCopy
{
SourceOffset = SrcOffset,
DestinationOffset = DstOffset,
Size = Size
};
}
}
/// <summary>
/// Parameters for a copy between buffer and image.
/// </summary>
/// <param name="BufferOffset">The offset into the buffer.</param>
/// <param name="BufferStride">If 0, image data is assumed tightly packed.</param>
/// <param name="BufferImageHeight">If 0, image data is assumed tightly packed.</param>
[StructLayout(LayoutKind.Sequential)]
public readonly record struct BufferImageCopy(
uint BufferOffset,
uint BufferStride,
uint BufferImageHeight
) {
public SDL_Gpu.BufferImageCopy ToRefresh()
{
return new SDL_Gpu.BufferImageCopy
{
BufferOffset = BufferOffset,
BufferStride = BufferStride,
BufferImageHeight = BufferImageHeight
};
}
}

View File

@ -1,12 +1,12 @@
using RefreshCS;
using SDL2_gpuCS;
namespace MoonWorks.Graphics
namespace MoonWorks.Graphics;
/// <summary>
/// All of the information that is used to create a sampler.
/// </summary>
public struct SamplerCreateInfo
{
/// <summary>
/// All of the information that is used to create a sampler.
/// </summary>
public struct SamplerCreateInfo
{
/// <summary>
/// Minification filter mode. Used when the image is downscaled.
/// </summary>
@ -150,25 +150,24 @@ namespace MoonWorks.Graphics
MaxLod = 1000
};
public Refresh.SamplerStateCreateInfo ToRefreshSamplerStateCreateInfo()
public SDL_Gpu.SamplerCreateInfo ToSDL()
{
return new Refresh.SamplerStateCreateInfo
return new SDL_Gpu.SamplerCreateInfo
{
minFilter = (Refresh.Filter) MinFilter,
magFilter = (Refresh.Filter) MagFilter,
mipmapMode = (Refresh.SamplerMipmapMode) MipmapMode,
addressModeU = (Refresh.SamplerAddressMode) AddressModeU,
addressModeV = (Refresh.SamplerAddressMode) AddressModeV,
addressModeW = (Refresh.SamplerAddressMode) AddressModeW,
mipLodBias = MipLodBias,
anisotropyEnable = Conversions.BoolToByte(AnisotropyEnable),
maxAnisotropy = MaxAnisotropy,
compareEnable = Conversions.BoolToByte(CompareEnable),
compareOp = (Refresh.CompareOp) CompareOp,
minLod = MinLod,
maxLod = MaxLod,
borderColor = (Refresh.BorderColor) BorderColor
MinFilter = (SDL_Gpu.Filter) MinFilter,
MagFilter = (SDL_Gpu.Filter) MagFilter,
MipmapMode = (SDL_Gpu.SamplerMipmapMode) MipmapMode,
AddressModeU = (SDL_Gpu.SamplerAddressMode) AddressModeU,
AddressModeV = (SDL_Gpu.SamplerAddressMode) AddressModeV,
AddressModeW = (SDL_Gpu.SamplerAddressMode) AddressModeW,
MipLodBias = MipLodBias,
AnisotropyEnable = Conversions.BoolToByte(AnisotropyEnable),
MaxAnisotropy = MaxAnisotropy,
CompareEnable = Conversions.BoolToByte(CompareEnable),
CompareOp = (SDL_Gpu.CompareOp) CompareOp,
MinLod = MinLod,
MaxLod = MaxLod,
BorderColor = (SDL_Gpu.BorderColor) BorderColor
};
}
}
}