• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 Google, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <cassert>
18 #include <cmath>
19 #include <cstring>
20 #include <array>
21 #include <unordered_map>
22 
23 #include "Helpers.h"
24 #include "Meshes.h"
25 
26 namespace {
27 
28 class Mesh {
29 public:
30     struct Position {
31         float x;
32         float y;
33         float z;
34     };
35 
36     struct Normal {
37         float x;
38         float y;
39         float z;
40     };
41 
42     struct Face {
43         int v0;
44         int v1;
45         int v2;
46     };
47 
vertex_stride()48     static uint32_t vertex_stride()
49     {
50         // Position + Normal
51         const int comp_count = 6;
52 
53         return sizeof(float) * comp_count;
54     }
55 
vertex_input_binding()56     static VkVertexInputBindingDescription vertex_input_binding()
57     {
58         VkVertexInputBindingDescription vi_binding = {};
59         vi_binding.binding = 0;
60         vi_binding.stride = vertex_stride();
61         vi_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
62 
63         return vi_binding;
64     }
65 
vertex_input_attributes()66     static std::vector<VkVertexInputAttributeDescription> vertex_input_attributes()
67     {
68         std::vector<VkVertexInputAttributeDescription> vi_attrs(2);
69         // Position
70         vi_attrs[0].location = 0;
71         vi_attrs[0].binding = 0;
72         vi_attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
73         vi_attrs[0].offset = 0;
74         // Normal
75         vi_attrs[1].location = 1;
76         vi_attrs[1].binding = 0;
77         vi_attrs[1].format = VK_FORMAT_R32G32B32_SFLOAT;
78         vi_attrs[1].offset = sizeof(float) * 3;
79 
80         return vi_attrs;
81     }
82 
index_type()83     static VkIndexType index_type()
84     {
85         return VK_INDEX_TYPE_UINT32;
86     }
87 
input_assembly_state()88     static VkPipelineInputAssemblyStateCreateInfo input_assembly_state()
89     {
90         VkPipelineInputAssemblyStateCreateInfo ia_info = {};
91         ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
92         ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
93         ia_info.primitiveRestartEnable = false;
94         return ia_info;
95     }
96 
build(const std::vector<std::array<float,6>> & vertices,const std::vector<std::array<int,3>> & faces)97     void build(const std::vector<std::array<float, 6>> &vertices, const std::vector<std::array<int, 3>> &faces)
98     {
99         positions_.reserve(vertices.size());
100         normals_.reserve(vertices.size());
101         for (const auto &v : vertices) {
102             positions_.emplace_back(Position{ v[0], v[1], v[2] });
103             normals_.emplace_back(Normal{ v[3], v[4], v[5] });
104         }
105 
106         faces_.reserve(faces.size());
107         for (const auto &f : faces)
108             faces_.emplace_back(Face{ f[0], f[1], f[2] });
109     }
110 
vertex_count() const111     uint32_t vertex_count() const
112     {
113         return static_cast<uint32_t>(positions_.size());
114     }
115 
vertex_buffer_size() const116     VkDeviceSize vertex_buffer_size() const
117     {
118         return vertex_stride() * vertex_count();
119     }
120 
vertex_buffer_write(void * data) const121     void vertex_buffer_write(void *data) const
122     {
123         float *dst = reinterpret_cast<float *>(data);
124         for (size_t i = 0; i < positions_.size(); i++) {
125             const Position &pos = positions_[i];
126             const Normal &normal = normals_[i];
127             dst[0] = pos.x;
128             dst[1] = pos.y;
129             dst[2] = pos.z;
130             dst[3] = normal.x;
131             dst[4] = normal.y;
132             dst[5] = normal.z;
133             dst += 6;
134         }
135     }
136 
index_count() const137     uint32_t index_count() const
138     {
139         return static_cast<uint32_t>(faces_.size()) * 3;
140     }
141 
index_buffer_size() const142     VkDeviceSize index_buffer_size() const
143     {
144         return sizeof(uint32_t) * index_count();
145     }
146 
index_buffer_write(void * data) const147     void index_buffer_write(void *data) const
148     {
149         uint32_t *dst = reinterpret_cast<uint32_t *>(data);
150         for (const auto &face : faces_) {
151             dst[0] = face.v0;
152             dst[1] = face.v1;
153             dst[2] = face.v2;
154             dst += 3;
155         }
156     }
157 
158     std::vector<Position> positions_;
159     std::vector<Normal> normals_;
160     std::vector<Face> faces_;
161 };
162 
163 class BuildPyramid {
164 public:
BuildPyramid(Mesh & mesh)165     BuildPyramid(Mesh &mesh)
166     {
167         const std::vector<std::array<float, 6>> vertices = {
168             //      position                normal
169             {  0.0f,  0.0f,  1.0f,    0.0f,  0.0f,  1.0f },
170             { -1.0f, -1.0f, -1.0f,   -1.0f, -1.0f, -1.0f },
171             {  1.0f, -1.0f, -1.0f,    1.0f, -1.0f, -1.0f },
172             {  1.0f,  1.0f, -1.0f,    1.0f,  1.0f, -1.0f },
173             { -1.0f,  1.0f, -1.0f,   -1.0f,  1.0f, -1.0f },
174         };
175 
176         const std::vector<std::array<int, 3>> faces = {
177             { 0, 1, 2 },
178             { 0, 2, 3 },
179             { 0, 3, 4 },
180             { 0, 4, 1 },
181             { 1, 4, 3 },
182             { 1, 3, 2 },
183         };
184 
185         mesh.build(vertices, faces);
186     }
187 };
188 
189 class BuildIcosphere {
190 public:
BuildIcosphere(Mesh & mesh)191     BuildIcosphere(Mesh &mesh) : mesh_(mesh), radius_(1.0f)
192     {
193         const int tessellate_level = 2;
194 
195         build_icosahedron();
196         for (int i = 0; i < tessellate_level; i++)
197             tessellate();
198     }
199 
200 private:
build_icosahedron()201     void build_icosahedron()
202     {
203         // https://en.wikipedia.org/wiki/Regular_icosahedron
204         const float l1 = std::sqrt(2.0f / (5.0f + std::sqrt(5.0f))) * radius_;
205         const float l2 = std::sqrt(2.0f / (5.0f - std::sqrt(5.0f))) * radius_;
206         // vertices are from three golden rectangles
207         const std::vector<std::array<float, 6>> icosahedron_vertices = {
208             //   position           normal
209             { -l1, -l2, 0.0f,   -l1, -l2, 0.0f, },
210             {  l1, -l2, 0.0f,    l1, -l2, 0.0f, },
211             {  l1,  l2, 0.0f,    l1,  l2, 0.0f, },
212             { -l1,  l2, 0.0f,   -l1,  l2, 0.0f, },
213 
214             { -l2, 0.0f, -l1,   -l2, 0.0f, -l1, },
215             {  l2, 0.0f, -l1,    l2, 0.0f, -l1, },
216             {  l2, 0.0f,  l1,    l2, 0.0f,  l1, },
217             { -l2, 0.0f,  l1,   -l2, 0.0f,  l1, },
218 
219             { 0.0f, -l1, -l2,   0.0f, -l1, -l2, },
220             { 0.0f,  l1, -l2,   0.0f,  l1, -l2, },
221             { 0.0f,  l1,  l2,   0.0f,  l1,  l2, },
222             { 0.0f, -l1,  l2,   0.0f, -l1,  l2, },
223         };
224         const std::vector<std::array<int, 3>> icosahedron_faces = {
225             // triangles sharing vertex 0
226             {  0,  1, 11 },
227             {  0, 11,  7 },
228             {  0,  7,  4 },
229             {  0,  4,  8 },
230             {  0,  8,  1 },
231             // adjacent triangles
232             { 11,  1,  6 },
233             {  7, 11, 10 },
234             {  4,  7,  3 },
235             {  8,  4,  9 },
236             {  1,  8,  5 },
237             // triangles sharing vertex 2
238             {  2,  3, 10 },
239             {  2, 10,  6 },
240             {  2,  6,  5 },
241             {  2,  5,  9 },
242             {  2,  9,  3 },
243             // adjacent triangles
244             { 10,  3,  7 },
245             {  6, 10, 11 },
246             {  5,  6,  1 },
247             {  9,  5,  8 },
248             {  3,  9,  4 },
249         };
250 
251         mesh_.build(icosahedron_vertices, icosahedron_faces);
252     }
253 
tessellate()254     void tessellate()
255     {
256         size_t middle_point_count = mesh_.faces_.size() * 3 / 2;
257         size_t final_face_count = mesh_.faces_.size() * 4;
258 
259         std::vector<Mesh::Face> faces;
260         faces.reserve(final_face_count);
261 
262         middle_points_.clear();
263         middle_points_.reserve(middle_point_count);
264 
265         mesh_.positions_.reserve(mesh_.vertex_count() + middle_point_count);
266         mesh_.normals_.reserve(mesh_.vertex_count() + middle_point_count);
267 
268         for (const auto &f : mesh_.faces_) {
269             int v0 = f.v0;
270             int v1 = f.v1;
271             int v2 = f.v2;
272 
273             int v01 = add_middle_point(v0, v1);
274             int v12 = add_middle_point(v1, v2);
275             int v20 = add_middle_point(v2, v0);
276 
277             faces.emplace_back(Mesh::Face{ v0, v01, v20 });
278             faces.emplace_back(Mesh::Face{ v1, v12, v01 });
279             faces.emplace_back(Mesh::Face{ v2, v20, v12 });
280             faces.emplace_back(Mesh::Face{ v01, v12, v20 });
281         }
282 
283         mesh_.faces_.swap(faces);
284     }
285 
add_middle_point(int a,int b)286     int add_middle_point(int a, int b)
287     {
288         uint64_t key = (a < b) ? ((uint64_t) a << 32 | b) : ((uint64_t) b << 32 | a);
289         auto it = middle_points_.find(key);
290         if (it != middle_points_.end())
291             return it->second;
292 
293         const Mesh::Position &pos_a = mesh_.positions_[a];
294         const Mesh::Position &pos_b = mesh_.positions_[b];
295         Mesh::Position pos_mid = {
296             (pos_a.x + pos_b.x) / 2.0f,
297             (pos_a.y + pos_b.y) / 2.0f,
298             (pos_a.z + pos_b.z) / 2.0f,
299         };
300         float scale = radius_ / std::sqrt(pos_mid.x * pos_mid.x +
301                                           pos_mid.y * pos_mid.y +
302                                           pos_mid.z * pos_mid.z);
303         pos_mid.x *= scale;
304         pos_mid.y *= scale;
305         pos_mid.z *= scale;
306 
307         Mesh::Normal normal_mid = { pos_mid.x, pos_mid.y, pos_mid.z };
308         normal_mid.x /= radius_;
309         normal_mid.y /= radius_;
310         normal_mid.z /= radius_;
311 
312         mesh_.positions_.emplace_back(pos_mid);
313         mesh_.normals_.emplace_back(normal_mid);
314 
315         int mid = mesh_.vertex_count() - 1;
316         middle_points_.emplace(std::make_pair(key, mid));
317 
318         return mid;
319     }
320 
321     Mesh &mesh_;
322     const float radius_;
323     std::unordered_map<uint64_t, uint32_t> middle_points_;
324 };
325 
326 class BuildTeapot {
327 public:
BuildTeapot(Mesh & mesh)328     BuildTeapot(Mesh &mesh)
329     {
330 #include "Meshes.teapot.h"
331         const int position_count = sizeof(teapot_positions) / sizeof(teapot_positions[0]);
332         const int index_count = sizeof(teapot_indices) / sizeof(teapot_indices[0]);
333         assert(position_count % 3 == 0 && index_count % 3 == 0);
334 
335         Mesh::Position translate;
336         float scale;
337         get_transform(teapot_positions, position_count, translate, scale);
338 
339         for (int i = 0; i < position_count; i += 3) {
340             mesh.positions_.emplace_back(Mesh::Position{
341                 (teapot_positions[i + 0] + translate.x) * scale,
342                 (teapot_positions[i + 1] + translate.y) * scale,
343                 (teapot_positions[i + 2] + translate.z) * scale,
344             });
345 
346             mesh.normals_.emplace_back(Mesh::Normal{
347                 teapot_normals[i + 0],
348                 teapot_normals[i + 1],
349                 teapot_normals[i + 2],
350             });
351         }
352 
353         for (int i = 0; i < index_count; i += 3) {
354             mesh.faces_.emplace_back(Mesh::Face{
355                 teapot_indices[i + 0],
356                 teapot_indices[i + 1],
357                 teapot_indices[i + 2]
358             });
359         }
360     }
361 
get_transform(const float * positions,int position_count,Mesh::Position & translate,float & scale)362     void get_transform(const float *positions, int position_count,
363                        Mesh::Position &translate, float &scale)
364     {
365         float min[3] = {
366             positions[0],
367             positions[1],
368             positions[2],
369         };
370         float max[3] = {
371             positions[0],
372             positions[1],
373             positions[2],
374         };
375         for (int i = 3; i < position_count; i += 3) {
376             for (int j = 0; j < 3; j++) {
377                 if (min[j] > positions[i + j])
378                     min[j] = positions[i + j];
379                 if (max[j] < positions[i + j])
380                     max[j] = positions[i + j];
381             }
382         }
383 
384         translate.x = -(min[0] + max[0]) / 2.0f;
385         translate.y = -(min[1] + max[1]) / 2.0f;
386         translate.z = -(min[2] + max[2]) / 2.0f;
387 
388         float extents[3] = {
389             max[0] + translate.x,
390             max[1] + translate.y,
391             max[2] + translate.z,
392         };
393 
394         float max_extent = extents[0];
395         if (max_extent < extents[1])
396             max_extent = extents[1];
397         if (max_extent < extents[2])
398             max_extent = extents[2];
399 
400         scale = 1.0f / max_extent;
401     }
402 };
403 
build_meshes(std::array<Mesh,Meshes::MESH_COUNT> & meshes)404 void build_meshes(std::array<Mesh, Meshes::MESH_COUNT> &meshes)
405 {
406     BuildPyramid build_pyramid(meshes[Meshes::MESH_PYRAMID]);
407     BuildIcosphere build_icosphere(meshes[Meshes::MESH_ICOSPHERE]);
408     BuildTeapot build_teapot(meshes[Meshes::MESH_TEAPOT]);
409 }
410 
411 } // namespace
412 
Meshes(VkDevice dev,const std::vector<VkMemoryPropertyFlags> & mem_flags)413 Meshes::Meshes(VkDevice dev, const std::vector<VkMemoryPropertyFlags> &mem_flags)
414     : dev_(dev),
415       vertex_input_binding_(Mesh::vertex_input_binding()),
416       vertex_input_attrs_(Mesh::vertex_input_attributes()),
417       vertex_input_state_(),
418       input_assembly_state_(Mesh::input_assembly_state()),
419       index_type_(Mesh::index_type())
420 {
421     vertex_input_state_.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
422     vertex_input_state_.vertexBindingDescriptionCount = 1;
423     vertex_input_state_.pVertexBindingDescriptions = &vertex_input_binding_;
424     vertex_input_state_.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertex_input_attrs_.size());
425     vertex_input_state_.pVertexAttributeDescriptions = vertex_input_attrs_.data();
426 
427     std::array<Mesh, MESH_COUNT> meshes;
428     build_meshes(meshes);
429 
430     draw_commands_.reserve(meshes.size());
431     uint32_t first_index = 0;
432     int32_t vertex_offset = 0;
433     VkDeviceSize vb_size = 0;
434     VkDeviceSize ib_size = 0;
435     for (const auto &mesh : meshes) {
436         VkDrawIndexedIndirectCommand draw = {};
437         draw.indexCount = mesh.index_count();
438         draw.instanceCount = 1;
439         draw.firstIndex = first_index;
440         draw.vertexOffset = vertex_offset;
441         draw.firstInstance = 0;
442 
443         draw_commands_.push_back(draw);
444 
445         first_index += mesh.index_count();
446         vertex_offset += mesh.vertex_count();
447         vb_size += mesh.vertex_buffer_size();
448         ib_size += mesh.index_buffer_size();
449     }
450 
451     allocate_resources(vb_size, ib_size, mem_flags);
452 
453     uint8_t *vb_data, *ib_data;
454     vk::assert_success(vk::MapMemory(dev_, mem_, 0, VK_WHOLE_SIZE,
455                 0, reinterpret_cast<void **>(&vb_data)));
456     ib_data = vb_data + ib_mem_offset_;
457 
458     for (const auto &mesh : meshes) {
459         mesh.vertex_buffer_write(vb_data);
460         mesh.index_buffer_write(ib_data);
461         vb_data += mesh.vertex_buffer_size();
462         ib_data += mesh.index_buffer_size();
463     }
464 
465     vk::UnmapMemory(dev_, mem_);
466 }
467 
~Meshes()468 Meshes::~Meshes()
469 {
470     vk::FreeMemory(dev_, mem_, nullptr);
471     vk::DestroyBuffer(dev_, vb_, nullptr);
472     vk::DestroyBuffer(dev_, ib_, nullptr);
473 }
474 
cmd_bind_buffers(VkCommandBuffer cmd) const475 void Meshes::cmd_bind_buffers(VkCommandBuffer cmd) const
476 {
477     const VkDeviceSize vb_offset = 0;
478     vk::CmdBindVertexBuffers(cmd, 0, 1, &vb_, &vb_offset);
479 
480     vk::CmdBindIndexBuffer(cmd, ib_, 0, index_type_);
481 }
482 
cmd_draw(VkCommandBuffer cmd,Type type) const483 void Meshes::cmd_draw(VkCommandBuffer cmd, Type type) const
484 {
485     const auto &draw = draw_commands_[type];
486     vk::CmdDrawIndexed(cmd, draw.indexCount, draw.instanceCount,
487             draw.firstIndex, draw.vertexOffset, draw.firstInstance);
488 }
489 
allocate_resources(VkDeviceSize vb_size,VkDeviceSize ib_size,const std::vector<VkMemoryPropertyFlags> & mem_flags)490 void Meshes::allocate_resources(VkDeviceSize vb_size, VkDeviceSize ib_size, const std::vector<VkMemoryPropertyFlags> &mem_flags)
491 {
492     VkBufferCreateInfo buf_info = {};
493     buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
494     buf_info.size = vb_size;
495     buf_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
496     buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
497     vk::CreateBuffer(dev_, &buf_info, nullptr, &vb_);
498 
499     buf_info.size = ib_size;
500     buf_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
501     vk::CreateBuffer(dev_, &buf_info, nullptr, &ib_);
502 
503     VkMemoryRequirements vb_mem_reqs, ib_mem_reqs;
504     vk::GetBufferMemoryRequirements(dev_, vb_, &vb_mem_reqs);
505     vk::GetBufferMemoryRequirements(dev_, ib_, &ib_mem_reqs);
506 
507     // indices follow vertices
508     ib_mem_offset_ = vb_mem_reqs.size +
509         (ib_mem_reqs.alignment - (vb_mem_reqs.size % ib_mem_reqs.alignment));
510 
511     VkMemoryAllocateInfo mem_info = {};
512     mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
513     mem_info.allocationSize = ib_mem_offset_ + ib_mem_reqs.size;
514 
515     // find any supported and mappable memory type
516     uint32_t mem_types = (vb_mem_reqs.memoryTypeBits & ib_mem_reqs.memoryTypeBits);
517     for (uint32_t idx = 0; idx < mem_flags.size(); idx++) {
518         if ((mem_types & (1 << idx)) &&
519             (mem_flags[idx] & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) &&
520             (mem_flags[idx] & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
521             // TODO this may not be reachable
522             mem_info.memoryTypeIndex = idx;
523             break;
524         }
525     }
526 
527     vk::AllocateMemory(dev_, &mem_info, nullptr, &mem_);
528 
529     vk::BindBufferMemory(dev_, vb_, mem_, 0);
530     vk::BindBufferMemory(dev_, ib_, mem_, ib_mem_offset_);
531 }
532