draw a triangle with vulkan in csharp

Here is an example of how to draw a triangle with Vulkan in C# using the SharpVk library:

main.cs
using 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 chars
355 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