Here is an example of how to draw a triangle with Vulkan in C# using the SharpVk library:
main.csusing System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using SharpVk;
using SharpVk.Khronos;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Initializing Vulkan...");
var appInfo = new ApplicationInfo()
{
ApplicationName = "Triangle",
ApplicationVersion = new Version(1, 0, 0),
EngineName = "No Engine",
EngineVersion = new Version(1, 0, 0),
ApiVersion = new Version(1, 0, 0)
};
var instance = Vk.Instance.Create(
instanceCreateInfo: new InstanceCreateInfo()
{
ApplicationInfo = appInfo
});
var physicalDevice = instance.EnumeratePhysicalDevices().First();
var queueFamilyIndices = physicalDevice.GetQueueFamilyIndices();
var device = physicalDevice.CreateDevice(queueFamilyIndices);
var queue = device.GetQueue(queueFamilyIndices.Graphics, 0);
var memoryProperties = physicalDevice.GetMemoryProperties();
var vertexData = new[]
{
new Vertex() { Position = new Vector2(-0.5f, -0.5f), Color = new Vector3(1.0f, 0.0f, 0.0f) },
new Vertex() { Position = new Vector2(0.5f, -0.5f), Color = new Vector3(0.0f, 1.0f, 0.0f) },
new Vertex() { Position = new Vector2(0.0f, 0.5f), Color = new Vector3(0.0f, 0.0f, 1.0f) }
};
var vertexBufferSize = (ulong)Marshal.SizeOf(vertexData[0]) * (ulong)vertexData.Length;
var vertexBuffer = device.CreateBuffer(new BufferCreateInfo()
{
Size = vertexBufferSize,
Usage = BufferUsageFlags.VertexBuffer,
SharingMode = SharingMode.Exclusive
});
var vertexMemoryRequirements = device.GetBufferMemoryRequirements(vertexBuffer);
var vertexMemoryIndex = memoryProperties.GetMemoryTypeIndex(vertexMemoryRequirements.MemoryTypeBits, MemoryPropertyFlags.HostVisible | MemoryPropertyFlags.HostCoherent);
var vertexMemory = device.AllocateMemory(new MemoryAllocateInfo()
{
AllocationSize = vertexMemoryRequirements.Size,
MemoryTypeIndex = vertexMemoryIndex
});
var vertexDataPtr = vertexMemory.Map(0, vertexMemoryRequirements.Size);
Marshal.Copy(vertexData, 0, vertexDataPtr, (int)vertexBufferSize);
vertexMemory.Unmap();
device.BindBufferMemory(vertexBuffer, vertexMemory, 0);
var surface = instance.CreateWindowSurface(new Win32SurfaceCreateInfo()
{
Hwnd = IntPtr.Zero,
Hinstance = IntPtr.Zero
});
var swapChain = device.CreateSwapchain(surface, queueFamilyIndices.Graphics, Vk.Format.B8g8r8a8Unorm);
var swapChainImageCount = swapChain.GetImages().Length;
var swapChainExtent = swapChain.GetExtent();
var renderPass = device.CreateRenderPass(new RenderPassCreateInfo()
{
Attachments = new[]
{
new AttachmentDescription()
{
Format = Vk.Format.B8g8r8a8Unorm,
Samples = Vk.SampleCountFlags.Count1,
LoadOp = AttachmentLoadOp.Clear,
StoreOp = AttachmentStoreOp.Store,
StencilLoadOp = AttachmentLoadOp.DontCare,
StencilStoreOp = AttachmentStoreOp.DontCare,
InitialLayout = ImageLayout.Undefined,
FinalLayout = ImageLayout.PresentSrcKhr
}
},
Subpasses = new[]
{
new SubpassDescription()
{
ColorAttachments = new[]
{
new AttachmentReference()
{
Attachment = 0,
Layout = ImageLayout.ColorAttachmentOptimal
}
}
}
}
});
var pipelineLayout = device.CreatePipelineLayout(new PipelineLayoutCreateInfo());
var vertexShader = device.CreateShaderModule(Encoding.UTF8.GetBytes(VertexShaderSource));
var fragmentShader = device.CreateShaderModule(Encoding.UTF8.GetBytes(FragmentShaderSource));
var pipeline = device.CreateGraphicsPipeline(new GraphicsPipelineCreateInfo()
{
VertexInputState = new PipelineVertexInputStateCreateInfo()
{
VertexBindingDescriptions = new[]
{
new VertexInputBindingDescription
{
Binding = 0,
Stride = (uint)Marshal.SizeOf<Vertex>(),
InputRate = VertexInputRate.Vertex
}
},
VertexAttributeDescriptions = new[]
{
new VertexInputAttributeDescription
{
Binding = 0,
Location = 0,
Format = Format.R32g32Sfloat,
Offset = (uint)Marshal.OffsetOf<Vertex>("Position")
},
new VertexInputAttributeDescription
{
Binding = 0,
Location = 1,
Format = Format.R32g32b32Sfloat,
Offset = (uint)Marshal.OffsetOf<Vertex>("Color")
}
}
},
InputAssemblyState = new PipelineInputAssemblyStateCreateInfo()
{
Topology = PrimitiveTopology.TriangleList,
PrimitiveRestartEnable = false
},
ViewportState = new PipelineViewportStateCreateInfo()
{
Viewports = new[]
{
new Viewport()
{
X = 0,
Y = 0,
Width = swapChainExtent.Width,
Height = swapChainExtent.Height,
MinDepth = 0,
MaxDepth = 1
}
},
Scissors = new[]
{
new Rect2D()
{
Offset = new Offset2D()
{
X = 0,
Y = 0
},
Extent = swapChainExtent
}
}
},
RasterizationState = new PipelineRasterizationStateCreateInfo()
{
DepthClampEnable = false,
RasterizerDiscardEnable = false,
PolygonMode = PolygonMode.Fill,
LineWidth = 1,
CullMode = CullModeFlags.None,
FrontFace = FrontFace.CounterClockwise,
DepthBiasEnable = false,
DepthBiasConstantFactor = 0,
DepthBiasClamp = 0,
DepthBiasSlopeFactor = 0
},
MultisampleState = new PipelineMultisampleStateCreateInfo()
{
RasterizationSamples = Vk.SampleCountFlags.Count1,
SampleShadingEnable = false,
MinSampleShading = 0,
SampleMask = null,
AlphaToOneEnable = false,
AlphaToCoverageEnable = false
},
ColorBlendState = new PipelineColorBlendStateCreateInfo()
{
Attachments = new[]
{
new PipelineColorBlendAttachmentState()
{
ColorWriteMask = ColorComponentFlags.R | ColorComponentFlags.G | ColorComponentFlags.B | ColorComponentFlags.A,
BlendEnable = false,
SrcColorBlendFactor = BlendFactor.One,
DstColorBlendFactor = BlendFactor.Zero,
ColorBlendOp = BlendOp.Add,
SrcAlphaBlendFactor = BlendFactor.One,
DstAlphaBlendFactor = BlendFactor.Zero,
AlphaBlendOp = BlendOp.Add
}
},
LogicOpEnable = false,
LogicOp = LogicOp.Copy,
BlendConstants = new[] { 0.0f, 0.0f, 0.0f, 0.0f }
},
DepthStencilState = new PipelineDepthStencilStateCreateInfo()
{
DepthTestEnable = false,
DepthWriteEnable = false,
DepthCompareOp = CompareOp.Never,
DepthBoundsTestEnable = false,
StencilTestEnable = false,
Back = new StencilOpState(),
Front = new StencilOpState()
},
Layout = pipelineLayout,
RenderPass = renderPass,
Subpass = 0,
VertexShader = vertexShader,
FragmentShader = fragmentShader,
DynamicState = new PipelineDynamicStateCreateInfo()
{
DynamicStates = new[]
{
DynamicState.Viewport,
DynamicState.Scissor
}
}
});
var vertexBufferView = new BufferView[]
{
new BufferView()
{
Buffer = vertexBuffer,
Format = Vk.Format.Undefined,
Offset = 0,
Range = vertexBufferSize
}
};
var commandPool = device.CreateCommandPool(queueFamilyIndices.Graphics);
var commandBuffers = device.AllocateCommandBuffers(new CommandBufferAllocateInfo()
{
CommandPool = commandPool,
Level = CommandBufferLevel.Primary,
CommandBufferCount = swapChainImageCount
});
var fence = device.CreateFence(false);
while (true)
{
var result = instance.WaitForEvents(TimeSpan.FromSeconds(1));
if (result == Vk.Result.Timeout)
{
continue;
}
break;
}
var currentBufferIndex = 0;
while (true)
{
var result = device.WaitForFences(fence, true, TimeSpan.FromSeconds(1));
if (result == Vk.Result.Timeout)
{
continue;
}
break;
}
device.ResetFences(fence);
var submitInfo = new SubmitInfo()
{
CommandBuffers = commandBuffers[currentBufferIndex]
};
queue.Submit(submitInfo, fence);
swapChain.Present(queue, currentBufferIndex);
Console.ReadKey();
}
struct Vertex
{
public Vector2 Position;
public Vector3 Color;
}
const string VertexShaderSource = @"
#version 450
layout (location = 0) in vec2 i_position;
layout (location = 1) in vec3 i_color;
out gl_PerVertex
{
vec4 gl_Position;
};
out vec3 v_color;
void main()
{
gl_Position = vec4(i_position, 0.0, 1.0);
v_color = i_color;
}
";
const string FragmentShaderSource = @"
#version 450
in vec3 v_color;
out vec4 o_color;
void main()
{
o_color = vec4(v_color, 1.0);
}
";
}
11567 chars355 lines
This program will draw a triangle with three different colored vertices. Note that vertex buffer data and shader code is embedded directly into the program. In a practical application, you would want to load these from files or other sources as appropriate.
gistlibby LogSnag