• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Smoke(const std::vector<std::string> & args)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 
~Smoke()63 Smoke::~Smoke()
64 {
65 }
66 
init_workers()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 
attach_shell(Shell & sh)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 
detach_shell()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 
create_render_pass()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 
create_shader_modules()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 
create_descriptor_set_layout()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 
create_pipeline_layout()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 
create_pipeline()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 
create_frame_data(int count)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 
destroy_frame_data()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 
create_fences()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 
create_command_buffers()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 
create_buffers()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 
create_buffer_memory()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 
create_descriptor_sets()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 
attach_swapchain()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 
detach_swapchain()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 
prepare_viewport(const VkExtent2D & extent)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 
prepare_framebuffers(VkSwapchainKHR swapchain)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 
update_camera()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 
draw_object(const Simulation::Object & obj,FrameData & data,VkCommandBuffer cmd) const673 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 
update_simulation(const Worker & worker)699 void Smoke::update_simulation(const Worker &worker)
700 {
701     sim_.update(worker.tick_interval_, worker.object_begin_, worker.object_end_);
702 }
703 
draw_objects(Worker & worker)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 
on_key(Key key)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 
on_tick()760 void Smoke::on_tick()
761 {
762     if (sim_paused_)
763         return;
764 
765     for (auto &worker : workers_)
766         worker->update_simulation();
767 }
768 
on_frame(float frame_pred)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 
Worker(Smoke & smoke,int index,int object_begin,int object_end)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 
start()832 void Smoke::Worker::start()
833 {
834     state_ = IDLE;
835     thread_ = std::thread(Smoke::Worker::thread_loop, this);
836 }
837 
stop()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 
update_simulation()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 
draw_objects(VkFramebuffer fb)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 
wait_idle()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 
update_loop()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