basic rendering structure

main
cosmonaut 2021-01-14 17:25:15 -08:00
parent e89cafd34b
commit f92309c614
24 changed files with 571 additions and 33 deletions

@ -1 +1 @@
Subproject commit 5591c81d14d75dc24759dc2843e7b05ab6d98ecc
Subproject commit 377d53f7a9645758a5702d86fa9059c4d8bd1835

10
src/BlendConstants.cs Normal file
View File

@ -0,0 +1,10 @@
namespace Campari
{
public struct BlendConstants
{
public float R;
public float G;
public float B;
public float A;
}
}

38
src/Buffer.cs Normal file
View File

@ -0,0 +1,38 @@
using System;
using RefreshCS;
namespace Campari
{
public class Buffer : GraphicsResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyBuffer;
/* FIXME: could we have a special flags struct? */
public Buffer(RefreshDevice device, uint usageFlags, uint sizeInBytes) : base(device)
{
Handle = Refresh.Refresh_CreateBuffer(
device.Handle,
usageFlags,
sizeInBytes
);
}
public unsafe void SetData<T>(
uint offsetInBytes,
T[] data,
uint dataLengthInBytes
) where T : unmanaged
{
fixed (T* ptr = &data[0])
{
Refresh.Refresh_SetBufferData(
Device.Handle,
Handle,
offsetInBytes,
(IntPtr) ptr,
dataLengthInBytes
);
}
}
}
}

173
src/CommandBuffer.cs Normal file
View File

@ -0,0 +1,173 @@
using System;
using System.Runtime.InteropServices;
using RefreshCS;
namespace Campari
{
public struct CommandBuffer
{
public RefreshDevice Device { get; }
public IntPtr Handle { get; }
// called from RefreshDevice
internal CommandBuffer(RefreshDevice device, IntPtr handle)
{
Device = device;
Handle = handle;
}
public void BeginRenderPass(
RenderPass renderPass,
Framebuffer framebuffer,
ref Refresh.Rect renderArea,
Refresh.Color[] clearColors,
ref Refresh.DepthStencilValue depthStencilClearValue
) {
var clearColorHandle = GCHandle.Alloc(clearColors, GCHandleType.Pinned);
Refresh.Refresh_BeginRenderPass(
Device.Handle,
Handle,
renderPass.Handle,
framebuffer.Handle,
ref renderArea,
clearColorHandle.AddrOfPinnedObject(),
(uint) clearColors.Length,
ref depthStencilClearValue
);
clearColorHandle.Free();
}
public void BindGraphicsPipeline(
GraphicsPipeline graphicsPipeline
) {
Refresh.Refresh_BindGraphicsPipeline(
Device.Handle,
Handle,
graphicsPipeline.Handle
);
}
public unsafe uint PushVertexShaderParams<T>(
T[] uniforms
) where T : unmanaged
{
fixed (T* ptr = &uniforms[0])
{
return Refresh.Refresh_PushVertexShaderParams(
Device.Handle,
Handle,
(IntPtr) ptr,
(uint) uniforms.Length
);
}
}
public unsafe uint PushFragmentShaderParams<T>(
T[] uniforms
) where T : unmanaged
{
fixed (T* ptr = &uniforms[0])
{
return Refresh.Refresh_PushFragmentShaderParams(
Device.Handle,
Handle,
(IntPtr) ptr,
(uint) uniforms.Length
);
}
}
public void BindVertexBuffers(
uint firstBinding,
uint bindingCount,
Buffer[] buffers,
UInt64[] offsets
) {
var bufferHandle = GCHandle.Alloc(buffers, GCHandleType.Pinned);
var offsetHandle = GCHandle.Alloc(offsets, GCHandleType.Pinned);
Refresh.Refresh_BindVertexBuffers(
Device.Handle,
Handle,
firstBinding,
bindingCount,
bufferHandle.AddrOfPinnedObject(),
offsetHandle.AddrOfPinnedObject()
);
bufferHandle.Free();
offsetHandle.Free();
}
public void BindIndexBuffer(
Buffer indexBuffer,
uint offset,
Refresh.IndexElementSize indexElementSize
) {
Refresh.Refresh_BindIndexBuffer(
Device.Handle,
Handle,
indexBuffer.Handle,
offset,
indexElementSize
);
}
public void BindFragmentSamplers(
Texture[] textures,
Sampler[] samplers
) {
var textureHandle = GCHandle.Alloc(textures, GCHandleType.Pinned);
var samplerHandle = GCHandle.Alloc(samplers, GCHandleType.Pinned);
Refresh.Refresh_BindFragmentSamplers(
Device.Handle,
Handle,
textureHandle.AddrOfPinnedObject(),
samplerHandle.AddrOfPinnedObject()
);
textureHandle.Free();
samplerHandle.Free();
}
public void DrawPrimitives(
uint vertexStart,
uint primitiveCount,
uint vertexParamOffset,
uint fragmentParamOffset
) {
Refresh.Refresh_DrawPrimitives(
Device.Handle,
Handle,
vertexStart,
primitiveCount,
vertexParamOffset,
fragmentParamOffset
);
}
public void EndRenderPass()
{
Refresh.Refresh_EndRenderPass(
Device.Handle,
Handle
);
}
public void QueuePresent(ref TextureSlice textureSlice, ref Refresh.Rect rectangle, Refresh.Filter filter)
{
var refreshTextureSlice = textureSlice.ToRefreshTextureSlice();
Refresh.Refresh_QueuePresent(
Device.Handle,
Handle,
ref refreshTextureSlice,
ref rectangle,
filter
);
}
}
}

