render a triangle in vulkan in csharp

To render a triangle in Vulkan using C#, you can follow these steps:

  1. Initialize the Vulkan instance.
main.cs
// Create a new Vulkan instance.
VkInstance instance = new VkInstance();
73 chars
3 lines
  1. Initialize a physical device.
main.cs
// Get the physical device.
VkPhysicalDevice[] physical_devices = instance.EnumeratePhysicalDevices();
VkPhysicalDevice physical_device = physical_devices[0];

// Get the device properties.
VkPhysicalDeviceProperties device_properties = physical_device.GetProperties();
270 chars
7 lines
  1. Create a logical device.
main.cs
// Create the device interface.
VkDevice device = physical_device.CreateDevice();

// Get the device queue.
VkQueue queue = device.GetQueue(0, 0);

// Select a graphics queue family.
int queue_family_index = 0;

VkDeviceQueueCreateInfo queue_create_info = new VkDeviceQueueCreateInfo
{
    sType = VkStructureType.StructureTypeDeviceQueueCreateInfo,
    queueFamilyIndex = queue_family_index,
    queueCount = 1,
    pQueuePriorities = new float[] { 1.0f },
};

// Create the logical device.
VkDevice device = physical_device.CreateDevice(queue_create_infos);
560 chars
20 lines
  1. Create a swap chain.
main.cs
// Create a swap chain.
VkSwapchainKHR swap_chain = device.CreateSwapchainKHR();

// Get the swap chain images.
VkImage[] swap_chain_images = device.GetSwapchainImagesKHR(swap_chain);

// Get the swap chain image format.
VkFormat swap_chain_image_format = device.GetSwapchainImageFormatKHR(swap_chain);

// Get the swap chain extent.
VkExtent2D swap_chain_extent = device.GetSwapchainExtentKHR(swap_chain);
407 chars
12 lines
  1. Create a render pass.
main.cs
// Create the color attachment.
VkAttachmentDescription color_attachment = new VkAttachmentDescription
{
    format = swap_chain_image_format,
    samples = VkSampleCountFlags.SampleCountFlags1,
    loadOp = VkAttachmentLoadOp.Clear,
    storeOp = VkAttachmentStoreOp.Store,
    stencilLoadOp = VkAttachmentLoadOp.DontCare,
    stencilStoreOp = VkAttachmentStoreOp.DontCare,
    initialLayout = VkImageLayout.Undefined,
    finalLayout = VkImageLayout.PresentSrcKHR,
};

// Create the attachment reference.
VkAttachmentReference color_attachment_reference = new VkAttachmentReference
{
    attachment = 0,
    layout = VkImageLayout.ColorAttachmentOptimal,
};

// Create the subpass.
VkSubpassDescription subpass = new VkSubpassDescription
{
    pipelineBindPoint = VkPipelineBindPoint.Graphics,
    colorAttachmentCount = 1,
    pColorAttachments = &color_attachment_reference,
};

// Create the render pass.
VkRenderPass render_pass = device.CreateRenderPass(subpass);
971 chars
31 lines
  1. Create a vertex buffer.
main.cs
// Define the triangle vertices.
float[] triangle_vertices = new float[]
{
    0.0f,  1.0f, 0.0f,
   -1.0f, -1.0f, 0.0f,
    1.0f, -1.0f, 0.0f,
};

// Create the vertex buffer.
VkBufferCreateInfo vertex_buffer_create_info = new VkBufferCreateInfo
{
    sType = VkStructureType.StructureTypeBufferCreateInfo,
    size = sizeof(float) * 9,
    usage = VkBufferUsageFlags.BufferUsageVertexBufferBit,
};

VkBuffer vertex_buffer = device.CreateBuffer(vertex_buffer_create_info);

// Allocate memory for the vertex buffer.
VkMemoryRequirements vertex_buffer_requirements = vertex_buffer.GetMemoryRequirements();

