1 #pragma once
2 
3 #include <mbgl/gl/types.hpp>
4 #include <mbgl/gl/vertex_buffer.hpp>
5 #include <mbgl/util/ignore.hpp>
6 #include <mbgl/util/indexed_tuple.hpp>
7 #include <mbgl/util/optional.hpp>
8 
9 #include <cstddef>
10 #include <vector>
11 #include <set>
12 #include <functional>
13 #include <string>
14 #include <array>
15 #include <limits>
16 
17 namespace mbgl {
18 namespace gl {
19 
20 template <class> struct DataTypeOf;
21 template <> struct DataTypeOf< int8_t>  : std::integral_constant<DataType, DataType::Byte> {};
22 template <> struct DataTypeOf<uint8_t>  : std::integral_constant<DataType, DataType::UnsignedByte> {};
23 template <> struct DataTypeOf< int16_t> : std::integral_constant<DataType, DataType::Short> {};
24 template <> struct DataTypeOf<uint16_t> : std::integral_constant<DataType, DataType::UnsignedShort> {};
25 template <> struct DataTypeOf< int32_t> : std::integral_constant<DataType, DataType::Integer> {};
26 template <> struct DataTypeOf<uint32_t> : std::integral_constant<DataType, DataType::UnsignedInteger> {};
27 template <> struct DataTypeOf<float>    : std::integral_constant<DataType, DataType::Float> {};
28 
29 class AttributeBinding {
30 public:
31     DataType attributeType;
32     uint8_t attributeSize;
33     uint32_t attributeOffset;
34 
35     BufferID vertexBuffer;
36     uint32_t vertexSize;
37     uint32_t vertexOffset;
38 
operator ==(const AttributeBinding & lhs,const AttributeBinding & rhs)39     friend bool operator==(const AttributeBinding& lhs,
40                            const AttributeBinding& rhs) {
41         return std::tie(lhs.attributeType, lhs.attributeSize, lhs.attributeOffset, lhs.vertexBuffer, lhs.vertexSize, lhs.vertexOffset)
42             == std::tie(rhs.attributeType, rhs.attributeSize, rhs.attributeOffset, rhs.vertexBuffer, rhs.vertexSize, rhs.vertexOffset);
43     }
44 };
45 
46 using AttributeBindingArray = std::vector<optional<AttributeBinding>>;
47 
48 /*
49     gl::Attribute<T,N> manages the binding of a vertex buffer to a GL program attribute.
50       - T is the underlying primitive type (exposed as Attribute<T,N>::ValueType)
51       - N is the number of components in the attribute declared in the shader (exposed as Attribute<T,N>::Dimensions)
52 */
53 template <class T, std::size_t N>
54 class Attribute {
55 public:
56     using ValueType = T;
57     static constexpr size_t Dimensions = N;
58     using Value = std::array<T, N>;
59 
60     using Location = AttributeLocation;
61     using Binding = AttributeBinding;
62 
63     /*
64         Create a binding for this attribute.  The `attributeSize` parameter may be used to
65         override the number of components available in the buffer for each vertex.  Thus,
66         a buffer with only one float for each vertex can be bound to a `vec2` attribute
67     */
68     template <class Vertex, class DrawMode>
binding(const VertexBuffer<Vertex,DrawMode> & buffer,std::size_t attributeIndex,std::size_t attributeSize=N)69     static Binding binding(const VertexBuffer<Vertex, DrawMode>& buffer,
70                            std::size_t attributeIndex,
71                            std::size_t attributeSize = N) {
72         static_assert(std::is_standard_layout<Vertex>::value, "vertex type must use standard layout");
73         assert(attributeSize >= 1);
74         assert(attributeSize <= 4);
75         assert(Vertex::attributeOffsets[attributeIndex] <= std::numeric_limits<uint32_t>::max());
76         static_assert(sizeof(Vertex) <= std::numeric_limits<uint32_t>::max(), "vertex too large");
77         return AttributeBinding {
78             DataTypeOf<T>::value,
79             static_cast<uint8_t>(attributeSize),
80             static_cast<uint32_t>(Vertex::attributeOffsets[attributeIndex]),
81             buffer.buffer,
82             static_cast<uint32_t>(sizeof(Vertex)),
83             0,
84         };
85     }
86 
offsetBinding(const optional<Binding> & binding,std::size_t vertexOffset)87     static optional<Binding> offsetBinding(const optional<Binding>& binding, std::size_t vertexOffset) {
88         assert(vertexOffset <= std::numeric_limits<uint32_t>::max());
89         if (binding) {
90             AttributeBinding result = *binding;
91             result.vertexOffset = static_cast<uint32_t>(vertexOffset);
92             return result;
93         } else {
94             return binding;
95         }
96     }
97 };
98 
99 #define MBGL_DEFINE_ATTRIBUTE(type_, n_, name_)        \
100     struct name_ {                                     \
101         static auto name() { return #name_; }          \
102         using Type = ::mbgl::gl::Attribute<type_, n_>; \
103     }
104 
105 namespace detail {
106 
107 // Attribute binding requires member offsets. The only standard way to
108 // obtain an offset is the offsetof macro. The offsetof macro has defined
109 // behavior only for standard layout types. That rules out std::tuple and
110 // any other solution that relies on chained inheritance. Manually implemented
111 // variadic specialization looks like the only solution. Fortunately, we
112 // only use a maximum of five attributes.
113 
114 template <class... As>
115 class Vertex;
116 
117 template <>
118 class Vertex<> {
119 public:
120     using VertexType = Vertex<>;
121 };
122 
123 template <class A1>
124 class Vertex<A1> {
125 public:
126     typename A1::Value a1;
127 
128     using VertexType = Vertex<A1>;
129     static const std::size_t attributeOffsets[1];
130 };
131 
132 template <class A1, class A2>
133 class Vertex<A1, A2> {
134 public:
135     typename A1::Value a1;
136     typename A2::Value a2;
137 
138     using VertexType = Vertex<A1, A2>;
139     static const std::size_t attributeOffsets[2];
140 };
141 
142 template <class A1, class A2, class A3>
143 class Vertex<A1, A2, A3> {
144 public:
145     typename A1::Value a1;
146     typename A2::Value a2;
147     typename A3::Value a3;
148 
149     using VertexType = Vertex<A1, A2, A3>;
150     static const std::size_t attributeOffsets[3];
151 };
152 
153 template <class A1, class A2, class A3, class A4>
154 class Vertex<A1, A2, A3, A4> {
155 public:
156     typename A1::Value a1;
157     typename A2::Value a2;
158     typename A3::Value a3;
159     typename A4::Value a4;
160 
161     using VertexType = Vertex<A1, A2, A3, A4>;
162     static const std::size_t attributeOffsets[4];
163 };
164 
165 template <class A1, class A2, class A3, class A4, class A5>
166 class Vertex<A1, A2, A3, A4, A5> {
167 public:
168     typename A1::Value a1;
169     typename A2::Value a2;
170     typename A3::Value a3;
171     typename A4::Value a4;
172     typename A5::Value a5;
173 
174     using VertexType = Vertex<A1, A2, A3, A4, A5>;
175     static const std::size_t attributeOffsets[5];
176 };
177 
178 template <class A1>
179 const std::size_t Vertex<A1>::attributeOffsets[1] = {
180     offsetof(VertexType, a1)
181 };
182 
183 template <class A1, class A2>
184 const std::size_t Vertex<A1, A2>::attributeOffsets[2] = {
185     offsetof(VertexType, a1),
186     offsetof(VertexType, a2)
187 };
188 
189 template <class A1, class A2, class A3>
190 const std::size_t Vertex<A1, A2, A3>::attributeOffsets[3] = {
191     offsetof(VertexType, a1),
192     offsetof(VertexType, a2),
193     offsetof(VertexType, a3)
194 };
195 
196 template <class A1, class A2, class A3, class A4>
197 const std::size_t Vertex<A1, A2, A3, A4>::attributeOffsets[4] = {
198     offsetof(VertexType, a1),
199     offsetof(VertexType, a2),
200     offsetof(VertexType, a3),
201     offsetof(VertexType, a4)
202 };
203 
204 template <class A1, class A2, class A3, class A4, class A5>
205 const std::size_t Vertex<A1, A2, A3, A4, A5>::attributeOffsets[5] = {
206     offsetof(VertexType, a1),
207     offsetof(VertexType, a2),
208     offsetof(VertexType, a3),
209     offsetof(VertexType, a4),
210     offsetof(VertexType, a5)
211 };
212 
213 } // namespace detail
214 
215 class Context;
216 void bindAttributeLocation(Context&, ProgramID, AttributeLocation, const char * name);
217 std::set<std::string> getActiveAttributes(ProgramID);
218 
219 template <class... As>
220 class Attributes {
221 public:
222     using Types = TypeList<As...>;
223     using Locations = IndexedTuple<
224         TypeList<As...>,
225         TypeList<optional<typename As::Type::Location>...>>;
226     using Bindings = IndexedTuple<
227         TypeList<As...>,
228         TypeList<optional<typename As::Type::Binding>...>>;
229     using NamedLocations = std::vector<std::pair<const std::string, AttributeLocation>>;
230 
231     using Vertex = detail::Vertex<typename As::Type...>;
232 
bindLocations(Context & context,const ProgramID & id)233     static Locations bindLocations(Context& context, const ProgramID& id) {
234         std::set<std::string> activeAttributes = getActiveAttributes(id);
235 
236         AttributeLocation location = 0;
237         auto maybeBindLocation = [&](const char* name) -> optional<AttributeLocation> {
238             if (activeAttributes.count(name)) {
239                 bindAttributeLocation(context, id, location, name);
240                 return location++;
241             } else {
242                 return {};
243             }
244         };
245 
246         return Locations { maybeBindLocation(As::name())... };
247     }
248 
249     template <class Program>
loadNamedLocations(const Program & program)250     static Locations loadNamedLocations(const Program& program) {
251         return Locations{ program.attributeLocation(As::name())... };
252     }
253 
getNamedLocations(const Locations & locations)254     static NamedLocations getNamedLocations(const Locations& locations) {
255         NamedLocations result;
256 
257         auto maybeAddLocation = [&] (const std::string& name, const optional<AttributeLocation>& location) {
258             if (location) {
259                 result.emplace_back(name, *location);
260             }
261         };
262 
263         util::ignore({ (maybeAddLocation(As::name(), locations.template get<As>()), 0)... });
264 
265         return result;
266     }
267 
268     template <class DrawMode>
bindings(const VertexBuffer<Vertex,DrawMode> & buffer)269     static Bindings bindings(const VertexBuffer<Vertex, DrawMode>& buffer) {
270         return Bindings { As::Type::binding(buffer, TypeIndex<As, As...>::value)... };
271     }
272 
offsetBindings(const Bindings & bindings,std::size_t vertexOffset)273     static Bindings offsetBindings(const Bindings& bindings, std::size_t vertexOffset) {
274         return Bindings { As::Type::offsetBinding(bindings.template get<As>(), vertexOffset)... };
275     }
276 
toBindingArray(const Locations & locations,const Bindings & bindings)277     static AttributeBindingArray toBindingArray(const Locations& locations, const Bindings& bindings) {
278         AttributeBindingArray result;
279         result.resize(sizeof...(As));
280 
281         auto maybeAddBinding = [&] (const optional<AttributeLocation>& location,
282                                     const optional<AttributeBinding>& binding) {
283             if (location) {
284                 result.at(*location) = binding;
285             }
286         };
287 
288         util::ignore({ (maybeAddBinding(locations.template get<As>(), bindings.template get<As>()), 0)... });
289 
290         return result;
291     }
292 
activeBindingCount(const Bindings & bindings)293     static uint32_t activeBindingCount(const Bindings& bindings) {
294         uint32_t result = 0;
295         util::ignore({ ((result += bool(bindings.template get<As>())), 0)... });
296         return result;
297     }
298 };
299 
300 namespace detail {
301 
302 template <class...>
303 struct ConcatenateAttributes;
304 
305 template <class... As, class... Bs>
306 struct ConcatenateAttributes<TypeList<As...>, TypeList<Bs...>> {
307     using Type = Attributes<As..., Bs...>;
308 };
309 
310 } // namespace detail
311 
312 template <class A, class B>
313 using ConcatenateAttributes = typename detail::ConcatenateAttributes<
314     typename A::Types,
315     typename B::Types>::Type;
316 
317 } // namespace gl
318 } // namespace mbgl
319