10
src/Conversions.cs Normal file
View File

@ -0,0 +1,10 @@
namespace Campari
{
public static class Conversions
{
public static byte BoolToByte(bool b)
{
return (byte)(b ? 1 : 0);
}
}
}

View File

@ -3,7 +3,7 @@ using RefreshCS;
namespace Campari
{
class DepthStencilTarget : GraphicsResource
public class DepthStencilTarget : GraphicsResource
{
public uint Width { get; }
public uint Height { get; }

51
src/Framebuffer.cs Normal file
View File

@ -0,0 +1,51 @@
using System;
using RefreshCS;
namespace Campari
{
public class Framebuffer : GraphicsResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyFramebuffer;
public unsafe Framebuffer(
RefreshDevice device,
uint width,
uint height,
RenderPass renderPass,
DepthStencilTarget depthStencilTarget, /* can be NULL */
params ColorTarget[] colorTargets
) : base(device)
{
IntPtr[] colorTargetHandles = new IntPtr[colorTargets.Length];
for (var i = 0; i < colorTargets.Length; i += 1)
{
colorTargetHandles[i] = colorTargets[i].Handle;
}
IntPtr depthStencilTargetHandle;
if (depthStencilTarget == null)
{
depthStencilTargetHandle = IntPtr.Zero;
}
else
{
depthStencilTargetHandle = depthStencilTarget.Handle;
}
fixed (IntPtr* colorTargetHandlesPtr = colorTargetHandles)
{
Refresh.FramebufferCreateInfo framebufferCreateInfo = new Refresh.FramebufferCreateInfo
{
width = width,
height = height,
colorTargetCount = (uint) colorTargets.Length,
pColorTargets = (IntPtr) colorTargetHandlesPtr,
depthStencilTarget = depthStencilTargetHandle,
renderPass = renderPass.Handle
};
Handle = Refresh.Refresh_CreateFramebuffer(device.Handle, ref framebufferCreateInfo);
}
}
}
}

99
src/GraphicsPipeline.cs Normal file
View File

@ -0,0 +1,99 @@
using System;
using System.Runtime.InteropServices;
using RefreshCS;
namespace Campari
{
public class GraphicsPipeline : GraphicsResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyGraphicsPipeline;
public unsafe GraphicsPipeline(
RefreshDevice device,
ColorBlendState colorBlendState,
DepthStencilState depthStencilState,
ShaderStageState fragmentShaderState,
ShaderStageState vertexShaderState,
MultisampleState multisampleState,
GraphicsPipelineLayoutCreateInfo pipelineLayoutCreateInfo,
RasterizerState rasterizerState,
Refresh.PrimitiveType primitiveType,
VertexInputState vertexInputState,
ViewportState viewportState,
RenderPass renderPass
) : base(device)
{
var blendStateHandle = GCHandle.Alloc(colorBlendState.ColorTargetBlendStates, GCHandleType.Pinned);
var vertexAttributesHandle = GCHandle.Alloc(vertexInputState.VertexAttributes, GCHandleType.Pinned);
var vertexBindingsHandle = GCHandle.Alloc(vertexInputState.VertexBindings, GCHandleType.Pinned);
var viewportHandle = GCHandle.Alloc(viewportState.Viewports, GCHandleType.Pinned);
var scissorHandle = GCHandle.Alloc(viewportState.Scissors, GCHandleType.Pinned);
Refresh.GraphicsPipelineCreateInfo graphicsPipelineCreateInfo;
graphicsPipelineCreateInfo.colorBlendState.logicOpEnable = Conversions.BoolToByte(colorBlendState.LogicOpEnable);
graphicsPipelineCreateInfo.colorBlendState.logicOp = colorBlendState.LogicOp;
graphicsPipelineCreateInfo.colorBlendState.blendStates = blendStateHandle.AddrOfPinnedObject();
graphicsPipelineCreateInfo.colorBlendState.blendStateCount = colorBlendState.BlendStateCount;
graphicsPipelineCreateInfo.colorBlendState.blendConstants[0] = colorBlendState.BlendConstants.R;
graphicsPipelineCreateInfo.colorBlendState.blendConstants[1] = colorBlendState.BlendConstants.G;
graphicsPipelineCreateInfo.colorBlendState.blendConstants[2] = colorBlendState.BlendConstants.B;
graphicsPipelineCreateInfo.colorBlendState.blendConstants[3] = colorBlendState.BlendConstants.A;
graphicsPipelineCreateInfo.depthStencilState.backStencilState = depthStencilState.BackStencilState;
graphicsPipelineCreateInfo.depthStencilState.compareOp = depthStencilState.CompareOp;
graphicsPipelineCreateInfo.depthStencilState.depthBoundsTestEnable = Conversions.BoolToByte(depthStencilState.DepthBoundsTestEnable);
graphicsPipelineCreateInfo.depthStencilState.depthTestEnable = Conversions.BoolToByte(depthStencilState.DepthTestEnable);
graphicsPipelineCreateInfo.depthStencilState.depthWriteEnable = Conversions.BoolToByte(depthStencilState.DepthWriteEnable);
graphicsPipelineCreateInfo.depthStencilState.frontStencilState = depthStencilState.FrontStencilState;
graphicsPipelineCreateInfo.depthStencilState.maxDepthBounds = depthStencilState.MaxDepthBounds;
graphicsPipelineCreateInfo.depthStencilState.minDepthBounds = depthStencilState.MinDepthBounds;
graphicsPipelineCreateInfo.depthStencilState.stencilTestEnable = Conversions.BoolToByte(depthStencilState.StencilTestEnable);
graphicsPipelineCreateInfo.vertexShaderState.entryPointName = vertexShaderState.EntryPointName;
graphicsPipelineCreateInfo.vertexShaderState.shaderModule = vertexShaderState.ShaderModule.Handle;
graphicsPipelineCreateInfo.vertexShaderState.uniformBufferSize = vertexShaderState.UniformBufferSize;
graphicsPipelineCreateInfo.fragmentShaderState.entryPointName = fragmentShaderState.EntryPointName;
graphicsPipelineCreateInfo.fragmentShaderState.shaderModule = fragmentShaderState.ShaderModule.Handle;
graphicsPipelineCreateInfo.fragmentShaderState.uniformBufferSize = fragmentShaderState.UniformBufferSize;
graphicsPipelineCreateInfo.multisampleState.multisampleCount = multisampleState.MultisampleCount;
graphicsPipelineCreateInfo.multisampleState.sampleMask = multisampleState.SampleMask;
graphicsPipelineCreateInfo.pipelineLayoutCreateInfo.vertexSamplerBindingCount = pipelineLayoutCreateInfo.VertexSamplerBindingCount;
graphicsPipelineCreateInfo.pipelineLayoutCreateInfo.fragmentSamplerBindingCount = pipelineLayoutCreateInfo.FragmentSamplerBindingCount;
graphicsPipelineCreateInfo.rasterizerState.cullMode = rasterizerState.CullMode;
graphicsPipelineCreateInfo.rasterizerState.depthBiasClamp = rasterizerState.DepthBiasClamp;
graphicsPipelineCreateInfo.rasterizerState.depthBiasConstantFactor = rasterizerState.DepthBiasConstantFactor;
graphicsPipelineCreateInfo.rasterizerState.depthBiasEnable = Conversions.BoolToByte(rasterizerState.DepthBiasEnable);
graphicsPipelineCreateInfo.rasterizerState.depthBiasSlopeFactor = rasterizerState.DepthBiasSlopeFactor;
graphicsPipelineCreateInfo.rasterizerState.depthClampEnable = Conversions.BoolToByte(rasterizerState.DepthClampEnable);
graphicsPipelineCreateInfo.rasterizerState.fillMode = rasterizerState.FillMode;
graphicsPipelineCreateInfo.rasterizerState.frontFace = rasterizerState.FrontFace;
graphicsPipelineCreateInfo.rasterizerState.lineWidth = rasterizerState.LineWidth;
graphicsPipelineCreateInfo.vertexInputState.vertexAttributes = vertexAttributesHandle.AddrOfPinnedObject();
graphicsPipelineCreateInfo.vertexInputState.vertexAttributeCount = vertexInputState.VertexAttributeCount;
graphicsPipelineCreateInfo.vertexInputState.vertexBindings = vertexBindingsHandle.AddrOfPinnedObject();
graphicsPipelineCreateInfo.vertexInputState.vertexBindingCount = vertexInputState.VertexBindingCount;
graphicsPipelineCreateInfo.viewportState.viewports = viewportHandle.AddrOfPinnedObject();
graphicsPipelineCreateInfo.viewportState.viewportCount = viewportState.ViewportCount;
graphicsPipelineCreateInfo.viewportState.scissors = scissorHandle.AddrOfPinnedObject();
graphicsPipelineCreateInfo.viewportState.scissorCount = viewportState.ScissorCount;
graphicsPipelineCreateInfo.primitiveType = primitiveType;
graphicsPipelineCreateInfo.renderPass = renderPass.Handle;
Handle = Refresh.Refresh_CreateGraphicsPipeline(device.Handle, ref graphicsPipelineCreateInfo);
blendStateHandle.Free();
vertexAttributesHandle.Free();
vertexBindingsHandle.Free();
viewportHandle.Free();
scissorHandle.Free();
}
}
}

