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