Demo application that renders a triangle using Vulkan on the Pixel 3A.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

triangle.cc 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. #include <png.h>
  2. #include <vulkan/vulkan.h>
  3. #include <fstream>
  4. #include <iostream>
  5. #include <sstream>
  6. #include <string>
  7. #include <vector>
  8. const uint32_t kWidth = 256;
  9. const uint32_t kHeight = 256;
  10. const VkFormat kVulkanFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
  11. const std::string kVertexShaderPath = "triangle.vert.spv";
  12. const std::string kFragmentShaderPath = "triangle.frag.spv";
  13. #define ERROR(message) \
  14. std::cerr << message << std::endl; \
  15. std::exit(EXIT_FAILURE)
  16. VkInstance CreateVkInstance() {
  17. VkApplicationInfo app_info = {};
  18. app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
  19. app_info.apiVersion = VK_API_VERSION_1_1;
  20. VkInstanceCreateInfo create_info = {};
  21. create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
  22. create_info.pApplicationInfo = &app_info;
  23. create_info.enabledExtensionCount = 0;
  24. create_info.ppEnabledExtensionNames = nullptr;
  25. create_info.enabledLayerCount = 0;
  26. create_info.ppEnabledLayerNames = nullptr;
  27. VkInstance instance;
  28. VkResult result = vkCreateInstance(&create_info, nullptr, &instance);
  29. if (result != VK_SUCCESS) {
  30. ERROR("Error creating VkInstance: " << result);
  31. }
  32. return instance;
  33. }
  34. VkPhysicalDevice ChooseVkPhysicalDevice(VkInstance instance) {
  35. uint32_t device_count = 0;
  36. VkResult result = vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
  37. if (result != VK_SUCCESS) {
  38. ERROR("Error enumerating VkPhysicalDevices: " << result);
  39. }
  40. if (device_count == 0) {
  41. ERROR("No available VkPhysicalDevices");
  42. }
  43. std::vector<VkPhysicalDevice> devices(device_count);
  44. result = vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
  45. if (result != VK_SUCCESS) {
  46. ERROR("Error fetching VkPhysicalDevices: " << result);
  47. }
  48. std::cout << "Found " << device_count << " device(s):" << std::endl;
  49. VkPhysicalDevice chosen_device = VK_NULL_HANDLE;
  50. for (VkPhysicalDevice device : devices) {
  51. VkPhysicalDeviceProperties device_props;
  52. vkGetPhysicalDeviceProperties(device, &device_props);
  53. std::cout << "\t- " << device_props.deviceName << " [V: " <<
  54. VK_VERSION_MAJOR(device_props.apiVersion) << "." <<
  55. VK_VERSION_MINOR(device_props.apiVersion) << "." <<
  56. VK_VERSION_PATCH(device_props.apiVersion) << "]" << std::endl;
  57. // Currently, any device with Vulkan API version 1.1 is fine.
  58. if (chosen_device == VK_NULL_HANDLE && device_props.apiVersion >= VK_API_VERSION_1_1) {
  59. chosen_device = device;
  60. }
  61. }
  62. if (chosen_device == VK_NULL_HANDLE) {
  63. ERROR("Unable to find suitable VkPhysicalDevice");
  64. }
  65. return chosen_device;
  66. }
  67. uint32_t ChooseDeviceQueueFamilyIndex(VkPhysicalDevice physical_device) {
  68. uint32_t props_count;
  69. vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &props_count, nullptr);
  70. std::vector<VkQueueFamilyProperties> props(props_count);
  71. vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &props_count, props.data());
  72. // Simply choose the first graphics queue.
  73. for (size_t i = 0; i < props_count; i++) {
  74. const VkQueueFamilyProperties& prop = props[i];
  75. if ((prop.queueFlags & VK_QUEUE_GRAPHICS_BIT) && prop.queueCount > 0) {
  76. return i;
  77. }
  78. }
  79. ERROR("Unable to find suitable queue family");
  80. }
  81. VkDevice CreateVkDevice(VkPhysicalDevice physical_device, uint32_t device_queue_family_index) {
  82. VkDeviceQueueCreateInfo queue_create_info = {};
  83. queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
  84. queue_create_info.queueFamilyIndex = device_queue_family_index;
  85. queue_create_info.queueCount = 1;
  86. float queue_priority = 1.0f;
  87. queue_create_info.pQueuePriorities = &queue_priority;
  88. VkDeviceCreateInfo device_create_info = {};
  89. device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
  90. device_create_info.queueCreateInfoCount = 1;
  91. device_create_info.pQueueCreateInfos = &queue_create_info;
  92. // Let's not use any device extensions for now.
  93. device_create_info.enabledExtensionCount = 0;
  94. device_create_info.ppEnabledExtensionNames = nullptr;
  95. VkDevice device;
  96. VkResult result = vkCreateDevice(physical_device, &device_create_info, nullptr, &device);
  97. if (result != VK_SUCCESS) {
  98. ERROR("Unable to create logical device: " << result);
  99. }
  100. return device;
  101. }
  102. VkQueue GetVkQueue(VkDevice device, uint32_t device_queue_family_index) {
  103. VkQueue queue;
  104. vkGetDeviceQueue(device, device_queue_family_index, /*queueIndex*/ 0, &queue);
  105. return queue;
  106. }
  107. VkCommandPool CreateVkCommandPool(VkDevice device, uint32_t device_queue_family_index) {
  108. VkCommandPool command_pool;
  109. VkCommandPoolCreateInfo create_info = {};
  110. create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  111. create_info.flags = 0;
  112. create_info.queueFamilyIndex = device_queue_family_index;
  113. VkResult result = vkCreateCommandPool(device, &create_info, nullptr, &command_pool);
  114. if (result != VK_SUCCESS) {
  115. ERROR("Unable to create command pool: " << result);
  116. }
  117. return command_pool;
  118. }
  119. VkCommandBuffer CreateVkCommandBuffer(VkDevice device, VkCommandPool command_pool) {
  120. VkCommandBufferAllocateInfo allocate_info = {};
  121. allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  122. allocate_info.commandPool = command_pool;
  123. allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  124. allocate_info.commandBufferCount = 1;
  125. VkCommandBuffer command_buffer;
  126. VkResult result = vkAllocateCommandBuffers( device, &allocate_info, &command_buffer);
  127. if (result != VK_SUCCESS) {
  128. ERROR("Unable to create VkCommandBuffer: " << result);
  129. }
  130. return command_buffer;
  131. }
  132. VkRenderPass CreateVkRenderPass(VkDevice device) {
  133. VkAttachmentDescription attachment_description = {};
  134. attachment_description.format = kVulkanFormat;
  135. attachment_description.samples = VK_SAMPLE_COUNT_1_BIT;
  136. attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
  137. attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
  138. attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  139. attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
  140. attachment_description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  141. attachment_description.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
  142. VkAttachmentReference attachment_reference = {};
  143. attachment_reference.attachment = 0;
  144. attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
  145. VkSubpassDescription subpass_description = {};
  146. subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
  147. subpass_description.colorAttachmentCount = 1;
  148. subpass_description.pColorAttachments = &attachment_reference;
  149. VkSubpassDependency subpass_dependency = {};
  150. subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
  151. subpass_dependency.dstSubpass = 0;
  152. subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  153. subpass_dependency.srcAccessMask = 0;
  154. subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  155. subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
  156. VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  157. VkRenderPassCreateInfo render_pass_info = {};
  158. render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
  159. render_pass_info.attachmentCount = 1;
  160. render_pass_info.pAttachments = &attachment_description;
  161. render_pass_info.subpassCount = 1;
  162. render_pass_info.pSubpasses = &subpass_description;
  163. render_pass_info.dependencyCount = 1;
  164. render_pass_info.pDependencies = &subpass_dependency;
  165. VkRenderPass render_pass;
  166. VkResult result = vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass);
  167. if (result != VK_SUCCESS) {
  168. ERROR("Unable to create render pass: " << result);
  169. }
  170. return render_pass;
  171. }
  172. VkShaderModule CreateVkShaderModule(VkDevice device, std::string path) {
  173. std::ifstream fstream(path);
  174. if (!fstream) {
  175. ERROR("Unable to open: " << path);
  176. }
  177. std::stringstream buffer;
  178. buffer << fstream.rdbuf();
  179. std::string spirv_source = buffer.str();
  180. VkShaderModuleCreateInfo create_info = {};
  181. create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
  182. create_info.codeSize = spirv_source.length();
  183. create_info.pCode = (const uint32_t*)spirv_source.c_str();
  184. VkShaderModule shader_module;
  185. VkResult result = vkCreateShaderModule(device, &create_info, nullptr, &shader_module);
  186. if (result != VK_SUCCESS) {
  187. ERROR("Unable to create shader module for " << path << ": ");
  188. }
  189. return shader_module;
  190. }
  191. VkPipeline CreateVkPipeline(VkDevice device, VkRenderPass render_pass) {
  192. VkShaderModule vertex_shader_module = CreateVkShaderModule(device, kVertexShaderPath);
  193. VkShaderModule fragment_shader_module = CreateVkShaderModule(device, kFragmentShaderPath);
  194. VkPipelineShaderStageCreateInfo vertex_shader_stage_info = {};
  195. vertex_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  196. vertex_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
  197. vertex_shader_stage_info.module = vertex_shader_module;
  198. vertex_shader_stage_info.pName = "main";
  199. VkPipelineShaderStageCreateInfo fragment_shader_stage_info = {};
  200. fragment_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  201. fragment_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
  202. fragment_shader_stage_info.module = fragment_shader_module;
  203. fragment_shader_stage_info.pName = "main";
  204. std::vector<VkPipelineShaderStageCreateInfo> shader_stages =
  205. {vertex_shader_stage_info, fragment_shader_stage_info};
  206. VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
  207. vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
  208. vertex_input_info.vertexBindingDescriptionCount = 0;
  209. vertex_input_info.pVertexBindingDescriptions = nullptr;
  210. vertex_input_info.vertexAttributeDescriptionCount = 0;
  211. vertex_input_info.pVertexAttributeDescriptions = nullptr;
  212. VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {};
  213. input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
  214. input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
  215. input_assembly_info.primitiveRestartEnable = VK_FALSE;
  216. VkViewport viewport = {};
  217. viewport.x = 0.0f;
  218. viewport.y = 0.0f;
  219. viewport.width = (float)kWidth;
  220. viewport.height = (float)kHeight;
  221. viewport.minDepth = 0.0f;
  222. viewport.maxDepth = 1.0f;
  223. VkExtent2D extent = {};
  224. extent.width = kWidth;
  225. extent.height = kHeight;
  226. VkRect2D scissor = {};
  227. scissor.offset = {0, 0};
  228. scissor.extent = extent;
  229. VkPipelineViewportStateCreateInfo viewport_state_info = {};
  230. viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
  231. viewport_state_info.viewportCount = 1;
  232. viewport_state_info.pViewports = &viewport;
  233. viewport_state_info.scissorCount = 1;
  234. viewport_state_info.pScissors = &scissor;
  235. VkPipelineRasterizationStateCreateInfo rasterization_state_info = {};
  236. rasterization_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
  237. rasterization_state_info.depthClampEnable = VK_FALSE;
  238. rasterization_state_info.rasterizerDiscardEnable = VK_FALSE;
  239. rasterization_state_info.polygonMode = VK_POLYGON_MODE_FILL;
  240. rasterization_state_info.lineWidth = 1.0f;
  241. rasterization_state_info.cullMode = VK_CULL_MODE_BACK_BIT;
  242. rasterization_state_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
  243. rasterization_state_info.depthBiasEnable = VK_FALSE;
  244. VkPipelineMultisampleStateCreateInfo multisampling_state_info = {};
  245. multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
  246. multisampling_state_info.sampleShadingEnable = VK_FALSE;
  247. multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
  248. VkPipelineColorBlendAttachmentState color_blend_attachment_state = {};
  249. color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
  250. VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
  251. color_blend_attachment_state.blendEnable = VK_FALSE;
  252. VkPipelineColorBlendStateCreateInfo color_blend_state = {};
  253. color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
  254. color_blend_state.logicOpEnable = VK_FALSE;
  255. color_blend_state.attachmentCount = 1;
  256. color_blend_state.pAttachments = &color_blend_attachment_state;
  257. VkPipelineLayoutCreateInfo pipeline_layout_create_info = {};
  258. pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
  259. VkPipelineLayout pipeline_layout;
  260. VkResult result = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr,
  261. &pipeline_layout);
  262. if (result != VK_SUCCESS) {
  263. ERROR("Unable to create VkPipelineLayout: " << result);
  264. }
  265. VkGraphicsPipelineCreateInfo pipeline_create_info = {};
  266. pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
  267. pipeline_create_info.stageCount = 2;
  268. pipeline_create_info.pStages = shader_stages.data();
  269. pipeline_create_info.pVertexInputState = &vertex_input_info;
  270. pipeline_create_info.pInputAssemblyState = &input_assembly_info;
  271. pipeline_create_info.pViewportState = &viewport_state_info;
  272. pipeline_create_info.pRasterizationState = &rasterization_state_info;
  273. pipeline_create_info.pMultisampleState = &multisampling_state_info;
  274. pipeline_create_info.pDepthStencilState = nullptr;
  275. pipeline_create_info.pColorBlendState = &color_blend_state;
  276. pipeline_create_info.pDynamicState = nullptr;
  277. pipeline_create_info.layout = pipeline_layout;
  278. pipeline_create_info.renderPass = render_pass;
  279. pipeline_create_info.subpass = 0;
  280. pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
  281. pipeline_create_info.basePipelineIndex = -1;
  282. VkPipeline pipeline;
  283. result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr,
  284. &pipeline);
  285. if (result != VK_SUCCESS) {
  286. ERROR("Unable to create VkPipeline: " << result);
  287. }
  288. return pipeline;
  289. }
  290. VkImage CreateVkImage(VkDevice device, VkImageTiling image_tiling, VkImageUsageFlags usage) {
  291. VkImageCreateInfo create_info = {};
  292. create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  293. create_info.pNext = nullptr;
  294. create_info.imageType = VK_IMAGE_TYPE_2D;
  295. create_info.format = kVulkanFormat;
  296. VkExtent3D extent = {};
  297. extent.width = kWidth;
  298. extent.height = kHeight;
  299. extent.depth = 1;
  300. create_info.extent = extent;
  301. create_info.mipLevels = 1;
  302. create_info.arrayLayers = 1;
  303. create_info.samples = VK_SAMPLE_COUNT_1_BIT;
  304. create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  305. create_info.tiling = image_tiling;
  306. create_info.usage = usage;
  307. create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  308. VkImage image;
  309. VkResult result = vkCreateImage(device, &create_info, nullptr, &image);
  310. if (result != VK_SUCCESS) {
  311. ERROR("Unable to create VkImage: " << result);
  312. }
  313. return image;
  314. }
  315. inline VkImage CreateRenderVkImage(VkDevice device) {
  316. return CreateVkImage(device, VK_IMAGE_TILING_OPTIMAL,
  317. VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
  318. }
  319. inline VkImage CreateScanoutVkImage(VkDevice device) {
  320. return CreateVkImage(device, VK_IMAGE_TILING_LINEAR,
  321. VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
  322. }
  323. uint32_t FindMemoryType(uint32_t valid_image_memory_types,
  324. VkPhysicalDeviceMemoryProperties device_memory_properties,
  325. VkMemoryPropertyFlags memory_property_flags) {
  326. for (uint32_t i = 0; i < device_memory_properties.memoryTypeCount; i++) {
  327. // We don't care about performance, so just choose the first mappable memory type.
  328. if ((valid_image_memory_types & (1 << i)) &&
  329. ((device_memory_properties.memoryTypes[i].propertyFlags & memory_property_flags) ==
  330. memory_property_flags)) {
  331. return i;
  332. }
  333. }
  334. ERROR("Unable to find suitable memory type index");
  335. }
  336. VkDeviceMemory AllocateAndBindMemory(VkPhysicalDevice physical_device, VkDevice device,
  337. VkImage image, VkMemoryPropertyFlags memory_property_flags) {
  338. VkMemoryRequirements image_memory_requirements;
  339. vkGetImageMemoryRequirements(device, image, &image_memory_requirements);
  340. VkPhysicalDeviceMemoryProperties device_memory_properties;
  341. vkGetPhysicalDeviceMemoryProperties(physical_device, &device_memory_properties);
  342. VkMemoryAllocateInfo memory_allocate_info = {};
  343. memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
  344. memory_allocate_info.allocationSize = image_memory_requirements.size;
  345. memory_allocate_info.memoryTypeIndex = FindMemoryType(
  346. image_memory_requirements.memoryTypeBits, device_memory_properties,
  347. memory_property_flags);
  348. VkDeviceMemory image_memory;
  349. VkResult result = vkAllocateMemory(device, &memory_allocate_info, nullptr, &image_memory);
  350. if (result != VK_SUCCESS) {
  351. ERROR("Unable to allocate image memory: " << result);
  352. }
  353. result = vkBindImageMemory(device, image, image_memory, 0);
  354. if (result != VK_SUCCESS) {
  355. ERROR("Unable to bind image memory: " << result);
  356. }
  357. return image_memory;
  358. }
  359. inline VkDeviceMemory AllocateAndBindRenderMemory(VkPhysicalDevice physical_device,
  360. VkDevice device, VkImage image) {
  361. return AllocateAndBindMemory(physical_device, device, image, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
  362. }
  363. inline VkDeviceMemory AllocateAndBindScanoutMemory(VkPhysicalDevice physical_device,
  364. VkDevice device, VkImage image) {
  365. return AllocateAndBindMemory(physical_device, device, image, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
  366. }
  367. VkImageView CreateVkImageView(VkDevice device, VkImage image) {
  368. VkImageViewCreateInfo create_info = {};
  369. create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  370. create_info.image = image;
  371. create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
  372. create_info.format = kVulkanFormat;
  373. VkComponentMapping component_mapping = {};
  374. component_mapping.r = VK_COMPONENT_SWIZZLE_IDENTITY;
  375. component_mapping.b = VK_COMPONENT_SWIZZLE_IDENTITY;
  376. component_mapping.g = VK_COMPONENT_SWIZZLE_IDENTITY;
  377. component_mapping.a = VK_COMPONENT_SWIZZLE_IDENTITY;
  378. create_info.components = component_mapping;
  379. VkImageSubresourceRange subresource_range = {};
  380. subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  381. subresource_range.baseMipLevel = 0;
  382. subresource_range.levelCount = 1;
  383. subresource_range.baseArrayLayer = 0;
  384. subresource_range.layerCount = 1;
  385. create_info.subresourceRange = subresource_range;
  386. VkImageView image_view;
  387. VkResult result = vkCreateImageView(device, &create_info, nullptr, &image_view);
  388. if (result != VK_SUCCESS) {
  389. ERROR("Unable to create VkImageView: " << result);
  390. }
  391. return image_view;
  392. }
  393. VkFramebuffer CreateVkFramebuffer(VkDevice device, VkRenderPass render_pass,
  394. VkImageView image_view) {
  395. VkFramebufferCreateInfo create_info = {};
  396. create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
  397. create_info.renderPass = render_pass;
  398. create_info.attachmentCount = 1;
  399. create_info.pAttachments = &image_view;
  400. create_info.width = kWidth;
  401. create_info.height = kHeight;
  402. create_info.layers = 1;
  403. VkFramebuffer framebuffer;
  404. VkResult result = vkCreateFramebuffer(device, &create_info, nullptr, &framebuffer);
  405. if (result != VK_SUCCESS) {
  406. ERROR("Unable to create VkFramebuffer: " << result);
  407. }
  408. return framebuffer;
  409. }
  410. void BeginCommandBuffer(VkCommandBuffer command_buffer) {
  411. VkCommandBufferBeginInfo command_buffer_begin_info = {};
  412. command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  413. command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
  414. VkResult result = vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info);
  415. if (result != VK_SUCCESS) {
  416. ERROR("Unable to begin command buffer recording: " << result);
  417. }
  418. }
  419. void EndCommandBufferAndSubmit(VkCommandBuffer command_buffer, VkQueue queue) {
  420. VkResult result = vkEndCommandBuffer(command_buffer);
  421. if (result != VK_SUCCESS) {
  422. ERROR("Unable to end command buffer recording: " << result);
  423. }
  424. VkSubmitInfo submit_info = {};
  425. submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  426. submit_info.commandBufferCount = 1;
  427. submit_info.pCommandBuffers = &command_buffer;
  428. result = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
  429. if (result != VK_SUCCESS) {
  430. ERROR("Error in submitting command buffer to queue: " << result);
  431. }
  432. }
  433. void WaitForIdle(VkQueue queue) {
  434. // In a real application, we should use real synchronization primitives to figure out when the
  435. // command buffers have been executed. Waiting on an idle queue is simple, but it can cause
  436. // deadlock if we continue to submit to the queue while we wait for idle.
  437. VkResult result = vkQueueWaitIdle(queue);
  438. if (result != VK_SUCCESS) {
  439. ERROR("Error in waiting for graphics queue to reach idle state: " << result);
  440. }
  441. }
  442. void Draw(VkDevice device, VkQueue queue, VkRenderPass render_pass, VkPipeline pipeline,
  443. VkFramebuffer framebuffer, VkCommandBuffer command_buffer) {
  444. BeginCommandBuffer(command_buffer);
  445. VkRenderPassBeginInfo render_pass_begin_info = {};
  446. render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
  447. render_pass_begin_info.renderPass = render_pass;
  448. render_pass_begin_info.framebuffer = framebuffer;
  449. VkRect2D render_area = {};
  450. render_area.offset = { 0, 0 };
  451. render_area.extent = { kWidth, kHeight };
  452. render_pass_begin_info.renderArea = render_area;
  453. render_pass_begin_info.clearValueCount = 1;
  454. VkClearValue clear_value = {};
  455. clear_value.color.float32[0] = 1.0f;
  456. clear_value.color.float32[1] = 1.0f;
  457. clear_value.color.float32[2] = 0.0f;
  458. clear_value.color.float32[3] = 1.0f;
  459. render_pass_begin_info.pClearValues = &clear_value;
  460. vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
  461. vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
  462. vkCmdDraw(command_buffer, 3, 1, 0, 0);
  463. vkCmdEndRenderPass(command_buffer);
  464. }
  465. void BlitToScanoutImage(VkQueue queue, VkCommandBuffer command_buffer, VkImage source_image,
  466. VkImage dest_image) {
  467. // We need to make sure the writes of the render pass have executed before actually scanning out
  468. // the rendered image. Also, we need to transition the layout of the scanout image to
  469. // VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.
  470. VkImageMemoryBarrier image_memory_barrier = {};
  471. image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
  472. image_memory_barrier.pNext = nullptr;
  473. image_memory_barrier.srcAccessMask = 0;
  474. image_memory_barrier.dstAccessMask = 0;
  475. image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  476. image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
  477. image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  478. image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  479. image_memory_barrier.image = dest_image;
  480. VkImageSubresourceRange subresource_range = {};
  481. subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  482. subresource_range.baseMipLevel = 0;
  483. subresource_range.levelCount = 1;
  484. subresource_range.baseArrayLayer = 0;
  485. subresource_range.layerCount = 1;
  486. image_memory_barrier.subresourceRange = subresource_range;
  487. vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
  488. VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
  489. VkOffset3D blit_start = {};
  490. blit_start.x = 0;
  491. blit_start.y = 0;
  492. blit_start.z = 0;
  493. VkOffset3D blit_end = {};
  494. blit_end.x = kWidth;
  495. blit_end.y = kHeight;
  496. blit_end.z = 1;
  497. VkImageSubresourceLayers subresource_layers = {};
  498. subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  499. subresource_layers.mipLevel = 0;
  500. subresource_layers.baseArrayLayer = 0;
  501. subresource_layers.layerCount = 1;
  502. VkImageBlit image_blit = {};
  503. image_blit.srcSubresource = subresource_layers;
  504. image_blit.srcOffsets[0] = blit_start;
  505. image_blit.srcOffsets[1] = blit_end;
  506. image_blit.dstSubresource = subresource_layers;
  507. image_blit.dstOffsets[0] = blit_start;
  508. image_blit.dstOffsets[1] = blit_end;
  509. // TODO(brkho): Support multi-planar formats.
  510. vkCmdBlitImage(command_buffer, source_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dest_image,
  511. VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_blit, VK_FILTER_NEAREST);
  512. EndCommandBufferAndSubmit(command_buffer, queue);
  513. }
  514. uint8_t* MapVkDeviceMemory(VkDevice device, VkDeviceMemory image_memory) {
  515. void* mapped_memory = nullptr;
  516. VkResult result = vkMapMemory(device, image_memory, 0, kWidth * kHeight * 4, 0, &mapped_memory);
  517. if (result != VK_SUCCESS) {
  518. ERROR("Unable to map device memory: " << result);
  519. }
  520. return static_cast<uint8_t*>(mapped_memory);
  521. }
  522. void WriteImage(uint8_t* pixels) {
  523. FILE *f = fopen("triangle.png", "wb");
  524. png_image image_info = {};
  525. image_info.version = PNG_IMAGE_VERSION;
  526. image_info.width = kWidth;
  527. image_info.height = kHeight;
  528. image_info.format = PNG_FORMAT_RGBA;
  529. if (png_image_write_to_stdio(&image_info, f, 0, pixels, kWidth * 4, nullptr) == 0) {
  530. ERROR("Error writing PNG: " << image_info.message);
  531. }
  532. fclose(f);
  533. }
  534. int main() {
  535. VkInstance instance = CreateVkInstance();
  536. VkPhysicalDevice physical_device = ChooseVkPhysicalDevice(instance);
  537. uint32_t device_queue_family_index = ChooseDeviceQueueFamilyIndex(physical_device);
  538. VkDevice device = CreateVkDevice(physical_device, device_queue_family_index);
  539. VkQueue queue = GetVkQueue(device, device_queue_family_index);
  540. VkCommandPool command_pool = CreateVkCommandPool(device, device_queue_family_index);
  541. VkCommandBuffer command_buffer = CreateVkCommandBuffer(device, command_pool);
  542. VkRenderPass render_pass = CreateVkRenderPass(device);
  543. VkPipeline pipeline = CreateVkPipeline(device, render_pass);
  544. VkImage render_image = CreateRenderVkImage(device);
  545. AllocateAndBindRenderMemory(physical_device, device, render_image);
  546. VkImageView render_image_view = CreateVkImageView(device, render_image);
  547. VkFramebuffer framebuffer = CreateVkFramebuffer(device, render_pass, render_image_view);
  548. VkImage scanout_image = CreateScanoutVkImage(device);
  549. VkDeviceMemory scanout_image_memory =
  550. AllocateAndBindScanoutMemory(physical_device, device, scanout_image);
  551. Draw(device, queue, render_pass, pipeline, framebuffer, command_buffer);
  552. // Since the render target is created with VK_IMAGE_TILING_OPTIMAL, we need to copy it to a linear
  553. // format before scanning out.
  554. BlitToScanoutImage(queue, command_buffer, render_image, scanout_image);
  555. WaitForIdle(queue);
  556. uint8_t* pixels = MapVkDeviceMemory(device, scanout_image_memory);
  557. WriteImage(pixels);
  558. return EXIT_SUCCESS;
  559. }