View File

@ -1,18 +0,0 @@
namespace Campari
{
public struct Rectangle
{
public int X { get; }
public int Y { get; }
public int W { get; }
public int H { get; }
public Rectangle(int x, int y, int w, int h)
{
X = x;
Y = y;
W = w;
H = h;
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using RefreshCS;
@ -16,11 +17,31 @@ namespace Campari
bool debugMode
) {
Handle = Refresh.Refresh_CreateDevice(
ref presentationParameters,
ref presentationParameters,
(byte) (debugMode ? 1 : 0)
);
}
/* FIXME: pool this */
public CommandBuffer AcquireCommandBuffer()
{
var commandBufferHandle = Refresh.Refresh_AcquireCommandBuffer(Handle, 0);
return new CommandBuffer(this, commandBufferHandle);
}
public void Submit(CommandBuffer[] commandBuffers)
{
var commandBufferHandle = GCHandle.Alloc(commandBuffers, GCHandleType.Pinned);
Refresh.Refresh_Submit(
Handle,
(uint) commandBuffers.Length,
commandBufferHandle.AddrOfPinnedObject()
);
commandBufferHandle.Free();
}
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)

View File

@ -7,7 +7,10 @@ namespace Campari
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyRenderPass;
public unsafe RenderPass(RefreshDevice device, params Refresh.ColorTargetDescription[] colorTargetDescriptions) : base(device)
public unsafe RenderPass(
RefreshDevice device,
params Refresh.ColorTargetDescription[] colorTargetDescriptions
) : base(device)
{
fixed (Refresh.ColorTargetDescription* ptr = colorTargetDescriptions)
{

18
src/Sampler.cs Normal file
View File

@ -0,0 +1,18 @@
using System;
using RefreshCS;
namespace Campari
{
public class Sampler : GraphicsResource
{
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroySampler;
public Sampler(
RefreshDevice device,
ref Refresh.SamplerStateCreateInfo samplerStateCreateInfo
) : base(device)
{
Handle = Refresh.Refresh_CreateSampler(device.Handle, ref samplerStateCreateInfo);
}
}
}

View File

@ -0,0 +1,13 @@
using RefreshCS;
namespace Campari
{
public unsafe struct ColorBlendState
{
public bool LogicOpEnable;
public Refresh.LogicOp LogicOp;
public BlendConstants BlendConstants;
public uint BlendStateCount;
public Refresh.ColorTargetBlendState[] ColorTargetBlendStates;
}
}

View File

@ -0,0 +1,17 @@
using RefreshCS;
namespace Campari
{
public struct DepthStencilState
{
public bool DepthTestEnable;
public Refresh.StencilOpState BackStencilState;
public Refresh.StencilOpState FrontStencilState;
public Refresh.CompareOp CompareOp;
public bool DepthBoundsTestEnable;
public bool DepthWriteEnable;
public float MinDepthBounds;
public float MaxDepthBounds;
public bool StencilTestEnable;
}
}

View File

@ -0,0 +1,8 @@
namespace Campari
{
public struct GraphicsPipelineLayoutCreateInfo
{
public uint VertexSamplerBindingCount;
public uint FragmentSamplerBindingCount;
}
}

View File

@ -0,0 +1,10 @@
using RefreshCS;
namespace Campari
{
public struct MultisampleState
{
public Refresh.SampleCount MultisampleCount;
public uint SampleMask;
}
}

View File

@ -0,0 +1,17 @@
using RefreshCS;
namespace Campari
{
public struct RasterizerState
{
public Refresh.CullMode CullMode;
public float DepthBiasClamp;
public float DepthBiasConstantFactor;
public bool DepthBiasEnable;
public float DepthBiasSlopeFactor;
public bool DepthClampEnable;
public Refresh.FillMode FillMode;
public Refresh.FrontFace FrontFace;
public float LineWidth;
}
}

View File

@ -0,0 +1,9 @@
namespace Campari
{
public struct ShaderStageState
{
public ShaderModule ShaderModule;
public string EntryPointName;
public uint UniformBufferSize;
}
}

View File

@ -0,0 +1,10 @@
namespace Campari
{
public struct VertexInputState
{
public VertexBinding[] VertexBindings;
public uint VertexBindingCount;
public VertexAttribute[] VertexAttributes;
public uint VertexAttributeCount;
}
}

View File

@ -0,0 +1,12 @@
using RefreshCS;
namespace Campari
{
public struct ViewportState
{
public Viewport[] Viewports;
public uint ViewportCount;
public Refresh.Rect[] Scissors;
public uint ScissorCount;
}
}

View File

@ -1,9 +1,11 @@
namespace Campari
using RefreshCS;
namespace Campari
{
public struct TextureSlice
{
public Texture Texture { get; }
public Rectangle Rectangle { get; }
public Refresh.Rect Rectangle { get; }
public uint Depth { get; }
public uint Layer { get; }
public uint Level { get; }
@ -11,13 +13,19 @@
public TextureSlice(Texture texture)
{
Texture = texture;
Rectangle = new Rectangle(0, 0, (int) texture.Width, (int) texture.Height);
Rectangle = new Refresh.Rect
{
x = 0,
y = 0,
w = (int) texture.Width,
h = (int) texture.Height
};
Depth = 0;
Layer = 0;
Level = 0;
}
public TextureSlice(Texture texture, Rectangle rectangle, uint depth = 0, uint layer = 0, uint level = 0)
public TextureSlice(Texture texture, Refresh.Rect rectangle, uint depth = 0, uint layer = 0, uint level = 0)
{
Texture = texture;
Rectangle = rectangle;
@ -31,13 +39,7 @@
RefreshCS.Refresh.TextureSlice textureSlice = new RefreshCS.Refresh.TextureSlice
{
texture = Texture.Handle,
rectangle = new RefreshCS.Refresh.Rect
{
x = Rectangle.X,
y = Rectangle.Y,
w = Rectangle.W,
h = Rectangle.H
},
rectangle = Rectangle,
depth = Depth,
layer = Layer,
level = Level

12
src/VertexAttribute.cs Normal file
View File

@ -0,0 +1,12 @@
using RefreshCS;
namespace Campari
{
public struct VertexAttribute
{
public uint Binding;
public uint Location;
public Refresh.VertexElementFormat Format;
public uint Offset;
}
}

11
src/VertexBinding.cs Normal file
View File

@ -0,0 +1,11 @@
using RefreshCS;
namespace Campari
{
public struct VertexBinding
{
public uint Binding;
public Refresh.VertexInputRate VertexInputRate;
public uint Stride;
}
}

12
src/Viewport.cs Normal file
View File

@ -0,0 +1,12 @@
namespace Campari
{
public struct Viewport
{
public float X;
public float Y;
public float W;
public float H;
public float MinDepth;
public float MaxDepth;
}
}