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