1 /* 2 * Copyright (C) 2016 Google, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included 12 * in all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 */ 22 23 #include <array> 24 25 #include <glm/gtc/type_ptr.hpp> 26 #include <glm/gtc/matrix_transform.hpp> 27 28 #include "Helpers.h" 29 #include "Smoke.h" 30 #include "Meshes.h" 31 #include "Shell.h" 32 33 namespace { 34 35 // TODO do not rely on compiler to use std140 layout 36 // TODO move lower frequency data to another descriptor set 37 struct ShaderParamBlock { 38 float light_pos[4]; 39 float light_color[4]; 40 float model[4 * 4]; 41 float view_projection[4 * 4]; 42 }; 43 44 } // namespace 45 46 Smoke::Smoke(const std::vector<std::string> &args) 47 : Game("Smoke", args), multithread_(true), use_push_constants_(false), 48 sim_paused_(false), sim_(5000), camera_(2.5f), frame_data_(), 49 render_pass_clear_value_({{ 0.0f, 0.1f, 0.2f, 1.0f }}), 50 render_pass_begin_info_(), 51 primary_cmd_begin_info_(), primary_cmd_submit_info_() 52 { 53 for (auto it = args.begin(); it != args.end(); ++it) { 54 if (*it == "-s") 55 multithread_ = false; 56 else if (*it == "-p") 57 use_push_constants_ = true; 58 } 59 60 init_workers(); 61 } 62 63 Smoke::~Smoke() 64 { 65 } 66 67 void Smoke::init_workers() 68 { 69 int worker_count = std::thread::hardware_concurrency(); 70 71 // not enough cores 72 if (!multithread_ || worker_count < 2) { 73 multithread_ = false; 74 worker_count = 1; 75 } 76 77 const int object_per_worker = sim_.objects().size() / worker_count; 78 int object_begin = 0, object_end = 0; 79 80 workers_.reserve(worker_count); 81 for (int i = 0; i < worker_count; i++) { 82 object_begin = object_end; 83 if (i < worker_count - 1) 84 object_end += object_per_worker; 85 else 86 object_end = sim_.objects().size(); 87 88 Worker *worker = new Worker(*this, i, object_begin, object_end); 89 workers_.emplace_back(std::unique_ptr<Worker>(worker)); 90 } 91 } 92 93 void Smoke::attach_shell(Shell &sh) 94 { 95 Game::attach_shell(sh); 96 97 const Shell::Context &ctx = sh.context(); 98 physical_dev_ = ctx.physical_dev; 99 dev_ = ctx.dev; 100 queue_ = ctx.game_queue; 101 queue_family_ = ctx.game_queue_family; 102 format_ = ctx.format.format; 103 104 vk::GetPhysicalDeviceProperties(physical_dev_, &physical_dev_props_); 105 106 if (use_push_constants_ && 107 sizeof(ShaderParamBlock) > physical_dev_props_.limits.maxPushConstantsSize) { 108 shell_->log(Shell::LOG_WARN, "cannot enable push constants"); 109 use_push_constants_ = false; 110 } 111 112 VkPhysicalDeviceMemoryProperties mem_props; 113 vk::GetPhysicalDeviceMemoryProperties(physical_dev_, &mem_props); 114 mem_flags_.reserve(mem_props.memoryTypeCount); 115 for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) 116 mem_flags_.push_back(mem_props.memoryTypes[i].propertyFlags); 117 118 meshes_ = new Meshes(dev_, mem_flags_); 119 120 create_render_pass(); 121 create_shader_modules(); 122 create_descriptor_set_layout(); 123 create_pipeline_layout(); 124 create_pipeline(); 125 126 create_frame_data(2); 127 128 render_pass_begin_info_.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; 129 render_pass_begin_info_.renderPass = render_pass_; 130 render_pass_begin_info_.clearValueCount = 1; 131 render_pass_begin_info_.pClearValues = &render_pass_clear_value_; 132 133 primary_cmd_begin_info_.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 134 primary_cmd_begin_info_.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 135 136 // we will render to the swapchain images 137 primary_cmd_submit_wait_stages_ = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 138 139 primary_cmd_submit_info_.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 140 primary_cmd_submit_info_.waitSemaphoreCount = 1; 141 primary_cmd_submit_info_.pWaitDstStageMask = &primary_cmd_submit_wait_stages_; 142 primary_cmd_submit_info_.commandBufferCount = 1; 143 primary_cmd_submit_info_.signalSemaphoreCount = 1; 144 145 if (multithread_) { 146 for (auto &worker : workers_) 147 worker->start(); 148 } 149 } 150 151 void Smoke::detach_shell() 152 { 153 if (multithread_) { 154 for (auto &worker : workers_) 155 worker->stop(); 156 } 157 158 destroy_frame_data(); 159 160 vk::DestroyPipeline(dev_, pipeline_, nullptr); 161 vk::DestroyPipelineLayout(dev_, pipeline_layout_, nullptr); 162 if (!use_push_constants_) 163 vk::DestroyDescriptorSetLayout(dev_, desc_set_layout_, nullptr); 164 vk::DestroyShaderModule(dev_, fs_, nullptr); 165 vk::DestroyShaderModule(dev_, vs_, nullptr); 166 vk::DestroyRenderPass(dev_, render_pass_, nullptr); 167 168 delete meshes_; 169 170 Game::detach_shell(); 171 } 172 173 void Smoke::create_render_pass() 174 { 175 VkAttachmentDescription attachment = {}; 176 attachment.format = format_; 177 attachment.samples = VK_SAMPLE_COUNT_1_BIT; 178 attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 179 attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 180 attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 181 attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; 182 183 VkAttachmentReference attachment_ref = {}; 184 attachment_ref.attachment = 0; 185 attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 186 187 VkSubpassDescription subpass = {}; 188 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; 189 subpass.colorAttachmentCount = 1; 190 subpass.pColorAttachments = &attachment_ref; 191 192 std::array<VkSubpassDependency, 2> subpass_deps; 193 subpass_deps[0].srcSubpass = VK_SUBPASS_EXTERNAL; 194 subpass_deps[0].dstSubpass = 0; 195 subpass_deps[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; 196 subpass_deps[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 197 subpass_deps[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; 198 subpass_deps[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | 199 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; 200 subpass_deps[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; 201 202 subpass_deps[1].srcSubpass = 0; 203 subpass_deps[1].dstSubpass = VK_SUBPASS_EXTERNAL; 204 subpass_deps[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 205 subpass_deps[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; 206 subpass_deps[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | 207 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; 208 subpass_deps[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; 209 subpass_deps[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; 210 211 VkRenderPassCreateInfo render_pass_info = {}; 212 render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; 213 render_pass_info.attachmentCount = 1; 214 render_pass_info.pAttachments = &attachment; 215 render_pass_info.subpassCount = 1; 216 render_pass_info.pSubpasses = &subpass; 217 render_pass_info.dependencyCount = (uint32_t)subpass_deps.size(); 218 render_pass_info.pDependencies = subpass_deps.data(); 219 220 vk::assert_success(vk::CreateRenderPass(dev_, &render_pass_info, nullptr, &render_pass_)); 221 } 222 223 void Smoke::create_shader_modules() 224 { 225 VkShaderModuleCreateInfo sh_info = {}; 226 sh_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 227 if (use_push_constants_) { 228 #include "Smoke.push_constant.vert.h" 229 sh_info.codeSize = sizeof(Smoke_push_constant_vert); 230 sh_info.pCode = Smoke_push_constant_vert; 231 } else { 232 #include "Smoke.vert.h" 233 sh_info.codeSize = sizeof(Smoke_vert); 234 sh_info.pCode = Smoke_vert; 235 } 236 vk::assert_success(vk::CreateShaderModule(dev_, &sh_info, nullptr, &vs_)); 237 238 #include "Smoke.frag.h" 239 sh_info.codeSize = sizeof(Smoke_frag); 240 sh_info.pCode = Smoke_frag; 241 vk::assert_success(vk::CreateShaderModule(dev_, &sh_info, nullptr, &fs_)); 242 } 243 244 void Smoke::create_descriptor_set_layout() 245 { 246 if (use_push_constants_) 247 return; 248 249 VkDescriptorSetLayoutBinding layout_binding = {}; 250 layout_binding.binding = 0; 251 layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; 252 layout_binding.descriptorCount = 1; 253 layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; 254 255 VkDescriptorSetLayoutCreateInfo layout_info = {}; 256 layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; 257 layout_info.bindingCount = 1; 258 layout_info.pBindings = &layout_binding; 259 260 vk::assert_success(vk::CreateDescriptorSetLayout(dev_, &layout_info, 261 nullptr, &desc_set_layout_)); 262 } 263 264 void Smoke::create_pipeline_layout() 265 { 266 VkPushConstantRange push_const_range = {}; 267 268 VkPipelineLayoutCreateInfo pipeline_layout_info = {}; 269 pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; 270 271 if (use_push_constants_) { 272 push_const_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; 273 push_const_range.offset = 0; 274 push_const_range.size = sizeof(ShaderParamBlock); 275 276 pipeline_layout_info.pushConstantRangeCount = 1; 277 pipeline_layout_info.pPushConstantRanges = &push_const_range; 278 } else { 279 pipeline_layout_info.setLayoutCount = 1; 280 pipeline_layout_info.pSetLayouts = &desc_set_layout_; 281 } 282 283 vk::assert_success(vk::CreatePipelineLayout(dev_, &pipeline_layout_info, 284 nullptr, &pipeline_layout_)); 285 } 286 287 void Smoke::create_pipeline() 288 { 289 VkPipelineShaderStageCreateInfo stage_info[2] = {}; 290 stage_info[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 291 stage_info[0].stage = VK_SHADER_STAGE_VERTEX_BIT; 292 stage_info[0].module = vs_; 293 stage_info[0].pName = "main"; 294 stage_info[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 295 stage_info[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; 296 stage_info[1].module = fs_; 297 stage_info[1].pName = "main"; 298 299 VkPipelineViewportStateCreateInfo viewport_info = {}; 300 viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 301 // both dynamic 302 viewport_info.viewportCount = 1; 303 viewport_info.scissorCount = 1; 304 305 VkPipelineRasterizationStateCreateInfo rast_info = {}; 306 rast_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 307 rast_info.depthClampEnable = false; 308 rast_info.rasterizerDiscardEnable = false; 309 rast_info.polygonMode = VK_POLYGON_MODE_FILL; 310 rast_info.cullMode = VK_CULL_MODE_NONE; 311 rast_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; 312 rast_info.depthBiasEnable = false; 313 rast_info.lineWidth = 1.0f; 314 315 VkPipelineMultisampleStateCreateInfo multisample_info = {}; 316 multisample_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; 317 multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; 318 multisample_info.sampleShadingEnable = false; 319 multisample_info.pSampleMask = nullptr; 320 multisample_info.alphaToCoverageEnable = false; 321 multisample_info.alphaToOneEnable = false; 322 323 VkPipelineColorBlendAttachmentState blend_attachment = {}; 324 blend_attachment.blendEnable = true; 325 blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; 326 blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; 327 blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; 328 blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; 329 blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; 330 blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; 331 blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | 332 VK_COLOR_COMPONENT_G_BIT | 333 VK_COLOR_COMPONENT_B_BIT | 334 VK_COLOR_COMPONENT_A_BIT; 335 336 VkPipelineColorBlendStateCreateInfo blend_info = {}; 337 blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; 338 blend_info.logicOpEnable = false; 339 blend_info.attachmentCount = 1; 340 blend_info.pAttachments = &blend_attachment; 341 342 std::array<VkDynamicState, 2> dynamic_states = { 343 VK_DYNAMIC_STATE_VIEWPORT, 344 VK_DYNAMIC_STATE_SCISSOR 345 }; 346 struct VkPipelineDynamicStateCreateInfo dynamic_info = {}; 347 dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; 348 dynamic_info.dynamicStateCount = (uint32_t)dynamic_states.size(); 349 dynamic_info.pDynamicStates = dynamic_states.data(); 350 351 VkGraphicsPipelineCreateInfo pipeline_info = {}; 352 pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; 353 pipeline_info.stageCount = 2; 354 pipeline_info.pStages = stage_info; 355 pipeline_info.pVertexInputState = &meshes_->vertex_input_state(); 356 pipeline_info.pInputAssemblyState = &meshes_->input_assembly_state(); 357 pipeline_info.pTessellationState = nullptr; 358 pipeline_info.pViewportState = &viewport_info; 359 pipeline_info.pRasterizationState = &rast_info; 360 pipeline_info.pMultisampleState = &multisample_info; 361 pipeline_info.pDepthStencilState = nullptr; 362 pipeline_info.pColorBlendState = &blend_info; 363 pipeline_info.pDynamicState = &dynamic_info; 364 pipeline_info.layout = pipeline_layout_; 365 pipeline_info.renderPass = render_pass_; 366 pipeline_info.subpass = 0; 367 vk::assert_success(vk::CreateGraphicsPipelines(dev_, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &pipeline_)); 368 } 369 370 void Smoke::create_frame_data(int count) 371 { 372 frame_data_.resize(count); 373 374 create_fences(); 375 create_command_buffers(); 376 377 if (!use_push_constants_) { 378 create_buffers(); 379 create_buffer_memory(); 380 create_descriptor_sets(); 381 } 382 383 frame_data_index_ = 0; 384 } 385 386 void Smoke::destroy_frame_data() 387 { 388 if (!use_push_constants_) { 389 vk::DestroyDescriptorPool(dev_, desc_pool_, nullptr); 390 391 for (auto cmd_pool : worker_cmd_pools_) 392 vk::DestroyCommandPool(dev_, cmd_pool, nullptr); 393 worker_cmd_pools_.clear(); 394 vk::DestroyCommandPool(dev_, primary_cmd_pool_, nullptr); 395 396 vk::UnmapMemory(dev_, frame_data_mem_); 397 vk::FreeMemory(dev_, frame_data_mem_, nullptr); 398 399 for (auto &data : frame_data_) 400 vk::DestroyBuffer(dev_, data.buf, nullptr); 401 } 402 403 for (auto &data : frame_data_) 404 vk::DestroyFence(dev_, data.fence, nullptr); 405 406 frame_data_.clear(); 407 } 408 409 void Smoke::create_fences() 410 { 411 VkFenceCreateInfo fence_info = {}; 412 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; 413 fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; 414 415 for (auto &data : frame_data_) 416 vk::assert_success(vk::CreateFence(dev_, &fence_info, nullptr, &data.fence)); 417 } 418 419 void Smoke::create_command_buffers() 420 { 421 VkCommandPoolCreateInfo cmd_pool_info = {}; 422 cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; 423 cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; 424 cmd_pool_info.queueFamilyIndex = queue_family_; 425 426 VkCommandBufferAllocateInfo cmd_info = {}; 427 cmd_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 428 cmd_info.commandBufferCount = static_cast<uint32_t>(frame_data_.size()); 429 430 // create command pools and buffers 431 std::vector<VkCommandPool> cmd_pools(workers_.size() + 1, VK_NULL_HANDLE); 432 std::vector<std::vector<VkCommandBuffer>> cmds_vec(workers_.size() + 1, 433 std::vector<VkCommandBuffer>(frame_data_.size(), VK_NULL_HANDLE)); 434 for (size_t i = 0; i < cmd_pools.size(); i++) { 435 auto &cmd_pool = cmd_pools[i]; 436 auto &cmds = cmds_vec[i]; 437 438 vk::assert_success(vk::CreateCommandPool(dev_, &cmd_pool_info, 439 nullptr, &cmd_pool)); 440 441 cmd_info.commandPool = cmd_pool; 442 cmd_info.level = (cmd_pool == cmd_pools.back()) ? 443 VK_COMMAND_BUFFER_LEVEL_PRIMARY : VK_COMMAND_BUFFER_LEVEL_SECONDARY; 444 445 vk::assert_success(vk::AllocateCommandBuffers(dev_, &cmd_info, cmds.data())); 446 } 447 448 // update frame_data_ 449 for (size_t i = 0; i < frame_data_.size(); i++) { 450 for (const auto &cmds : cmds_vec) { 451 if (cmds == cmds_vec.back()) { 452 frame_data_[i].primary_cmd = cmds[i]; 453 } else { 454 frame_data_[i].worker_cmds.push_back(cmds[i]); 455 } 456 } 457 } 458 459 primary_cmd_pool_ = cmd_pools.back(); 460 cmd_pools.pop_back(); 461 worker_cmd_pools_ = cmd_pools; 462 } 463 464 void Smoke::create_buffers() 465 { 466 VkDeviceSize object_data_size = sizeof(ShaderParamBlock); 467 // align object data to device limit 468 const VkDeviceSize &alignment = 469 physical_dev_props_.limits.minStorageBufferOffsetAlignment; 470 if (object_data_size % alignment) 471 object_data_size += alignment - (object_data_size % alignment); 472 473 // update simulation 474 sim_.set_frame_data_size(object_data_size); 475 476 VkBufferCreateInfo buf_info = {}; 477 buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 478 buf_info.size = object_data_size * sim_.objects().size(); 479 buf_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; 480 buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 481 482 for (auto &data : frame_data_) 483 vk::assert_success(vk::CreateBuffer(dev_, &buf_info, nullptr, &data.buf)); 484 } 485 486 void Smoke::create_buffer_memory() 487 { 488 VkMemoryRequirements mem_reqs; 489 vk::GetBufferMemoryRequirements(dev_, frame_data_[0].buf, &mem_reqs); 490 491 VkDeviceSize aligned_size = mem_reqs.size; 492 if (aligned_size % mem_reqs.alignment) 493 aligned_size += mem_reqs.alignment - (aligned_size % mem_reqs.alignment); 494 495 // allocate memory 496 VkMemoryAllocateInfo mem_info = {}; 497 mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 498 mem_info.allocationSize = aligned_size * (frame_data_.size() - 1) + 499 mem_reqs.size; 500 501 for (uint32_t idx = 0; idx < mem_flags_.size(); idx++) { 502 if ((mem_reqs.memoryTypeBits & (1 << idx)) && 503 (mem_flags_[idx] & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && 504 (mem_flags_[idx] & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { 505 // TODO is this guaranteed to exist? 506 mem_info.memoryTypeIndex = idx; 507 break; 508 } 509 } 510 511 vk::AllocateMemory(dev_, &mem_info, nullptr, &frame_data_mem_); 512 513 void *ptr; 514 vk::MapMemory(dev_, frame_data_mem_, 0, VK_WHOLE_SIZE, 0, &ptr); 515 516 VkDeviceSize offset = 0; 517 for (auto &data : frame_data_) { 518 vk::BindBufferMemory(dev_, data.buf, frame_data_mem_, offset); 519 data.base = reinterpret_cast<uint8_t *>(ptr) + offset; 520 offset += aligned_size; 521 } 522 } 523 524 void Smoke::create_descriptor_sets() 525 { 526 VkDescriptorPoolSize desc_pool_size = {}; 527 desc_pool_size.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; 528 desc_pool_size.descriptorCount = frame_data_.size(); 529 530 VkDescriptorPoolCreateInfo desc_pool_info = {}; 531 desc_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; 532 desc_pool_info.maxSets = frame_data_.size(); 533 desc_pool_info.poolSizeCount = 1; 534 desc_pool_info.pPoolSizes = &desc_pool_size; 535 536 // create descriptor pool 537 vk::assert_success(vk::CreateDescriptorPool(dev_, &desc_pool_info, 538 nullptr, &desc_pool_)); 539 540 std::vector<VkDescriptorSetLayout> set_layouts(frame_data_.size(), desc_set_layout_); 541 VkDescriptorSetAllocateInfo set_info = {}; 542 set_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; 543 set_info.descriptorPool = desc_pool_; 544 set_info.descriptorSetCount = static_cast<uint32_t>(set_layouts.size()); 545 set_info.pSetLayouts = set_layouts.data(); 546 547 // create descriptor sets 548 std::vector<VkDescriptorSet> desc_sets(frame_data_.size(), VK_NULL_HANDLE); 549 vk::assert_success(vk::AllocateDescriptorSets(dev_, &set_info, desc_sets.data())); 550 551 std::vector<VkDescriptorBufferInfo> desc_bufs(frame_data_.size()); 552 std::vector<VkWriteDescriptorSet> desc_writes(frame_data_.size()); 553 554 for (size_t i = 0; i < frame_data_.size(); i++) { 555 auto &data = frame_data_[i]; 556 557 data.desc_set = desc_sets[i]; 558 559 VkDescriptorBufferInfo desc_buf = {}; 560 desc_buf.buffer = data.buf; 561 desc_buf.offset = 0; 562 desc_buf.range = VK_WHOLE_SIZE; 563 desc_bufs[i] = desc_buf; 564 565 VkWriteDescriptorSet desc_write = {}; 566 desc_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 567 desc_write.dstSet = data.desc_set; 568 desc_write.dstBinding = 0; 569 desc_write.dstArrayElement = 0; 570 desc_write.descriptorCount = 1; 571 desc_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; 572 desc_write.pBufferInfo = &desc_bufs[i]; 573 desc_writes[i] = desc_write; 574 } 575 576 vk::UpdateDescriptorSets(dev_, 577 static_cast<uint32_t>(desc_writes.size()), 578 desc_writes.data(), 0, nullptr); 579 } 580 581 void Smoke::attach_swapchain() 582 { 583 const Shell::Context &ctx = shell_->context(); 584 585 prepare_viewport(ctx.extent); 586 prepare_framebuffers(ctx.swapchain); 587 588 update_camera(); 589 } 590 591 void Smoke::detach_swapchain() 592 { 593 for (auto fb : framebuffers_) 594 vk::DestroyFramebuffer(dev_, fb, nullptr); 595 for (auto view : image_views_) 596 vk::DestroyImageView(dev_, view, nullptr); 597 598 framebuffers_.clear(); 599 image_views_.clear(); 600 images_.clear(); 601 } 602 603 void Smoke::prepare_viewport(const VkExtent2D &extent) 604 { 605 extent_ = extent; 606 607 viewport_.x = 0.0f; 608 viewport_.y = 0.0f; 609 viewport_.width = static_cast<float>(extent.width); 610 viewport_.height = static_cast<float>(extent.height); 611 viewport_.minDepth = 0.0f; 612 viewport_.maxDepth = 1.0f; 613 614 scissor_.offset = { 0, 0 }; 615 scissor_.extent = extent_; 616 } 617 618 void Smoke::prepare_framebuffers(VkSwapchainKHR swapchain) 619 { 620 // get swapchain images 621 vk::get(dev_, swapchain, images_); 622 623 assert(framebuffers_.empty()); 624 image_views_.reserve(images_.size()); 625 framebuffers_.reserve(images_.size()); 626 for (auto img : images_) { 627 VkImageViewCreateInfo view_info = {}; 628 view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 629 view_info.image = img; 630 view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; 631 view_info.format = format_; 632 view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 633 view_info.subresourceRange.levelCount = 1; 634 view_info.subresourceRange.layerCount = 1; 635 636 VkImageView view; 637 vk::assert_success(vk::CreateImageView(dev_, &view_info, nullptr, &view)); 638 image_views_.push_back(view); 639 640 VkFramebufferCreateInfo fb_info = {}; 641 fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; 642 fb_info.renderPass = render_pass_; 643 fb_info.attachmentCount = 1; 644 fb_info.pAttachments = &view; 645 fb_info.width = extent_.width; 646 fb_info.height = extent_.height; 647 fb_info.layers = 1; 648 649 VkFramebuffer fb; 650 vk::assert_success(vk::CreateFramebuffer(dev_, &fb_info, nullptr, &fb)); 651 framebuffers_.push_back(fb); 652 } 653 } 654 655 void Smoke::update_camera() 656 { 657 const glm::vec3 center(0.0f); 658 const glm::vec3 up(0.f, 0.0f, 1.0f); 659 const glm::mat4 view = glm::lookAt(camera_.eye_pos, center, up); 660 661 float aspect = static_cast<float>(extent_.width) / static_cast<float>(extent_.height); 662 const glm::mat4 projection = glm::perspective(0.4f, aspect, 0.1f, 100.0f); 663 664 // Vulkan clip space has inverted Y and half Z. 665 const glm::mat4 clip(1.0f, 0.0f, 0.0f, 0.0f, 666 0.0f, -1.0f, 0.0f, 0.0f, 667 0.0f, 0.0f, 0.5f, 0.0f, 668 0.0f, 0.0f, 0.5f, 1.0f); 669 670 camera_.view_projection = clip * projection * view; 671 } 672 673 void Smoke::draw_object(const Simulation::Object &obj, FrameData &data, VkCommandBuffer cmd) const 674 { 675 if (use_push_constants_) { 676 ShaderParamBlock params; 677 memcpy(params.light_pos, glm::value_ptr(obj.light_pos), sizeof(obj.light_pos)); 678 memcpy(params.light_color, glm::value_ptr(obj.light_color), sizeof(obj.light_color)); 679 memcpy(params.model, glm::value_ptr(obj.model), sizeof(obj.model)); 680 memcpy(params.view_projection, glm::value_ptr(camera_.view_projection), sizeof(camera_.view_projection)); 681 682 vk::CmdPushConstants(cmd, pipeline_layout_, VK_SHADER_STAGE_VERTEX_BIT, 683 0, sizeof(params), ¶ms); 684 } else { 685 ShaderParamBlock *params = 686 reinterpret_cast<ShaderParamBlock *>(data.base + obj.frame_data_offset); 687 memcpy(params->light_pos, glm::value_ptr(obj.light_pos), sizeof(obj.light_pos)); 688 memcpy(params->light_color, glm::value_ptr(obj.light_color), sizeof(obj.light_color)); 689 memcpy(params->model, glm::value_ptr(obj.model), sizeof(obj.model)); 690 memcpy(params->view_projection, glm::value_ptr(camera_.view_projection), sizeof(camera_.view_projection)); 691 692 vk::CmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, 693 pipeline_layout_, 0, 1, &data.desc_set, 1, &obj.frame_data_offset); 694 } 695 696 meshes_->cmd_draw(cmd, obj.mesh); 697 } 698 699 void Smoke::update_simulation(const Worker &worker) 700 { 701 sim_.update(worker.tick_interval_, worker.object_begin_, worker.object_end_); 702 } 703 704 void Smoke::draw_objects(Worker &worker) 705 { 706 auto &data = frame_data_[frame_data_index_]; 707 auto cmd = data.worker_cmds[worker.index_]; 708 709 VkCommandBufferInheritanceInfo inherit_info = {}; 710 inherit_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; 711 inherit_info.renderPass = render_pass_; 712 inherit_info.framebuffer = worker.fb_; 713 714 VkCommandBufferBeginInfo begin_info = {}; 715 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 716 begin_info.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; 717 begin_info.pInheritanceInfo = &inherit_info; 718 719 vk::BeginCommandBuffer(cmd, &begin_info); 720 721 vk::CmdSetViewport(cmd, 0, 1, &viewport_); 722 vk::CmdSetScissor(cmd, 0, 1, &scissor_); 723 724 vk::CmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_); 725 726 meshes_->cmd_bind_buffers(cmd); 727 728 for (int i = worker.object_begin_; i < worker.object_end_; i++) { 729 auto &obj = sim_.objects()[i]; 730 731 draw_object(obj, data, cmd); 732 } 733 734 vk::EndCommandBuffer(cmd); 735 } 736 737 void Smoke::on_key(Key key) 738 { 739 switch (key) { 740 case KEY_SHUTDOWN: 741 case KEY_ESC: 742 shell_->quit(); 743 break; 744 case KEY_UP: 745 camera_.eye_pos -= glm::vec3(0.05f); 746 update_camera(); 747 break; 748 case KEY_DOWN: 749 camera_.eye_pos += glm::vec3(0.05f); 750 update_camera(); 751 break; 752 case KEY_SPACE: 753 sim_paused_ = !sim_paused_; 754 break; 755 default: 756 break; 757 } 758 } 759 760 void Smoke::on_tick() 761 { 762 if (sim_paused_) 763 return; 764 765 for (auto &worker : workers_) 766 worker->update_simulation(); 767 } 768 769 void Smoke::on_frame(float frame_pred) 770 { 771 auto &data = frame_data_[frame_data_index_]; 772 773 // wait for the last submission since we reuse frame data 774 vk::assert_success(vk::WaitForFences(dev_, 1, &data.fence, true, UINT64_MAX)); 775 vk::assert_success(vk::ResetFences(dev_, 1, &data.fence)); 776 777 const Shell::BackBuffer &back = shell_->context().acquired_back_buffer; 778 779 // ignore frame_pred 780 for (auto &worker : workers_) 781 worker->draw_objects(framebuffers_[back.image_index]); 782 783 VkResult res = vk::BeginCommandBuffer(data.primary_cmd, &primary_cmd_begin_info_); 784 785 VkBufferMemoryBarrier buf_barrier = {}; 786 buf_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; 787 buf_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; 788 buf_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; 789 buf_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 790 buf_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 791 buf_barrier.buffer = data.buf; 792 buf_barrier.offset = 0; 793 buf_barrier.size = VK_WHOLE_SIZE; 794 vk::CmdPipelineBarrier(data.primary_cmd, 795 VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 796 0, 0, nullptr, 1, &buf_barrier, 0, nullptr); 797 798 render_pass_begin_info_.framebuffer = framebuffers_[back.image_index]; 799 render_pass_begin_info_.renderArea.extent = extent_; 800 vk::CmdBeginRenderPass(data.primary_cmd, &render_pass_begin_info_, 801 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); 802 803 // record render pass commands 804 for (auto &worker : workers_) 805 worker->wait_idle(); 806 vk::CmdExecuteCommands(data.primary_cmd, 807 static_cast<uint32_t>(data.worker_cmds.size()), 808 data.worker_cmds.data()); 809 810 vk::CmdEndRenderPass(data.primary_cmd); 811 vk::EndCommandBuffer(data.primary_cmd); 812 813 // wait for the image to be owned and signal for render completion 814 primary_cmd_submit_info_.pWaitSemaphores = &back.acquire_semaphore; 815 primary_cmd_submit_info_.pCommandBuffers = &data.primary_cmd; 816 primary_cmd_submit_info_.pSignalSemaphores = &back.render_semaphore; 817 818 res = vk::QueueSubmit(queue_, 1, &primary_cmd_submit_info_, data.fence); 819 820 frame_data_index_ = (frame_data_index_ + 1) % frame_data_.size(); 821 822 (void) res; 823 } 824 825 Smoke::Worker::Worker(Smoke &smoke, int index, int object_begin, int object_end) 826 : smoke_(smoke), index_(index), 827 object_begin_(object_begin), object_end_(object_end), 828 tick_interval_(1.0f / smoke.settings_.ticks_per_second), state_(INIT) 829 { 830 } 831 832 void Smoke::Worker::start() 833 { 834 state_ = IDLE; 835 thread_ = std::thread(Smoke::Worker::thread_loop, this); 836 } 837 838 void Smoke::Worker::stop() 839 { 840 { 841 std::lock_guard<std::mutex> lock(mutex_); 842 state_ = INIT; 843 } 844 state_cv_.notify_one(); 845 846 thread_.join(); 847 } 848 849 void Smoke::Worker::update_simulation() 850 { 851 { 852 std::lock_guard<std::mutex> lock(mutex_); 853 bool started = (state_ != INIT); 854 855 state_ = STEP; 856 857 // step directly 858 if (!started) { 859 smoke_.update_simulation(*this); 860 state_ = INIT; 861 } 862 } 863 state_cv_.notify_one(); 864 } 865 866 void Smoke::Worker::draw_objects(VkFramebuffer fb) 867 { 868 // wait for step_objects first 869 wait_idle(); 870 871 { 872 std::lock_guard<std::mutex> lock(mutex_); 873 bool started = (state_ != INIT); 874 875 fb_ = fb; 876 state_ = DRAW; 877 878 // render directly 879 if (!started) { 880 smoke_.draw_objects(*this); 881 state_ = INIT; 882 } 883 } 884 state_cv_.notify_one(); 885 } 886 887 void Smoke::Worker::wait_idle() 888 { 889 std::unique_lock<std::mutex> lock(mutex_); 890 bool started = (state_ != INIT); 891 892 if (started) 893 state_cv_.wait(lock, [this] { return (state_ == IDLE); }); 894 } 895 896 void Smoke::Worker::update_loop() 897 { 898 while (true) { 899 std::unique_lock<std::mutex> lock(mutex_); 900 901 state_cv_.wait(lock, [this] { return (state_ != IDLE); }); 902 if (state_ == INIT) 903 break; 904 905 assert(state_ == STEP || state_ == DRAW); 906 if (state_ == STEP) 907 smoke_.update_simulation(*this); 908 else 909 smoke_.draw_objects(*this); 910 911 state_ = IDLE; 912 lock.unlock(); 913 state_cv_.notify_one(); 914 } 915 } 916