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