|  | // Copyright 2017 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/ui/lib/escher/shape/mesh_spec.h" | 
|  |  | 
|  | #include <lib/syslog/cpp/macros.h> | 
|  |  | 
|  | #include <type_traits> | 
|  |  | 
|  | #include "src/ui/lib/escher/geometry/types.h" | 
|  | #include "src/ui/lib/escher/util/bit_ops.h" | 
|  | #include "src/ui/lib/escher/util/hasher.h" | 
|  |  | 
|  | namespace escher { | 
|  |  | 
|  | // If these assertions fail, code throughout this file will need to be updated | 
|  | // to match the new invariants. | 
|  | static_assert(sizeof(MeshAttributes) == sizeof(uint32_t), "sizeof mismatch"); | 
|  | static_assert(VulkanLimits::kNumVertexBuffers >= 2, "too few vertex buffers"); | 
|  |  | 
|  | uint32_t GetMeshAttributeSize(MeshAttribute attr) { | 
|  | switch (attr) { | 
|  | case MeshAttribute::kPosition2D: | 
|  | return sizeof(vec2); | 
|  | case MeshAttribute::kPosition3D: | 
|  | return sizeof(vec3); | 
|  | case MeshAttribute::kPositionOffset: | 
|  | return sizeof(vec2); | 
|  | case MeshAttribute::kUV: | 
|  | return sizeof(vec2); | 
|  | case MeshAttribute::kPerimeterPos: | 
|  | return sizeof(float); | 
|  | case MeshAttribute::kBlendWeight1: | 
|  | return sizeof(float); | 
|  | case MeshAttribute::kStride: | 
|  | FX_CHECK(false); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | uint32_t MeshSpec::attribute_count(uint32_t vertex_buffer_index) const { | 
|  | FX_DCHECK(vertex_buffer_index < VulkanLimits::kNumVertexBuffers); | 
|  | auto attrib = std::underlying_type<MeshAttribute>::type(attributes[vertex_buffer_index]); | 
|  | return CountOnes(static_cast<uint32_t>(attrib)); | 
|  | } | 
|  |  | 
|  | uint32_t MeshSpec::total_attribute_count() const { | 
|  | auto all_attribs = std::underlying_type<MeshAttribute>::type(all_attributes()); | 
|  | return CountOnes(static_cast<uint32_t>(all_attribs)); | 
|  | } | 
|  |  | 
|  | uint32_t MeshSpec::attribute_offset(uint32_t vertex_buffer_index, MeshAttribute flag) const { | 
|  | FX_DCHECK(vertex_buffer_index < VulkanLimits::kNumVertexBuffers); | 
|  | return GetMeshAttributeOffset(attributes[vertex_buffer_index], flag); | 
|  | } | 
|  |  | 
|  | uint32_t GetMeshAttributeOffset(const MeshAttributes& attrs, MeshAttribute attr) { | 
|  | FX_DCHECK(attrs & attr || attr == MeshAttribute::kStride); | 
|  | uint32_t offset = 0; | 
|  |  | 
|  | if (attr == MeshAttribute::kPosition2D) { | 
|  | return offset; | 
|  | } else if (attrs & MeshAttribute::kPosition2D) { | 
|  | offset += sizeof(vec2); | 
|  | } | 
|  |  | 
|  | if (attr == MeshAttribute::kPosition3D) { | 
|  | return offset; | 
|  | } else if (attrs & MeshAttribute::kPosition3D) { | 
|  | offset += sizeof(vec3); | 
|  | } | 
|  |  | 
|  | if (attr == MeshAttribute::kPositionOffset) { | 
|  | return offset; | 
|  | } else if (attrs & MeshAttribute::kPositionOffset) { | 
|  | offset += sizeof(vec2); | 
|  | } | 
|  |  | 
|  | if (attr == MeshAttribute::kUV) { | 
|  | return offset; | 
|  | } else if (attrs & MeshAttribute::kUV) { | 
|  | offset += sizeof(vec2); | 
|  | } | 
|  |  | 
|  | if (attr == MeshAttribute::kPerimeterPos) { | 
|  | return offset; | 
|  | } else if (attrs & MeshAttribute::kPerimeterPos) { | 
|  | offset += sizeof(float); | 
|  | } | 
|  |  | 
|  | if (attr == MeshAttribute::kBlendWeight1) { | 
|  | return offset; | 
|  | } else if (attrs & MeshAttribute::kBlendWeight1) { | 
|  | offset += sizeof(float); | 
|  | } | 
|  |  | 
|  | FX_DCHECK(attr == MeshAttribute::kStride); | 
|  | return offset; | 
|  | } | 
|  |  | 
|  | bool MeshSpec::has_attribute(uint32_t vertex_buffer_index, MeshAttribute attr) const { | 
|  | FX_DCHECK(vertex_buffer_index < VulkanLimits::kNumVertexBuffers); | 
|  | return bool(attributes[vertex_buffer_index] & attr); | 
|  | } | 
|  |  | 
|  | bool MeshSpec::has_attributes(uint32_t vertex_buffer_index, MeshAttributes attrs) const { | 
|  | FX_DCHECK(vertex_buffer_index < VulkanLimits::kNumVertexBuffers); | 
|  | return (attributes[vertex_buffer_index] & attrs) == attrs; | 
|  | } | 
|  |  | 
|  | MeshAttributes MeshSpec::all_attributes() const { | 
|  | MeshAttributes all(attributes[0]); | 
|  | for (uint32_t i = 1; i < VulkanLimits::kNumVertexBuffers; ++i) { | 
|  | FX_DCHECK((all & attributes[i]) == MeshAttributes()); | 
|  | all |= attributes[i]; | 
|  | } | 
|  | return all; | 
|  | } | 
|  |  | 
|  | uint32_t MeshSpec::vertex_buffer_count() const { | 
|  | uint32_t count = 0; | 
|  | for (auto& attrs : attributes) { | 
|  | auto attr = std::underlying_type<MeshAttribute>::type(attrs); | 
|  | count += static_cast<uint32_t>(attr) ? 1 : 0; | 
|  | } | 
|  | return count; | 
|  | } | 
|  |  | 
|  | bool MeshSpec::IsValid() const { | 
|  | MeshAttributes all_attrs = all_attributes(); | 
|  | auto position_attrs = MeshAttribute::kPosition2D | MeshAttribute::kPosition3D; | 
|  | if (!(all_attrs & position_attrs)) { | 
|  | // Mesh must have a position attribute, either 2D or 3D. | 
|  | return false; | 
|  | } else if ((all_attrs & position_attrs) == position_attrs) { | 
|  | return false; | 
|  | } else { | 
|  | // Position attribute must always be in the first vertex buffer. | 
|  | return bool(attributes[0] & position_attrs); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool MeshSpec::IsValidOneBufferMesh() const { | 
|  | if (!IsValid()) { | 
|  | return false; | 
|  | } else if (attribute_count(0) != total_attribute_count()) { | 
|  | // Only the first vertex buffer is allowed to have any attributes. | 
|  | return false; | 
|  | } else if (has_attribute(0, MeshAttribute::kPosition3D) && | 
|  | (has_attribute(0, MeshAttribute::kPositionOffset) || | 
|  | has_attribute(0, MeshAttribute::kPerimeterPos))) { | 
|  | // Position-offset and perimeter attributes are only allowed for 2D meshes. | 
|  | // The latter inherently only makes sense for 2D, whereas the former could | 
|  | // be modified to support both 2D and 3D variants. | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::size_t MeshSpec::HashMapHasher::operator()(const MeshSpec& spec) const { | 
|  | Hasher h; | 
|  | for (auto& attrs : spec.attributes) { | 
|  | auto attr = std::underlying_type<MeshAttribute>::type(attrs); | 
|  | h.u32(static_cast<uint32_t>(attr)); | 
|  | } | 
|  | return h.value().val; | 
|  | } | 
|  |  | 
|  | }  // namespace escher |