Home | History | Annotate | Download | only in smoke
      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 
     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 
     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 
     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 
     83     static VkIndexType index_type()
     84     {
     85         return VK_INDEX_TYPE_UINT32;
     86     }
     87 
     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 
     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 
    111     uint32_t vertex_count() const
    112     {
    113         return static_cast<uint32_t>(positions_.size());
    114     }
    115 
    116     VkDeviceSize vertex_buffer_size() const
    117     {
    118         return vertex_stride() * vertex_count();
    119     }
    120 
    121     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 
    137     uint32_t index_count() const
    138     {
    139         return static_cast<uint32_t>(faces_.size()) * 3;
    140     }
    141 
    142     VkDeviceSize index_buffer_size() const
    143     {
    144         return sizeof(uint32_t) * index_count();
    145     }
    146 
    147     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:
    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:
    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:
    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 
    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 
    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:
    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 
    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 
    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 
    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 
    468 Meshes::~Meshes()
    469 {
    470     vk::FreeMemory(dev_, mem_, nullptr);
    471     vk::DestroyBuffer(dev_, vb_, nullptr);
    472     vk::DestroyBuffer(dev_, ib_, nullptr);
    473 }
    474 
    475 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 
    483 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 
    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