Binary that renders a triangle using Vulkan, scans the resulting image to a PNG, and makes an occlusion query to determine the number of fragments shaded. To be used in developing Turnip.
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.

occlusion.cc 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  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 = "occlusion.vert.spv";
  12. const std::string kFragmentShaderPath = "occlusion.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. VkQueryPool CreateVkQueryPool(VkDevice device) {
  133. VkQueryPoolCreateInfo query_pool_create_info = {};
  134. query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
  135. query_pool_create_info.pNext = nullptr;
  136. query_pool_create_info.queryType = VK_QUERY_TYPE_OCCLUSION;
  137. query_pool_create_info.queryCount = 1;
  138. query_pool_create_info.flags = 0;
  139. query_pool_create_info.pipelineStatistics = 0;
  140. VkQueryPool query_pool;
  141. VkResult result = vkCreateQueryPool(device, &query_pool_create_info, nullptr, &query_pool);
  142. if (result != VK_SUCCESS) {
  143. ERROR("Unable to create VkQueryPool: " << result);
  144. }
  145. return query_pool;
  146. }
  147. VkRenderPass CreateVkRenderPass(VkDevice device) {
  148. VkAttachmentDescription attachment_description = {};
  149. attachment_description.format = kVulkanFormat;
  150. attachment_description.samples = VK_SAMPLE_COUNT_1_BIT;
  151. attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
  152. attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
  153. attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  154. attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
  155. attachment_description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  156. attachment_description.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
  157. VkAttachmentReference attachment_reference = {};
  158. attachment_reference.attachment = 0;
  159. attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
  160. VkSubpassDescription subpass_description = {};
  161. subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
  162. subpass_description.colorAttachmentCount = 1;
  163. subpass_description.pColorAttachments = &attachment_reference;
  164. VkSubpassDependency subpass_dependency = {};
  165. subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
  166. subpass_dependency.dstSubpass = 0;
  167. subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  168. subpass_dependency.srcAccessMask = 0;
  169. subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  170. subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
  171. VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  172. VkRenderPassCreateInfo render_pass_info = {};
  173. render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
  174. render_pass_info.attachmentCount = 1;
  175. render_pass_info.pAttachments = &attachment_description;
  176. render_pass_info.subpassCount = 1;
  177. render_pass_info.pSubpasses = &subpass_description;
  178. render_pass_info.dependencyCount = 1;
  179. render_pass_info.pDependencies = &subpass_dependency;
  180. VkRenderPass render_pass;
  181. VkResult result = vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass);
  182. if (result != VK_SUCCESS) {
  183. ERROR("Unable to create render pass: " << result);
  184. }
  185. return render_pass;
  186. }
  187. VkShaderModule CreateVkShaderModule(VkDevice device, std::string path) {
  188. std::ifstream fstream(path);
  189. if (!fstream) {
  190. ERROR("Unable to open: " << path);
  191. }
  192. std::stringstream buffer;
  193. buffer << fstream.rdbuf();
  194. std::string spirv_source = buffer.str();
  195. VkShaderModuleCreateInfo create_info = {};
  196. create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
  197. create_info.codeSize = spirv_source.length();
  198. create_info.pCode = (const uint32_t*)spirv_source.c_str();
  199. VkShaderModule shader_module;
  200. VkResult result = vkCreateShaderModule(device, &create_info, nullptr, &shader_module);
  201. if (result != VK_SUCCESS) {
  202. ERROR("Unable to create shader module for " << path << ": ");
  203. }
  204. return shader_module;
  205. }
  206. VkPipeline CreateVkPipeline(VkDevice device, VkRenderPass render_pass) {
  207. VkShaderModule vertex_shader_module = CreateVkShaderModule(device, kVertexShaderPath);
  208. VkShaderModule fragment_shader_module = CreateVkShaderModule(device, kFragmentShaderPath);
  209. VkPipelineShaderStageCreateInfo vertex_shader_stage_info = {};
  210. vertex_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  211. vertex_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
  212. vertex_shader_stage_info.module = vertex_shader_module;
  213. vertex_shader_stage_info.pName = "main";
  214. VkPipelineShaderStageCreateInfo fragment_shader_stage_info = {};
  215. fragment_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  216. fragment_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
  217. fragment_shader_stage_info.module = fragment_shader_module;
  218. fragment_shader_stage_info.pName = "main";
  219. std::vector<VkPipelineShaderStageCreateInfo> shader_stages =
  220. {vertex_shader_stage_info, fragment_shader_stage_info};
  221. VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
  222. vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
  223. vertex_input_info.vertexBindingDescriptionCount = 0;
  224. vertex_input_info.pVertexBindingDescriptions = nullptr;
  225. vertex_input_info.vertexAttributeDescriptionCount = 0;
  226. vertex_input_info.pVertexAttributeDescriptions = nullptr;
  227. VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {};
  228. input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
  229. input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
  230. input_assembly_info.primitiveRestartEnable = VK_FALSE;
  231. VkViewport viewport = {};
  232. viewport.x = 0.0f;
  233. viewport.y = 0.0f;
  234. viewport.width = (float)kWidth;
  235. viewport.height = (float)kHeight;
  236. viewport.minDepth = 0.0f;
  237. viewport.maxDepth = 1.0f;
  238. VkExtent2D extent = {};
  239. extent.width = kWidth;
  240. extent.height = kHeight;
  241. VkRect2D scissor = {};
  242. scissor.offset = {0, 0};
  243. scissor.extent = extent;
  244. VkPipelineViewportStateCreateInfo viewport_state_info = {};
  245. viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
  246. viewport_state_info.viewportCount = 1;
  247. viewport_state_info.pViewports = &viewport;
  248. viewport_state_info.scissorCount = 1;
  249. viewport_state_info.pScissors = &scissor;
  250. VkPipelineRasterizationStateCreateInfo rasterization_state_info = {};
  251. rasterization_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
  252. rasterization_state_info.depthClampEnable = VK_FALSE;
  253. rasterization_state_info.rasterizerDiscardEnable = VK_FALSE;
  254. rasterization_state_info.polygonMode = VK_POLYGON_MODE_FILL;
  255. rasterization_state_info.lineWidth = 1.0f;
  256. rasterization_state_info.cullMode = VK_CULL_MODE_BACK_BIT;
  257. rasterization_state_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
  258. rasterization_state_info.depthBiasEnable = VK_FALSE;
  259. VkPipelineMultisampleStateCreateInfo multisampling_state_info = {};
  260. multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
  261. multisampling_state_info.sampleShadingEnable = VK_FALSE;
  262. multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
  263. VkPipelineColorBlendAttachmentState color_blend_attachment_state = {};
  264. color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
  265. VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
  266. color_blend_attachment_state.blendEnable = VK_FALSE;
  267. VkPipelineColorBlendStateCreateInfo color_blend_state = {};
  268. color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
  269. color_blend_state.logicOpEnable = VK_FALSE;
  270. color_blend_state.attachmentCount = 1;
  271. color_blend_state.pAttachments = &color_blend_attachment_state;
  272. VkPipelineLayoutCreateInfo pipeline_layout_create_info = {};
  273. pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
  274. VkPipelineLayout pipeline_layout;
  275. VkResult result = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr,
  276. &pipeline_layout);
  277. if (result != VK_SUCCESS) {
  278. ERROR("Unable to create VkPipelineLayout: " << result);
  279. }
  280. VkGraphicsPipelineCreateInfo pipeline_create_info = {};
  281. pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
  282. pipeline_create_info.stageCount = 2;
  283. pipeline_create_info.pStages = shader_stages.data();
  284. pipeline_create_info.pVertexInputState = &vertex_input_info;
  285. pipeline_create_info.pInputAssemblyState = &input_assembly_info;
  286. pipeline_create_info.pViewportState = &viewport_state_info;
  287. pipeline_create_info.pRasterizationState = &rasterization_state_info;
  288. pipeline_create_info.pMultisampleState = &multisampling_state_info;
  289. pipeline_create_info.pDepthStencilState = nullptr;
  290. pipeline_create_info.pColorBlendState = &color_blend_state;
  291. pipeline_create_info.pDynamicState = nullptr;
  292. pipeline_create_info.layout = pipeline_layout;
  293. pipeline_create_info.renderPass = render_pass;
  294. pipeline_create_info.subpass = 0;
  295. pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
  296. pipeline_create_info.basePipelineIndex = -1;
  297. VkPipeline pipeline;
  298. result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr,
  299. &pipeline);
  300. if (result != VK_SUCCESS) {
  301. ERROR("Unable to create VkPipeline: " << result);
  302. }
  303. return pipeline;
  304. }
  305. VkImage CreateVkImage(VkDevice device, VkImageTiling image_tiling, VkImageUsageFlags usage) {
  306. VkImageCreateInfo create_info = {};
  307. create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  308. create_info.pNext = nullptr;
  309. create_info.imageType = VK_IMAGE_TYPE_2D;
  310. create_info.format = kVulkanFormat;
  311. VkExtent3D extent = {};
  312. extent.width = kWidth;
  313. extent.height = kHeight;
  314. extent.depth = 1;
  315. create_info.extent = extent;
  316. create_info.mipLevels = 1;
  317. create_info.arrayLayers = 1;
  318. create_info.samples = VK_SAMPLE_COUNT_1_BIT;
  319. create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  320. create_info.tiling = image_tiling;
  321. create_info.usage = usage;
  322. create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  323. VkImage image;
  324. VkResult result = vkCreateImage(device, &create_info, nullptr, &image);
  325. if (result != VK_SUCCESS) {
  326. ERROR("Unable to create VkImage: " << result);
  327. }
  328. return image;
  329. }
  330. inline VkImage CreateRenderVkImage(VkDevice device) {
  331. return CreateVkImage(device, VK_IMAGE_TILING_OPTIMAL,
  332. VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
  333. }
  334. inline VkImage CreateScanoutVkImage(VkDevice device) {
  335. return CreateVkImage(device, VK_IMAGE_TILING_LINEAR,
  336. VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
  337. }
  338. uint32_t FindMemoryType(uint32_t valid_image_memory_types,
  339. VkPhysicalDeviceMemoryProperties device_memory_properties,
  340. VkMemoryPropertyFlags memory_property_flags) {
  341. for (uint32_t i = 0; i < device_memory_properties.memoryTypeCount; i++) {
  342. // We don't care about performance, so just choose the first mappable memory type.
  343. if ((valid_image_memory_types % (1 << i)) &&
  344. ((device_memory_properties.memoryTypes[i].propertyFlags & memory_property_flags) ==
  345. memory_property_flags)) {
  346. return i;
  347. }
  348. }
  349. ERROR("Unable to find suitable memory type index");
  350. }
  351. VkDeviceMemory AllocateAndBindMemory(VkPhysicalDevice physical_device, VkDevice device,
  352. VkImage image, VkMemoryPropertyFlags memory_property_flags) {
  353. VkMemoryRequirements image_memory_requirements;
  354. vkGetImageMemoryRequirements(device, image, &image_memory_requirements);
  355. VkPhysicalDeviceMemoryProperties device_memory_properties;
  356. vkGetPhysicalDeviceMemoryProperties(physical_device, &device_memory_properties);
  357. VkMemoryAllocateInfo memory_allocate_info = {};
  358. memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
  359. memory_allocate_info.allocationSize = image_memory_requirements.size;
  360. memory_allocate_info.memoryTypeIndex = FindMemoryType(
  361. image_memory_requirements.memoryTypeBits, device_memory_properties,
  362. memory_property_flags);
  363. VkDeviceMemory image_memory;
  364. VkResult result = vkAllocateMemory(device, &memory_allocate_info, nullptr, &image_memory);
  365. if (result != VK_SUCCESS) {
  366. ERROR("Unable to allocate image memory: " << result);
  367. }
  368. result = vkBindImageMemory(device, image, image_memory, 0);
  369. if (result != VK_SUCCESS) {
  370. ERROR("Unable to bind image memory: " << result);
  371. }
  372. return image_memory;
  373. }
  374. inline VkDeviceMemory AllocateAndBindRenderMemory(VkPhysicalDevice physical_device,
  375. VkDevice device, VkImage image) {
  376. return AllocateAndBindMemory(physical_device, device, image, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
  377. }
  378. inline VkDeviceMemory AllocateAndBindScanoutMemory(VkPhysicalDevice physical_device,
  379. VkDevice device, VkImage image) {
  380. return AllocateAndBindMemory(physical_device, device, image, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
  381. }
  382. VkImageView CreateVkImageView(VkDevice device, VkImage image) {
  383. VkImageViewCreateInfo create_info = {};
  384. create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  385. create_info.image = image;
  386. create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
  387. create_info.format = kVulkanFormat;
  388. VkComponentMapping component_mapping = {};
  389. component_mapping.r = VK_COMPONENT_SWIZZLE_IDENTITY;
  390. component_mapping.b = VK_COMPONENT_SWIZZLE_IDENTITY;
  391. component_mapping.g = VK_COMPONENT_SWIZZLE_IDENTITY;
  392. component_mapping.a = VK_COMPONENT_SWIZZLE_IDENTITY;
  393. create_info.components = component_mapping;
  394. VkImageSubresourceRange subresource_range = {};
  395. subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  396. subresource_range.baseMipLevel = 0;
  397. subresource_range.levelCount = 1;
  398. subresource_range.baseArrayLayer = 0;
  399. subresource_range.layerCount = 1;
  400. create_info.subresourceRange = subresource_range;
  401. VkImageView image_view;
  402. VkResult result = vkCreateImageView(device, &create_info, nullptr, &image_view);
  403. if (result != VK_SUCCESS) {
  404. ERROR("Unable to create VkImageView: " << result);
  405. }
  406. return image_view;
  407. }
  408. VkFramebuffer CreateVkFramebuffer(VkDevice device, VkRenderPass render_pass,
  409. VkImageView image_view) {
  410. VkFramebufferCreateInfo create_info = {};
  411. create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
  412. create_info.renderPass = render_pass;
  413. create_info.attachmentCount = 1;
  414. create_info.pAttachments = &image_view;
  415. create_info.width = kWidth;
  416. create_info.height = kHeight;
  417. create_info.layers = 1;
  418. VkFramebuffer framebuffer;
  419. VkResult result = vkCreateFramebuffer(device, &create_info, nullptr, &framebuffer);
  420. if (result != VK_SUCCESS) {
  421. ERROR("Unable to create VkFramebuffer: " << result);
  422. }
  423. return framebuffer;
  424. }
  425. void BeginQuery(VkCommandBuffer command_buffer, VkQueryPool query_pool) {
  426. vkCmdResetQueryPool(command_buffer, query_pool, 0, 1);
  427. vkCmdBeginQuery(command_buffer, query_pool, 0, 0);
  428. }
  429. void EndQuery(VkCommandBuffer command_buffer, VkQueryPool query_pool) {
  430. vkCmdEndQuery(command_buffer, query_pool, 0);
  431. }
  432. void BeginCommandBuffer(VkCommandBuffer command_buffer) {
  433. VkCommandBufferBeginInfo command_buffer_begin_info = {};
  434. command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  435. command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
  436. VkResult result = vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info);
  437. if (result != VK_SUCCESS) {
  438. ERROR("Unable to begin command buffer recording: " << result);
  439. }
  440. }
  441. void EndCommandBufferAndSubmit(VkCommandBuffer command_buffer, VkQueue queue) {
  442. VkResult result = vkEndCommandBuffer(command_buffer);
  443. if (result != VK_SUCCESS) {
  444. ERROR("Unable to end command buffer recording: " << result);
  445. }
  446. VkSubmitInfo submit_info = {};
  447. submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  448. submit_info.commandBufferCount = 1;
  449. submit_info.pCommandBuffers = &command_buffer;
  450. result = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
  451. if (result != VK_SUCCESS) {
  452. ERROR("Error in submitting command buffer to queue: " << result);
  453. }
  454. }
  455. void WaitForIdle(VkQueue queue) {
  456. // In a real application, we should use real synchronization primitives to figure out when the
  457. // command buffers have been executed. Waiting on an idle queue is simple, but it can cause
  458. // deadlock if we continue to submit to the queue while we wait for idle.
  459. VkResult result = vkQueueWaitIdle(queue);
  460. if (result != VK_SUCCESS) {
  461. ERROR("Error in waiting for graphics queue to reach idle state: " << result);
  462. }
  463. }
  464. void Draw(VkDevice device, VkQueue queue, VkRenderPass render_pass, VkPipeline pipeline,
  465. VkFramebuffer framebuffer, VkCommandBuffer command_buffer, VkQueryPool query_pool) {
  466. BeginCommandBuffer(command_buffer);
  467. BeginQuery(command_buffer, query_pool);
  468. VkRenderPassBeginInfo render_pass_begin_info = {};
  469. render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
  470. render_pass_begin_info.renderPass = render_pass;
  471. render_pass_begin_info.framebuffer = framebuffer;
  472. VkRect2D render_area = {};
  473. render_area.offset = { 0, 0 };
  474. render_area.extent = { kWidth, kHeight };
  475. render_pass_begin_info.renderArea = render_area;
  476. render_pass_begin_info.clearValueCount = 1;
  477. VkClearValue clear_value = {};
  478. clear_value.color.float32[0] = 0.0f;
  479. clear_value.color.float32[1] = 0.0f;
  480. clear_value.color.float32[2] = 1.0f;
  481. clear_value.color.float32[3] = 1.0f;
  482. render_pass_begin_info.pClearValues = &clear_value;
  483. vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
  484. vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
  485. vkCmdDraw(command_buffer, 3, 1, 0, 0);
  486. vkCmdEndRenderPass(command_buffer);
  487. EndQuery(command_buffer, query_pool);
  488. }
  489. void BlitToScanoutImage(VkQueue queue, VkCommandBuffer command_buffer, VkImage source_image,
  490. VkImage dest_image) {
  491. // We need to make sure the writes of the render pass have executed before actually scanning out
  492. // the rendered image. Also, we need to transition the layout of the scanout image to
  493. // VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.
  494. VkImageMemoryBarrier image_memory_barrier = {};
  495. image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
  496. image_memory_barrier.pNext = nullptr;
  497. image_memory_barrier.srcAccessMask = 0;
  498. image_memory_barrier.dstAccessMask = 0;
  499. image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  500. image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
  501. image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  502. image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  503. image_memory_barrier.image = dest_image;
  504. VkImageSubresourceRange subresource_range = {};
  505. subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  506. subresource_range.baseMipLevel = 0;
  507. subresource_range.levelCount = 1;
  508. subresource_range.baseArrayLayer = 0;
  509. subresource_range.layerCount = 1;
  510. image_memory_barrier.subresourceRange = subresource_range;
  511. vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
  512. VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
  513. VkOffset3D blit_start = {};
  514. blit_start.x = 0;
  515. blit_start.y = 0;
  516. blit_start.z = 0;
  517. VkOffset3D blit_end = {};
  518. blit_end.x = kWidth;
  519. blit_end.y = kHeight;
  520. blit_end.z = 1;
  521. VkImageSubresourceLayers subresource_layers = {};
  522. subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  523. subresource_layers.mipLevel = 0;
  524. subresource_layers.baseArrayLayer = 0;
  525. subresource_layers.layerCount = 1;
  526. VkImageBlit image_blit = {};
  527. image_blit.srcSubresource = subresource_layers;
  528. image_blit.srcOffsets[0] = blit_start;
  529. image_blit.srcOffsets[1] = blit_end;
  530. image_blit.dstSubresource = subresource_layers;
  531. image_blit.dstOffsets[0] = blit_start;
  532. image_blit.dstOffsets[1] = blit_end;
  533. // TODO(brkho): Support multi-planar formats.
  534. vkCmdBlitImage(command_buffer, source_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dest_image,
  535. VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_blit, VK_FILTER_NEAREST);
  536. EndCommandBufferAndSubmit(command_buffer, queue);
  537. }
  538. uint8_t* MapVkDeviceMemory(VkDevice device, VkDeviceMemory image_memory) {
  539. void* mapped_memory = nullptr;
  540. VkResult result = vkMapMemory(device, image_memory, 0, kWidth * kHeight * 4, 0, &mapped_memory);
  541. if (result != VK_SUCCESS) {
  542. ERROR("Unable to map device memory: " << result);
  543. }
  544. return static_cast<uint8_t*>(mapped_memory);
  545. }
  546. uint32_t GetQueryPoolResults(VkDevice device, VkQueryPool query_pool) {
  547. uint32_t passed_fragments = 0;
  548. VkResult result = vkGetQueryPoolResults(device, query_pool, 0, 1, sizeof(passed_fragments),
  549. &passed_fragments, sizeof(uint32_t), VK_QUERY_RESULT_WAIT_BIT);
  550. // TODO(brkho): Currently, we query for results with the VK_QUERY_RESULT_WAIT_BIT flag set which
  551. // blocks execution until the results are ready. We should instead poll and check that result
  552. // isn't VK_NOT_READY.
  553. if (result != VK_SUCCESS) {
  554. ERROR("Unable to get query pool results: " << result);
  555. }
  556. return passed_fragments;
  557. }
  558. void WriteImage(uint8_t* pixels) {
  559. FILE *f = fopen("occlusion.png", "wb");
  560. png_image image_info = {};
  561. image_info.version = PNG_IMAGE_VERSION;
  562. image_info.width = kWidth;
  563. image_info.height = kHeight;
  564. image_info.format = PNG_FORMAT_RGBA;
  565. if (png_image_write_to_stdio(&image_info, f, 0, pixels, kWidth * 4, nullptr) == 0) {
  566. ERROR("Error writing PNG: " << image_info.message);
  567. }
  568. fclose(f);
  569. }
  570. uint32_t GetShadedFragmentsCountManual(uint8_t* pixels) {
  571. uint32_t count = 0;
  572. for (size_t i = 0; i < kHeight * kWidth * 4; i += 4) {
  573. uint8_t r = pixels[i];
  574. uint8_t g = pixels[i + 1];
  575. uint8_t b = pixels[i + 2];
  576. uint8_t a = pixels[i + 3];
  577. if (r == 255 && g == 0 && b == 0 && a == 255) {
  578. count++;
  579. }
  580. }
  581. return count;
  582. }
  583. int main() {
  584. VkInstance instance = CreateVkInstance();
  585. VkPhysicalDevice physical_device = ChooseVkPhysicalDevice(instance);
  586. uint32_t device_queue_family_index = ChooseDeviceQueueFamilyIndex(physical_device);
  587. VkDevice device = CreateVkDevice(physical_device, device_queue_family_index);
  588. VkQueue queue = GetVkQueue(device, device_queue_family_index);
  589. VkCommandPool command_pool = CreateVkCommandPool(device, device_queue_family_index);
  590. VkCommandBuffer command_buffer = CreateVkCommandBuffer(device, command_pool);
  591. VkQueryPool query_pool = CreateVkQueryPool(device);
  592. VkRenderPass render_pass = CreateVkRenderPass(device);
  593. VkPipeline pipeline = CreateVkPipeline(device, render_pass);
  594. VkImage render_image = CreateRenderVkImage(device);
  595. AllocateAndBindRenderMemory(physical_device, device, render_image);
  596. VkImageView render_image_view = CreateVkImageView(device, render_image);
  597. VkFramebuffer framebuffer = CreateVkFramebuffer(device, render_pass, render_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, query_pool);
  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. uint32_t shaded_fragments_count_query = GetQueryPoolResults(device, query_pool);
  609. uint32_t shaded_fragments_count_manual = GetShadedFragmentsCountManual(pixels);
  610. std::cout << "[Occlusion query] shaded fragments: " << shaded_fragments_count_query << std::endl;
  611. std::cout << "[Manual count] shaded fragments: " << shaded_fragments_count_manual << std::endl;
  612. return EXIT_SUCCESS;
  613. }