int vertex_buffer_index = physical_device.FindMemoryType(
    vertex_buffer_requirements.memoryTypeBits,
    VkMemoryPropertyFlags.MemoryPropertyHostVisibleBit | VkMemoryPropertyFlags.MemoryPropertyHostCoherentBit);

VkMemoryAllocateInfo vertex_buffer_allocate_info = new VkMemoryAllocateInfo
{
    sType = VkStructureType.StructureTypeMemoryAllocateInfo,
    allocationSize = vertex_buffer_requirements.size,
    memoryTypeIndex = vertex_buffer_index,
};

VkDeviceMemory vertex_buffer_memory = device.AllocateMemory(vertex_buffer_allocate_info);

// Bind the vertex buffer memory.
device.BindBufferMemory(vertex_buffer, vertex_buffer_memory);

// Copy the triangle vertices to the vertex buffer.
IntPtr vertex_buffer_data_ptr = device.MapMemory(vertex_buffer_memory, 0, vertex_buffer_create_info.size, VkMemoryMapFlags.MemoryMapWriteBit);
var vertex_buffer_data = new float[] { 0.0f,  1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, };
Marshal.Copy(vertex_buffer_data, 0, vertex_buffer_data_ptr, vertex_buffer_create_info.size);
device.UnmapMemory(vertex_buffer_memory);
1682 chars
43 lines
  1. Create a graphics pipeline.
main.cs
// Define the vertex input state.
VkVertexInputBindingDescription vertex_binding_description = new VkVertexInputBindingDescription
{
    binding = 0,
    stride = sizeof(float) * 3,
    inputRate = VkVertexInputRate.VertexInputRateVertex,
};

VkVertexInputAttributeDescription[] vertex_attribute_descriptions = new VkVertexInputAttributeDescription[]
{
    new VkVertexInputAttributeDescription
    {
        location = 0,
        binding = 0,
        format = VkFormat.FormatR32G32B32Sfloat,
        offset = 0,
    },
};

VkPipelineVertexInputStateCreateInfo vertex_input_state_create_info = new VkPipelineVertexInputStateCreateInfo
{
    sType = VkStructureType.StructureTypePipelineVertexInputStateCreateInfo,
    vertexBindingDescriptionCount = 1,
    pVertexBindingDescriptions = &vertex_binding_description,
    vertexAttributeDescriptionCount = 1,
    pVertexAttributeDescriptions = vertex_attribute_descriptions,
};

// Define the input assembly state.
VkPipelineInputAssemblyStateCreateInfo input_assembly_state_create_info = new VkPipelineInputAssemblyStateCreateInfo
{
    sType = VkStructureType.StructureTypePipelineInputAssemblyStateCreateInfo,
    topology = VkPrimitiveTopology.PrimitiveTopologyTriangleList,
    primitiveRestartEnable = false,
};

// Define the viewport state.
VkPipelineViewportStateCreateInfo viewport_state_create_info = new VkPipelineViewportStateCreateInfo
{
    sType = VkStructureType.StructureTypePipelineViewportStateCreateInfo,
    viewportCount = 1,
    pViewports = new VkViewport[] { new VkViewport { x = 0, y = 0, width = swap_chain_extent.width, height = swap_chain_extent.height, minDepth = 0, maxDepth = 1 } },
    scissorCount = 1,
    pScissors = new VkRect2D[] { new VkRect2D { offset = new VkOffset2D { x = 0, y = 0 }, extent = swap_chain_extent } },
};

// Define the rasterization state.
VkPipelineRasterizationStateCreateInfo rasterization_state_create_info = new VkPipelineRasterizationStateCreateInfo
{
    sType = VkStructureType.StructureTypePipelineRasterizationStateCreateInfo,
    depthClampEnable = false,
    rasterizerDiscardEnable = false,
    polygonMode = VkPolygonMode.PolygonModeFill,
    lineWidth = 1.0f,
    cullMode = VkCullModeFlags.CullModeNone,
    frontFace = VkFrontFace.FrontFaceCounterClockwise,
    depthBiasEnable = false,
};

