|
|
|
@@ -1,12 +1,17 @@ |
|
|
|
#include <png.h> |
|
|
|
#include <vulkan/vulkan.h> |
|
|
|
|
|
|
|
#include <fstream> |
|
|
|
#include <iostream> |
|
|
|
#include <sstream> |
|
|
|
#include <string> |
|
|
|
#include <vector> |
|
|
|
|
|
|
|
const uint32_t kWidth = 16; |
|
|
|
const uint32_t kHeight = 16; |
|
|
|
const VkFormat kVulkanFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32; |
|
|
|
const std::string kVertexShaderPath = "triangle.vert.spv"; |
|
|
|
const std::string kFragmentShaderPath = "triangle.frag.spv"; |
|
|
|
|
|
|
|
#define ERROR(message) \ |
|
|
|
std::cerr << message << std::endl; \ |
|
|
|
@@ -106,16 +111,223 @@ VkDevice CreateVkDevice(VkPhysicalDevice physical_device, uint32_t device_queue_ |
|
|
|
VkDevice device; |
|
|
|
VkResult result = vkCreateDevice(physical_device, &device_create_info, nullptr, &device); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create logical device"); |
|
|
|
ERROR("Unable to create logical device: " << result); |
|
|
|
} |
|
|
|
return device; |
|
|
|
} |
|
|
|
|
|
|
|
VkQueue GetVkQueue(VkDevice device, uint32_t device_queue_family_index) { |
|
|
|
VkQueue queue; |
|
|
|
vkGetDeviceQueue(device, device_queue_family_index, /*queueIndex*/ 0, &queue); |
|
|
|
return queue; |
|
|
|
} |
|
|
|
|
|
|
|
VkCommandPool CreateVkCommandPool(VkDevice device, uint32_t device_queue_family_index) { |
|
|
|
VkCommandPool command_pool; |
|
|
|
VkCommandPoolCreateInfo create_info = {}; |
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
|
|
|
create_info.flags = 0; |
|
|
|
create_info.queueFamilyIndex = device_queue_family_index; |
|
|
|
VkResult result = vkCreateCommandPool(device, &create_info, nullptr, &command_pool); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create command pool: " << result); |
|
|
|
} |
|
|
|
return command_pool; |
|
|
|
} |
|
|
|
|
|
|
|
VkRenderPass CreateVkRenderPass(VkDevice device) { |
|
|
|
VkAttachmentDescription attachment_description = {}; |
|
|
|
attachment_description.format = kVulkanFormat; |
|
|
|
attachment_description.samples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
|
|
|
attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
|
|
|
attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
|
|
|
attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
|
|
|
attachment_description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
|
|
|
attachment_description.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
|
|
|
|
|
|
|
VkAttachmentReference attachment_reference = {}; |
|
|
|
attachment_reference.attachment = 0; |
|
|
|
attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
|
|
|
|
|
|
|
VkSubpassDescription subpass_description = {}; |
|
|
|
subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
|
|
|
subpass_description.colorAttachmentCount = 1; |
|
|
|
subpass_description.pColorAttachments = &attachment_reference; |
|
|
|
|
|
|
|
VkSubpassDependency subpass_dependency = {}; |
|
|
|
subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL; |
|
|
|
subpass_dependency.dstSubpass = 0; |
|
|
|
subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
|
|
|
subpass_dependency.srcAccessMask = 0; |
|
|
|
subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
|
|
|
subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | |
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
|
|
|
|
|
|
|
VkRenderPassCreateInfo render_pass_info = {}; |
|
|
|
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
|
|
|
render_pass_info.attachmentCount = 1; |
|
|
|
render_pass_info.pAttachments = &attachment_description; |
|
|
|
render_pass_info.subpassCount = 1; |
|
|
|
render_pass_info.pSubpasses = &subpass_description; |
|
|
|
render_pass_info.dependencyCount = 1; |
|
|
|
render_pass_info.pDependencies = &subpass_dependency; |
|
|
|
|
|
|
|
VkRenderPass render_pass; |
|
|
|
VkResult result = vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create render pass: " << result); |
|
|
|
} |
|
|
|
return render_pass; |
|
|
|
} |
|
|
|
|
|
|
|
VkShaderModule CreateVkShaderModule(VkDevice device, std::string path) { |
|
|
|
std::ifstream fstream(path); |
|
|
|
if (!fstream) { |
|
|
|
ERROR("Unable to open: " << path); |
|
|
|
} |
|
|
|
std::stringstream buffer; |
|
|
|
buffer << fstream.rdbuf(); |
|
|
|
std::string spirv_source = buffer.str(); |
|
|
|
|
|
|
|
VkShaderModuleCreateInfo create_info = {}; |
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
|
|
|
create_info.codeSize = spirv_source.length(); |
|
|
|
create_info.pCode = (const uint32_t*)spirv_source.c_str(); |
|
|
|
|
|
|
|
VkShaderModule shader_module; |
|
|
|
VkResult result = vkCreateShaderModule(device, &create_info, nullptr, &shader_module); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create shader module for " << path << ": "); |
|
|
|
} |
|
|
|
return shader_module; |
|
|
|
} |
|
|
|
|
|
|
|
VkPipeline CreateVkPipeline(VkDevice device, VkRenderPass render_pass) { |
|
|
|
VkShaderModule vertex_shader_module = CreateVkShaderModule(device, kVertexShaderPath); |
|
|
|
VkShaderModule fragment_shader_module = CreateVkShaderModule(device, kFragmentShaderPath); |
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo vertex_shader_stage_info = {}; |
|
|
|
vertex_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
|
|
|
vertex_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; |
|
|
|
vertex_shader_stage_info.module = vertex_shader_module; |
|
|
|
vertex_shader_stage_info.pName = "main"; |
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo fragment_shader_stage_info = {}; |
|
|
|
fragment_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
|
|
|
fragment_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; |
|
|
|
fragment_shader_stage_info.module = fragment_shader_module; |
|
|
|
fragment_shader_stage_info.pName = "main"; |
|
|
|
|
|
|
|
std::vector<VkPipelineShaderStageCreateInfo> shader_stages = |
|
|
|
{vertex_shader_stage_info, fragment_shader_stage_info}; |
|
|
|
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; |
|
|
|
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
|
|
|
vertex_input_info.vertexBindingDescriptionCount = 0; |
|
|
|
vertex_input_info.pVertexBindingDescriptions = nullptr; |
|
|
|
vertex_input_info.vertexAttributeDescriptionCount = 0; |
|
|
|
vertex_input_info.pVertexAttributeDescriptions = nullptr; |
|
|
|
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {}; |
|
|
|
input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
|
|
|
input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
|
|
|
input_assembly_info.primitiveRestartEnable = VK_FALSE; |
|
|
|
|
|
|
|
VkViewport viewport = {}; |
|
|
|
viewport.x = 0.0f; |
|
|
|
viewport.y = 0.0f; |
|
|
|
viewport.width = (float)kWidth; |
|
|
|
viewport.height = (float)kHeight; |
|
|
|
viewport.minDepth = 0.0f; |
|
|
|
viewport.maxDepth = 1.0f; |
|
|
|
|
|
|
|
VkExtent2D extent = {}; |
|
|
|
extent.width = kWidth; |
|
|
|
extent.height = kHeight; |
|
|
|
VkRect2D scissor = {}; |
|
|
|
scissor.offset = {0, 0}; |
|
|
|
scissor.extent = extent; |
|
|
|
|
|
|
|
VkPipelineViewportStateCreateInfo viewport_state_info = {}; |
|
|
|
viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
|
|
|
viewport_state_info.viewportCount = 1; |
|
|
|
viewport_state_info.pViewports = &viewport; |
|
|
|
viewport_state_info.scissorCount = 1; |
|
|
|
viewport_state_info.pScissors = &scissor; |
|
|
|
|
|
|
|
VkPipelineRasterizationStateCreateInfo rasterization_state_info = {}; |
|
|
|
rasterization_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
|
|
|
rasterization_state_info.depthClampEnable = VK_FALSE; |
|
|
|
rasterization_state_info.rasterizerDiscardEnable = VK_FALSE; |
|
|
|
rasterization_state_info.polygonMode = VK_POLYGON_MODE_FILL; |
|
|
|
rasterization_state_info.lineWidth = 1.0f; |
|
|
|
rasterization_state_info.cullMode = VK_CULL_MODE_BACK_BIT; |
|
|
|
rasterization_state_info.frontFace = VK_FRONT_FACE_CLOCKWISE; |
|
|
|
rasterization_state_info.depthBiasEnable = VK_FALSE; |
|
|
|
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisampling_state_info = {}; |
|
|
|
multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
|
|
|
multisampling_state_info.sampleShadingEnable = VK_FALSE; |
|
|
|
multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
|
|
|
|
VkPipelineColorBlendAttachmentState color_blend_attachment_state = {}; |
|
|
|
color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | |
|
|
|
VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; |
|
|
|
color_blend_attachment_state.blendEnable = VK_FALSE; |
|
|
|
|
|
|
|
VkPipelineColorBlendStateCreateInfo color_blend_state = {}; |
|
|
|
color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
|
|
|
color_blend_state.logicOpEnable = VK_FALSE; |
|
|
|
color_blend_state.attachmentCount = 1; |
|
|
|
color_blend_state.pAttachments = &color_blend_attachment_state; |
|
|
|
|
|
|
|
VkPipelineLayoutCreateInfo pipeline_layout_create_info = {}; |
|
|
|
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
|
|
|
|
|
|
|
VkPipelineLayout pipeline_layout; |
|
|
|
VkResult result = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, |
|
|
|
&pipeline_layout); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create VkPipelineLayout: " << result); |
|
|
|
} |
|
|
|
|
|
|
|
VkGraphicsPipelineCreateInfo pipeline_create_info = {}; |
|
|
|
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; |
|
|
|
pipeline_create_info.stageCount = 2; |
|
|
|
pipeline_create_info.pStages = shader_stages.data(); |
|
|
|
pipeline_create_info.pVertexInputState = &vertex_input_info; |
|
|
|
pipeline_create_info.pInputAssemblyState = &input_assembly_info; |
|
|
|
pipeline_create_info.pViewportState = &viewport_state_info; |
|
|
|
pipeline_create_info.pRasterizationState = &rasterization_state_info; |
|
|
|
pipeline_create_info.pMultisampleState = &multisampling_state_info; |
|
|
|
pipeline_create_info.pDepthStencilState = nullptr; |
|
|
|
pipeline_create_info.pColorBlendState = &color_blend_state; |
|
|
|
pipeline_create_info.pDynamicState = nullptr; |
|
|
|
pipeline_create_info.layout = pipeline_layout; |
|
|
|
pipeline_create_info.renderPass = render_pass; |
|
|
|
pipeline_create_info.subpass = 0; |
|
|
|
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; |
|
|
|
pipeline_create_info.basePipelineIndex = -1; |
|
|
|
|
|
|
|
VkPipeline pipeline; |
|
|
|
result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr, |
|
|
|
&pipeline); |
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
ERROR("Unable to create VkPipeline: " << result); |
|
|
|
} |
|
|
|
return pipeline; |
|
|
|
} |
|
|
|
|
|
|
|
int main() { |
|
|
|
VkInstance instance = CreateVkInstance(); |
|
|
|
VkPhysicalDevice physical_device = ChooseVkPhysicalDevice(instance); |
|
|
|
uint32_t device_queue_family_index = ChooseDeviceQueueFamilyIndex(physical_device); |
|
|
|
VkDevice device = CreateVkDevice(physical_device, device_queue_family_index); |
|
|
|
VkQueue queue = GetVkQueue(device, device_queue_family_index); |
|
|
|
VkCommandPool command_pool = CreateVkCommandPool(device, device_queue_family_index); |
|
|
|
VkRenderPass render_pass = CreateVkRenderPass(device); |
|
|
|
VkPipeline pipeline = CreateVkPipeline(device, render_pass); |
|
|
|
|
|
|
|
// Example code for exporting a PNG. |
|
|
|
FILE *f = fopen("triangle.png", "wb"); |