Example application for subpasses in Vulkan.
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.

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