// Define the multisample state.
VkPipelineMultisampleStateCreateInfo multisample_state_create_info = new VkPipelineMultisampleStateCreateInfo
{
    sType = VkStructureType.StructureTypePipelineMultisampleStateCreateInfo,
    rasterizationSamples = VkSampleCountFlags.SampleCountFlags1,
    sampleShadingEnable = false,
};

// Define the color blend state.
VkPipelineColorBlendAttachmentState color_blend_attachment_state = new VkPipelineColorBlendAttachmentState
{
    colorWriteMask = VkColorComponentFlags.ColorComponentRBit | VkColorComponentFlags.ColorComponentGBit | VkColorComponentFlags.ColorComponentBBit,
    blendEnable = false,
};

VkPipelineColorBlendStateCreateInfo color_blend_state_create_info = new VkPipelineColorBlendStateCreateInfo
{
    sType = VkStructureType.StructureTypePipelineColorBlendStateCreateInfo,
    logicOpEnable = false,
    logicOp = VkLogicOp.LogicOpCopy,
    attachmentCount = 1,
    pAttachments = &color_blend_attachment_state,
};

// Define the pipeline layout.
VkPipelineLayoutCreateInfo pipeline_layout_create_info = new VkPipelineLayoutCreateInfo
{
    sType = VkStructureType.StructureTypePipelineLayoutCreateInfo,
};

VkPipelineLayout pipeline_layout = device.CreatePipelineLayout(pipeline_layout_create_info);

// Define the shader stages.
VkShaderModule vertex_shader_module = device.CreateShaderModule(vertex_shader_code);
VkShaderModule fragment_shader_module = device.CreateShaderModule(fragment_shader_code);

VkPipelineShaderStageCreateInfo vertex_shader_stage_create_info = new VkPipelineShaderStageCreateInfo
{
    sType = VkStructureType.StructureTypePipelineShaderStageCreateInfo,
    stage = VkShaderStageFlags.ShaderStageVertexBit,
    module = vertex_shader_module,
    pName = "main",
};

VkPipelineShaderStageCreateInfo fragment_shader_stage_create_info = new VkPipelineShaderStageCreateInfo
{
    sType = VkStructureType.StructureTypePipelineShaderStageCreateInfo,
    stage = VkShaderStageFlags.ShaderStageFragmentBit,
    module = fragment_shader_module,
    pName = "main",
};

VkPipelineShaderStageCreateInfo[] shader_stages_create_info = new VkPipelineShaderStageCreateInfo[]
{
    vertex_shader_stage_create_info,
    fragment_shader_stage_create_info,
};

// Create the graphics pipeline.
VkGraphicsPipelineCreateInfo graphics_pipeline_create_info = new VkGraphicsPipelineCreateInfo
{
    sType = VkStructureType.StructureTypeGraphicsPipelineCreateInfo,
    stageCount = (uint)shader_stages_create_info.Length,
    pStages = shader_stages_create_info,
    pVertexInputState = &vertex_input_state_create_info,
    pInputAssemblyState = &input_assembly_state_create_info,
    pViewportState = &viewport_state_create_info,
    pRasterizationState = &rasterization_state_create_info,
    pMultisampleState = &multisample_state_create_info,
    pColorBlendState = &color_blend_state_create_info,
    layout = pipeline_layout,
    renderPass = render_pass,
    subpass = 0,
};

VkPipeline graphics_pipeline = device.CreateGraphicsPipelines(VkPipelineCache.Null, new VkGraphicsPipelineCreateInfo[] { graphics_pipeline_create_info })[0];

// Destroy the shader modules.
device.DestroyShaderModule(vertex_shader_module);
device.DestroyShaderModule(fragment_shader_module);
5549 chars
140 lines
  1. Record commands into the command buffer.
