#pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace gl { template struct DataTypeOf; template <> struct DataTypeOf< int8_t> : std::integral_constant {}; template <> struct DataTypeOf : std::integral_constant {}; template <> struct DataTypeOf< int16_t> : std::integral_constant {}; template <> struct DataTypeOf : std::integral_constant {}; template <> struct DataTypeOf< int32_t> : std::integral_constant {}; template <> struct DataTypeOf : std::integral_constant {}; template <> struct DataTypeOf : std::integral_constant {}; class AttributeBinding { public: DataType attributeType; uint8_t attributeSize; uint32_t attributeOffset; BufferID vertexBuffer; uint32_t vertexSize; uint32_t vertexOffset; friend bool operator==(const AttributeBinding& lhs, const AttributeBinding& rhs) { return std::tie(lhs.attributeType, lhs.attributeSize, lhs.attributeOffset, lhs.vertexBuffer, lhs.vertexSize, lhs.vertexOffset) == std::tie(rhs.attributeType, rhs.attributeSize, rhs.attributeOffset, rhs.vertexBuffer, rhs.vertexSize, rhs.vertexOffset); } }; using AttributeBindingArray = std::vector>; /* gl::Attribute manages the binding of a vertex buffer to a GL program attribute. - T is the underlying primitive type (exposed as Attribute::ValueType) - N is the number of components in the attribute declared in the shader (exposed as Attribute::Dimensions) */ template class Attribute { public: using ValueType = T; static constexpr size_t Dimensions = N; using Value = std::array; using Location = AttributeLocation; using Binding = AttributeBinding; /* Create a binding for this attribute. The `attributeSize` parameter may be used to override the number of components available in the buffer for each vertex. Thus, a buffer with only one float for each vertex can be bound to a `vec2` attribute */ template static Binding binding(const VertexBuffer& buffer, std::size_t attributeIndex, std::size_t attributeSize = N) { static_assert(std::is_standard_layout::value, "vertex type must use standard layout"); assert(attributeSize >= 1); assert(attributeSize <= 4); assert(Vertex::attributeOffsets[attributeIndex] <= std::numeric_limits::max()); static_assert(sizeof(Vertex) <= std::numeric_limits::max(), "vertex too large"); return AttributeBinding { DataTypeOf::value, static_cast(attributeSize), static_cast(Vertex::attributeOffsets[attributeIndex]), buffer.buffer, static_cast(sizeof(Vertex)), 0, }; } static optional offsetBinding(const optional& binding, std::size_t vertexOffset) { assert(vertexOffset <= std::numeric_limits::max()); if (binding) { AttributeBinding result = *binding; result.vertexOffset = static_cast(vertexOffset); return result; } else { return binding; } } }; #define MBGL_DEFINE_ATTRIBUTE(type_, n_, name_) \ struct name_ { \ static auto name() { return #name_; } \ using Type = ::mbgl::gl::Attribute; \ } namespace detail { // Attribute binding requires member offsets. The only standard way to // obtain an offset is the offsetof macro. The offsetof macro has defined // behavior only for standard layout types. That rules out std::tuple and // any other solution that relies on chained inheritance. Manually implemented // variadic specialization looks like the only solution. Fortunately, we // only use a maximum of five attributes. template class Vertex; template <> class Vertex<> { public: using VertexType = Vertex<>; }; template class Vertex { public: typename A1::Value a1; using VertexType = Vertex; static const std::size_t attributeOffsets[1]; }; template class Vertex { public: typename A1::Value a1; typename A2::Value a2; using VertexType = Vertex; static const std::size_t attributeOffsets[2]; }; template class Vertex { public: typename A1::Value a1; typename A2::Value a2; typename A3::Value a3; using VertexType = Vertex; static const std::size_t attributeOffsets[3]; }; template class Vertex { public: typename A1::Value a1; typename A2::Value a2; typename A3::Value a3; typename A4::Value a4; using VertexType = Vertex; static const std::size_t attributeOffsets[4]; }; template class Vertex { public: typename A1::Value a1; typename A2::Value a2; typename A3::Value a3; typename A4::Value a4; typename A5::Value a5; using VertexType = Vertex; static const std::size_t attributeOffsets[5]; }; template const std::size_t Vertex::attributeOffsets[1] = { offsetof(VertexType, a1) }; template const std::size_t Vertex::attributeOffsets[2] = { offsetof(VertexType, a1), offsetof(VertexType, a2) }; template const std::size_t Vertex::attributeOffsets[3] = { offsetof(VertexType, a1), offsetof(VertexType, a2), offsetof(VertexType, a3) }; template const std::size_t Vertex::attributeOffsets[4] = { offsetof(VertexType, a1), offsetof(VertexType, a2), offsetof(VertexType, a3), offsetof(VertexType, a4) }; template const std::size_t Vertex::attributeOffsets[5] = { offsetof(VertexType, a1), offsetof(VertexType, a2), offsetof(VertexType, a3), offsetof(VertexType, a4), offsetof(VertexType, a5) }; } // namespace detail class Context; void bindAttributeLocation(Context&, ProgramID, AttributeLocation, const char * name); std::set getActiveAttributes(ProgramID); template class Attributes { public: using Types = TypeList; using Locations = IndexedTuple< TypeList, TypeList...>>; using Bindings = IndexedTuple< TypeList, TypeList...>>; using NamedLocations = std::vector>; using Vertex = detail::Vertex; static Locations bindLocations(Context& context, const ProgramID& id) { std::set activeAttributes = getActiveAttributes(id); AttributeLocation location = 0; auto maybeBindLocation = [&](const char* name) -> optional { if (activeAttributes.count(name)) { bindAttributeLocation(context, id, location, name); return location++; } else { return {}; } }; return Locations { maybeBindLocation(As::name())... }; } template static Locations loadNamedLocations(const Program& program) { return Locations{ program.attributeLocation(As::name())... }; } static NamedLocations getNamedLocations(const Locations& locations) { NamedLocations result; auto maybeAddLocation = [&] (const std::string& name, const optional& location) { if (location) { result.emplace_back(name, *location); } }; util::ignore({ (maybeAddLocation(As::name(), locations.template get()), 0)... }); return result; } template static Bindings bindings(const VertexBuffer& buffer) { return Bindings { As::Type::binding(buffer, TypeIndex::value)... }; } static Bindings offsetBindings(const Bindings& bindings, std::size_t vertexOffset) { return Bindings { As::Type::offsetBinding(bindings.template get(), vertexOffset)... }; } static AttributeBindingArray toBindingArray(const Locations& locations, const Bindings& bindings) { AttributeBindingArray result; result.resize(sizeof...(As)); auto maybeAddBinding = [&] (const optional& location, const optional& binding) { if (location) { result.at(*location) = binding; } }; util::ignore({ (maybeAddBinding(locations.template get(), bindings.template get()), 0)... }); return result; } static uint32_t activeBindingCount(const Bindings& bindings) { uint32_t result = 0; util::ignore({ ((result += bool(bindings.template get())), 0)... }); return result; } }; namespace detail { template struct ConcatenateAttributes; template struct ConcatenateAttributes, TypeList> { using Type = Attributes; }; } // namespace detail template using ConcatenateAttributes = typename detail::ConcatenateAttributes< typename A::Types, typename B::Types>::Type; } // namespace gl } // namespace mbgl