Setup entry point + integrated moonworks stuff
This commit is contained in:
88
Nerfed.Runtime/Graphics/Resources/Buffer.cs
Normal file
88
Nerfed.Runtime/Graphics/Resources/Buffer.cs
Normal 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);
|
||||
}
|
||||
}
|
91
Nerfed.Runtime/Graphics/Resources/ComputePipeline.cs
Normal file
91
Nerfed.Runtime/Graphics/Resources/ComputePipeline.cs
Normal 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;
|
||||
}
|
||||
}
|
25
Nerfed.Runtime/Graphics/Resources/Fence.cs
Normal file
25
Nerfed.Runtime/Graphics/Resources/Fence.cs
Normal 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;
|
||||
}
|
||||
}
|
99
Nerfed.Runtime/Graphics/Resources/GraphicsPipeline.cs
Normal file
99
Nerfed.Runtime/Graphics/Resources/GraphicsPipeline.cs
Normal 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
|
||||
}
|
||||
}
|
23
Nerfed.Runtime/Graphics/Resources/Sampler.cs
Normal file
23
Nerfed.Runtime/Graphics/Resources/Sampler.cs
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
91
Nerfed.Runtime/Graphics/Resources/Shader.cs
Normal file
91
Nerfed.Runtime/Graphics/Resources/Shader.cs
Normal 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;
|
||||
}
|
||||
}
|
326
Nerfed.Runtime/Graphics/Resources/Texture.cs
Normal file
326
Nerfed.Runtime/Graphics/Resources/Texture.cs
Normal 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);
|
||||
}
|
209
Nerfed.Runtime/Graphics/Resources/TransferBuffer.cs
Normal file
209
Nerfed.Runtime/Graphics/Resources/TransferBuffer.cs
Normal 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
|
||||
}
|
Reference in New Issue
Block a user