Home | History | Annotate | Download | only in smoke
      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), &params);
    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