main.cs
// Begin the command buffer.
VkCommandBuffer command_buffer = device.AllocateCommandBuffers(new VkCommandBufferAllocateInfo(VkCommandPool.Null, VkCommandBufferLevel.CommandBufferLevelPrimary, 1))[0];
VkCommandBufferBeginInfo command_buffer_begin_info = new VkCommandBufferBeginInfo(VkCommandBufferUsageFlags.CommandBufferUsageSimultaneousUseBit);
command_buffer.Begin(command_buffer_begin_info);

// Define the render pass parameters.
VkClearValue clear_color = new VkClearValue(new VkClearColorValue(0.0f, 0.0f, 0.0f, 0.0f));

VkRenderPassBeginInfo render_pass_begin_info = new VkRenderPassBeginInfo(render_pass, VkFramebuffer.Null, new VkRect2D(new VkOffset2D(0, 0), swap_chain_extent), clear_color);
command_buffer.CmdBeginRenderPass(render_pass_begin_info, VkSubpassContents.SubpassContentsInline);

// Bind the graphics pipeline.
command_buffer.CmdBindPipeline(VkPipelineBindPoint.PipelineBindPointGraphics, graphics_pipeline);

// Bind the vertex buffer.
VkDeviceSize vertex_buffer_offset = 0;
command_buffer.CmdBindVertexBuffers(0, new VkBuffer[] { vertex_buffer }, new VkDeviceSize[] { vertex_buffer_offset });

// Draw the triangle.
command_buffer.CmdDraw(3, 1, 0, 0);

// End the render pass.
command_buffer.CmdEndRenderPass();

// End the command buffer.
command_buffer.End();
1288 chars
27 lines
  1. Present the rendered image.
main.cs
// Submit the command buffer to the queue.
VkSemaphore image_available_semaphore = device.CreateSemaphore();
VkSemaphore render_finished_semaphore = device.CreateSemaphore();

VkPipelineStageFlags[] wait_dst_stage_mask = new VkPipelineStageFlags[] { VkPipelineStageFlags.PipelineStageColorAttachmentOutputBit };

VkSubmitInfo submit_info = new VkSubmitInfo
{
    sType = VkStructureType.StructureTypeSubmitInfo,
    waitSemaphoreCount = 1,
    pWaitSemaphores = new VkSemaphore[] { image_available_semaphore },
    pWaitDstStageMask = wait_dst_stage_mask,
    commandBufferCount = 1,
    pCommandBuffers = new VkCommandBuffer[] { command_buffer },
    signalSemaphoreCount = 1,
    pSignalSemaphores = new VkSemaphore[] { render_finished_semaphore },
};

queue.Submit(new VkSubmitInfo[] { submit_info });

// Present the rendered image.
VkPresentInfoKHR present_info = new VkPresentInfoKHR
{
    sType = VkStructureType.StructureTypePresentInfoKHR,
    waitSemaphoreCount = 1,
    pWaitSemaphores = new VkSemaphore[] { render_finished_semaphore },
    swapchainCount = 1,
    pSwapchains = new VkSwapchainKHR[] { swap_chain },
    pImageIndices = new uint[] { 0 },
};

queue.PresentKHR(present_info);
1201 chars
33 lines
  1. Clean up Vulkan resources.
main.cs
device.WaitIdle();

device.DestroySemaphore(image_available_semaphore);
device.DestroySemaphore(render_finished_semaphore);

device.DestroyPipelineLayout(pipeline_layout);
device.DestroyPipeline(graphics_pipeline);

device.FreeMemory(vertex_buffer_memory);
device.DestroyBuffer(vertex_buffer);

device.DestroyRenderPass(render_pass);

device.DestroySwapchainKHR(swap_chain);

device.Destroy();

instance.Destroy();
415 chars
19 lines

gistlibby LogSnag