diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2019-03-15 20:31:12 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-15 20:31:12 +0100 |
commit | c8cfdb1ced822711e772dfcc8f708b1a7a68b5fc (patch) | |
tree | bf511253e25459cafc91a5f3bea2f5c773677162 /src/mbgl/gfx | |
parent | 62695c56956add5560933137a479f29f2d3a091b (diff) | |
download | qtlocation-mapboxgl-c8cfdb1ced822711e772dfcc8f708b1a7a68b5fc.tar.gz |
Merge pull request #14126 from mapbox/gfx-refactor-4
Graphics refactor #4
Diffstat (limited to 'src/mbgl/gfx')
-rw-r--r-- | src/mbgl/gfx/attribute.cpp | 21 | ||||
-rw-r--r-- | src/mbgl/gfx/attribute.hpp | 123 | ||||
-rw-r--r-- | src/mbgl/gfx/context.hpp | 26 | ||||
-rw-r--r-- | src/mbgl/gfx/context_impl.hpp | 23 | ||||
-rw-r--r-- | src/mbgl/gfx/draw_mode.hpp | 89 | ||||
-rw-r--r-- | src/mbgl/gfx/draw_scope.hpp | 21 | ||||
-rw-r--r-- | src/mbgl/gfx/index_vector.hpp | 3 | ||||
-rw-r--r-- | src/mbgl/gfx/primitives.hpp | 24 | ||||
-rw-r--r-- | src/mbgl/gfx/program.hpp | 53 | ||||
-rw-r--r-- | src/mbgl/gfx/texture.hpp | 16 | ||||
-rw-r--r-- | src/mbgl/gfx/types.hpp | 10 | ||||
-rw-r--r-- | src/mbgl/gfx/uniform.hpp | 15 |
12 files changed, 310 insertions, 114 deletions
diff --git a/src/mbgl/gfx/attribute.cpp b/src/mbgl/gfx/attribute.cpp new file mode 100644 index 0000000000..ed5d4032f4 --- /dev/null +++ b/src/mbgl/gfx/attribute.cpp @@ -0,0 +1,21 @@ +#include <mbgl/gfx/attribute.hpp> + +#include <limits> +#include <cassert> + +namespace mbgl { +namespace gfx { + +optional<AttributeBinding> offsetAttributeBinding(const optional<AttributeBinding>& binding, std::size_t vertexOffset) { + assert(vertexOffset <= std::numeric_limits<uint32_t>::max()); + if (binding) { + AttributeBinding result = *binding; + result.vertexOffset = static_cast<uint32_t>(vertexOffset); + return result; + } else { + return binding; + } +} + +} // namespace gfx +} // namespace mbgl diff --git a/src/mbgl/gfx/attribute.hpp b/src/mbgl/gfx/attribute.hpp index 0dce6b2a06..4f44d68d7b 100644 --- a/src/mbgl/gfx/attribute.hpp +++ b/src/mbgl/gfx/attribute.hpp @@ -1,8 +1,11 @@ #pragma once #include <mbgl/gfx/types.hpp> +#include <mbgl/gfx/vertex_buffer.hpp> #include <mbgl/util/type_list.hpp> #include <mbgl/util/indexed_tuple.hpp> +#include <mbgl/util/ignore.hpp> +#include <mbgl/util/optional.hpp> #include <array> #include <type_traits> @@ -79,6 +82,21 @@ struct VertexDescriptor { AttributeDescriptor attributes[5]; }; +class AttributeBinding { +public: + AttributeDescriptor attribute; + uint8_t vertexStride; + const VertexBufferResource* vertexBufferResource; + uint32_t vertexOffset; + + friend bool operator==(const AttributeBinding& lhs, const AttributeBinding& rhs) { + return lhs.attribute == rhs.attribute && + lhs.vertexStride == rhs.vertexStride && + lhs.vertexBufferResource == rhs.vertexBufferResource && + lhs.vertexOffset == rhs.vertexOffset; + } +}; + // 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 @@ -89,32 +107,32 @@ struct VertexDescriptor { namespace detail { template <class...> -struct Vertex; +struct VertexType; template <class A1> -struct Vertex<A1> { - using Type = Vertex<A1>; +struct VertexType<A1> { + using Type = VertexType<A1>; typename A1::Value a1; } MBGL_VERTEX_ALIGN; template <class A1, class A2> -struct Vertex<A1, A2> { - using Type = Vertex<A1, A2>; +struct VertexType<A1, A2> { + using Type = VertexType<A1, A2>; typename A1::Value a1; typename A2::Value a2; } MBGL_VERTEX_ALIGN; template <class A1, class A2, class A3> -struct Vertex<A1, A2, A3> { - using Type = Vertex<A1, A2, A3>; +struct VertexType<A1, A2, A3> { + using Type = VertexType<A1, A2, A3>; typename A1::Value a1; typename A2::Value a2; typename A3::Value a3; } MBGL_VERTEX_ALIGN; template <class A1, class A2, class A3, class A4> -struct Vertex<A1, A2, A3, A4> { - using Type = Vertex<A1, A2, A3, A4>; +struct VertexType<A1, A2, A3, A4> { + using Type = VertexType<A1, A2, A3, A4>; typename A1::Value a1; typename A2::Value a2; typename A3::Value a3; @@ -122,8 +140,8 @@ struct Vertex<A1, A2, A3, A4> { } MBGL_VERTEX_ALIGN; template <class A1, class A2, class A3, class A4, class A5> -struct Vertex<A1, A2, A3, A4, A5> { - using Type = Vertex<A1, A2, A3, A4, A5>; +struct VertexType<A1, A2, A3, A4, A5> { + using Type = VertexType<A1, A2, A3, A4, A5>; typename A1::Value a1; typename A2::Value a2; typename A3::Value a3; @@ -135,8 +153,8 @@ template <class> struct Descriptor; template <class A1> -struct Descriptor<Vertex<A1>> { - using Type = Vertex<A1>; +struct Descriptor<VertexType<A1>> { + using Type = VertexType<A1>; static_assert(sizeof(Type) < 256, "vertex type must be smaller than 256 bytes"); static_assert(std::is_standard_layout<Type>::value, "vertex type must use standard layout"); static constexpr const VertexDescriptor data = { sizeof(Type), 1, { @@ -145,11 +163,11 @@ struct Descriptor<Vertex<A1>> { }; template <class A1> -constexpr const VertexDescriptor Descriptor<Vertex<A1>>::data; +constexpr const VertexDescriptor Descriptor<VertexType<A1>>::data; template <class A1, class A2> -struct Descriptor<Vertex<A1, A2>> { - using Type = Vertex<A1, A2>; +struct Descriptor<VertexType<A1, A2>> { + using Type = VertexType<A1, A2>; static_assert(sizeof(Type) < 256, "vertex type must be smaller than 256 bytes"); static_assert(std::is_standard_layout<Type>::value, "vertex type must use standard layout"); static constexpr const VertexDescriptor data = { sizeof(Type), 2, { @@ -159,11 +177,11 @@ struct Descriptor<Vertex<A1, A2>> { }; template <class A1, class A2> -constexpr const VertexDescriptor Descriptor<Vertex<A1, A2>>::data; +constexpr const VertexDescriptor Descriptor<VertexType<A1, A2>>::data; template <class A1, class A2, class A3> -struct Descriptor<Vertex<A1, A2, A3>> { - using Type = Vertex<A1, A2, A3>; +struct Descriptor<VertexType<A1, A2, A3>> { + using Type = VertexType<A1, A2, A3>; static_assert(sizeof(Type) < 256, "vertex type must be smaller than 256 bytes"); static_assert(std::is_standard_layout<Type>::value, "vertex type must use standard layout"); static constexpr const VertexDescriptor data = { sizeof(Type), 3, { @@ -174,11 +192,11 @@ struct Descriptor<Vertex<A1, A2, A3>> { }; template <class A1, class A2, class A3> -constexpr const VertexDescriptor Descriptor<Vertex<A1, A2, A3>>::data; +constexpr const VertexDescriptor Descriptor<VertexType<A1, A2, A3>>::data; template <class A1, class A2, class A3, class A4> -struct Descriptor<Vertex<A1, A2, A3, A4>> { - using Type = Vertex<A1, A2, A3, A4>; +struct Descriptor<VertexType<A1, A2, A3, A4>> { + using Type = VertexType<A1, A2, A3, A4>; static_assert(sizeof(Type) < 256, "vertex type must be smaller than 256 bytes"); static_assert(std::is_standard_layout<Type>::value, "vertex type must use standard layout"); static constexpr const VertexDescriptor data = { sizeof(Type), 4, { @@ -190,11 +208,11 @@ struct Descriptor<Vertex<A1, A2, A3, A4>> { }; template <class A1, class A2, class A3, class A4> -constexpr const VertexDescriptor Descriptor<Vertex<A1, A2, A3, A4>>::data; +constexpr const VertexDescriptor Descriptor<VertexType<A1, A2, A3, A4>>::data; template <class A1, class A2, class A3, class A4, class A5> -struct Descriptor<Vertex<A1, A2, A3, A4, A5>> { - using Type = Vertex<A1, A2, A3, A4, A5>; +struct Descriptor<VertexType<A1, A2, A3, A4, A5>> { + using Type = VertexType<A1, A2, A3, A4, A5>; static_assert(sizeof(Type) < 256, "vertex type must be smaller than 256 bytes"); static_assert(std::is_standard_layout<Type>::value, "vertex type must use standard layout"); static constexpr const VertexDescriptor data = { sizeof(Type), 5, { @@ -207,11 +225,14 @@ struct Descriptor<Vertex<A1, A2, A3, A4, A5>> { }; template <class A1, class A2, class A3, class A4, class A5> -constexpr const VertexDescriptor Descriptor<Vertex<A1, A2, A3, A4, A5>>::data; +constexpr const VertexDescriptor Descriptor<VertexType<A1, A2, A3, A4, A5>>::data; + +template <class> +struct Vertex; template <class... As> struct Vertex<TypeList<As...>> { - using Type = Vertex<typename As::Type...>; + using Type = VertexType<typename As::Type...>; }; } // namespace detail @@ -219,8 +240,52 @@ struct Vertex<TypeList<As...>> { template <class A> using Vertex = typename detail::Vertex<A>::Type; -template <class V> -using VertexDescriptorOf = detail::Descriptor<V>; +template <class T> +using VertexType = typename detail::VertexType<T>; + +template <size_t I = 0, class... As> +AttributeBinding attributeBinding(const VertexBuffer<detail::VertexType<As...>>& buffer) { + using Descriptor = detail::Descriptor<detail::VertexType<As...>>; + static_assert(I < Descriptor::data.count, "attribute index must be in range"); + return { + Descriptor::data.attributes[I], + Descriptor::data.stride, + buffer.resource.get(), + 0, + }; +} + +optional<gfx::AttributeBinding> offsetAttributeBinding(const optional<gfx::AttributeBinding>& binding, std::size_t vertexOffset); + +template <class> +class AttributeBindings; + +template <class... As> +class AttributeBindings<TypeList<As...>> final + : public IndexedTuple<TypeList<As...>, + TypeList<ExpandToType<As, optional<AttributeBinding>>...>> { + using Base = IndexedTuple<TypeList<As...>, + TypeList<ExpandToType<As, optional<AttributeBinding>>...>>; + +public: + AttributeBindings(const VertexBuffer<Vertex<TypeList<As...>>>& buffer) + : Base{ attributeBinding<TypeIndex<As, As...>::value>(buffer)... } { + } + + template <class... Args> + AttributeBindings(Args&&... args) : Base(std::forward<Args>(args)...) { + } + + AttributeBindings offset(const std::size_t vertexOffset) const { + return { offsetAttributeBinding(Base::template get<As>(), vertexOffset)... }; + } + + uint32_t activeCount() const { + uint32_t result = 0; + util::ignore({ ((result += bool(Base::template get<As>())), 0)... }); + return result; + } +}; } // namespace gfx } // namespace mbgl diff --git a/src/mbgl/gfx/context.hpp b/src/mbgl/gfx/context.hpp index 5ec73b8a41..90b62c94a4 100644 --- a/src/mbgl/gfx/context.hpp +++ b/src/mbgl/gfx/context.hpp @@ -5,14 +5,22 @@ #include <mbgl/gfx/index_vector.hpp> #include <mbgl/gfx/index_buffer.hpp> #include <mbgl/gfx/texture.hpp> +#include <mbgl/gfx/draw_scope.hpp> +#include <mbgl/gfx/program.hpp> #include <mbgl/gfx/types.hpp> namespace mbgl { + +class ProgramParameters; + namespace gfx { class Context { protected: - Context() = default; + Context(ContextType type_) : backend(type_) { + } + + const ContextType backend; public: Context(Context&&) = delete; @@ -89,6 +97,22 @@ protected: Size, const void* data, TexturePixelType, TextureChannelDataType) = 0; virtual void updateTextureResource(const TextureResource&, Size, const void* data, TexturePixelType, TextureChannelDataType) = 0; + +public: + DrawScope createDrawScope() { + return { createDrawScopeResource() }; + } + +protected: + virtual std::unique_ptr<DrawScopeResource> createDrawScopeResource() = 0; + +public: + template <typename Name> + std::unique_ptr<Program<Name>> createProgram(const ProgramParameters&); + +private: + template <typename Backend, typename Name> + std::unique_ptr<Program<Name>> createProgram(const ProgramParameters&); }; } // namespace gfx diff --git a/src/mbgl/gfx/context_impl.hpp b/src/mbgl/gfx/context_impl.hpp new file mode 100644 index 0000000000..0145535bb3 --- /dev/null +++ b/src/mbgl/gfx/context_impl.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <mbgl/gfx/context.hpp> + +namespace mbgl { + +namespace gl { +class Context; +} // namespace gl + +namespace gfx { + +template <typename Name> +std::unique_ptr<Program<Name>> Context::createProgram(const ProgramParameters& programParameters) { + if (backend == ContextType::OpenGL) { + return createProgram<gl::Context, Name>(programParameters); + } + assert(false); + return nullptr; +} + +} // namespace gfx +} // namespace mbgl diff --git a/src/mbgl/gfx/draw_mode.hpp b/src/mbgl/gfx/draw_mode.hpp index a0a9b6c575..e18a38903e 100644 --- a/src/mbgl/gfx/draw_mode.hpp +++ b/src/mbgl/gfx/draw_mode.hpp @@ -1,72 +1,77 @@ #pragma once #include <mbgl/gfx/types.hpp> -#include <mbgl/gfx/primitives.hpp> #include <cassert> namespace mbgl { namespace gfx { -class Points { -public: - using Primitive = Point; - - static constexpr std::size_t bufferGroupSize = 1; - static constexpr PrimitiveType primitiveType = PrimitiveType::Points; - - explicit Points(float pointSize_) : pointSize(pointSize_) {} - - float pointSize; -}; +class DrawMode { +protected: + DrawMode(DrawModeType type_, float size_) + : type(type_), size(size_) { + } -class Lines { public: - using Primitive = Line; + const DrawModeType type; + const float size; +}; - static constexpr std::size_t bufferGroupSize = 2; - static constexpr PrimitiveType primitiveType = PrimitiveType::Lines; +template <class> struct BufferGroupSizeOf; +template <class> struct PrimitiveTypeOf; - explicit Lines(float lineWidth_) : lineWidth(lineWidth_) { - assert(lineWidth > 0); +class Points : public DrawMode { +public: + explicit Points(float pointSize_) : DrawMode(DrawModeType::Points, pointSize_) { + assert(size > 0); } - - float lineWidth; }; -class LineStrip { +template <> struct BufferGroupSizeOf<Points> : std::integral_constant<std::size_t, 1> {}; +template <> struct PrimitiveTypeOf<Points> : std::integral_constant<PrimitiveType, PrimitiveType::Point> {}; + +class Lines : public DrawMode { public: - // LineStrip is a form of "Line" rendering, but the element buffer - // cannot be grouped into logical elements beyond a single Point. - using Primitive = Line; + explicit Lines(float lineWidth_) : DrawMode(DrawModeType::Lines, lineWidth_) { + assert(size > 0); + } +}; - static constexpr std::size_t bufferGroupSize = 1; - static constexpr PrimitiveType primitiveType = PrimitiveType::LineStrip; +template <> struct BufferGroupSizeOf<Lines> : std::integral_constant<std::size_t, 2> {}; +template <> struct PrimitiveTypeOf<Lines> : std::integral_constant<PrimitiveType, PrimitiveType::Line> {}; - explicit LineStrip(float lineWidth_) : lineWidth(lineWidth_) { - assert(lineWidth > 0); +// LineStrip is a form of "Line" rendering, but the element buffer +// cannot be grouped into logical elements beyond a single Point. +class LineStrip : public DrawMode { +public: + explicit LineStrip(float lineWidth_) : DrawMode(DrawModeType::LineStrip, lineWidth_) { + assert(size > 0); } - - float lineWidth; }; -class Triangles { -public: - using Primitive = Triangle; +template <> struct BufferGroupSizeOf<LineStrip> : std::integral_constant<std::size_t, 1> {}; +template <> struct PrimitiveTypeOf<LineStrip> : std::integral_constant<PrimitiveType, PrimitiveType::Line> {}; - static constexpr std::size_t bufferGroupSize = 3; - static constexpr PrimitiveType primitiveType = PrimitiveType::Triangles; +class Triangles : public DrawMode { +public: + explicit Triangles() : DrawMode(DrawModeType::Triangles, 0) { + } }; -class TriangleStrip { -public: - // TriangleStrip is a form of "Triangle" rendering, but the element buffer - // cannot be grouped into logical elements beyond a single Point. - using Primitive = Triangle; +template <> struct BufferGroupSizeOf<Triangles> : std::integral_constant<std::size_t, 3> {}; +template <> struct PrimitiveTypeOf<Triangles> : std::integral_constant<PrimitiveType, PrimitiveType::Triangle> {}; - static constexpr std::size_t bufferGroupSize = 1; - static constexpr PrimitiveType primitiveType = PrimitiveType::TriangleStrip; +// TriangleStrip is a form of "Triangle" rendering, but the element buffer +// cannot be grouped into logical elements beyond a single Point. +class TriangleStrip : public DrawMode { +public: + explicit TriangleStrip() : DrawMode(DrawModeType::TriangleStrip, 0) { + } }; +template <> struct BufferGroupSizeOf<TriangleStrip> : std::integral_constant<std::size_t, 1> {}; +template <> struct PrimitiveTypeOf<TriangleStrip> : std::integral_constant<PrimitiveType, PrimitiveType::Triangle> {}; + } // namespace gfx } // namespace mbgl diff --git a/src/mbgl/gfx/draw_scope.hpp b/src/mbgl/gfx/draw_scope.hpp new file mode 100644 index 0000000000..60aae7a44c --- /dev/null +++ b/src/mbgl/gfx/draw_scope.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <memory> + +namespace mbgl { +namespace gfx { + +class DrawScopeResource { +protected: + DrawScopeResource() = default; +public: + virtual ~DrawScopeResource() = default; +}; + +class DrawScope { +public: + std::unique_ptr<DrawScopeResource> resource; +}; + +} // namespace gfx +} // namespace mbgl diff --git a/src/mbgl/gfx/index_vector.hpp b/src/mbgl/gfx/index_vector.hpp index dc760feb90..826d43bee3 100644 --- a/src/mbgl/gfx/index_vector.hpp +++ b/src/mbgl/gfx/index_vector.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/gfx/draw_mode.hpp> #include <mbgl/util/ignore.hpp> #include <vector> @@ -10,7 +11,7 @@ namespace gfx { template <class DrawMode> class IndexVector { public: - static constexpr std::size_t groupSize = DrawMode::bufferGroupSize; + static constexpr std::size_t groupSize = BufferGroupSizeOf<DrawMode>::value; template <class... Args> void emplace_back(Args&&... args) { diff --git a/src/mbgl/gfx/primitives.hpp b/src/mbgl/gfx/primitives.hpp deleted file mode 100644 index f92b1ec525..0000000000 --- a/src/mbgl/gfx/primitives.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include <cstddef> - -namespace mbgl { -namespace gfx { - -class Point { -public: - static constexpr std::size_t vertexCount = 1; -}; - -class Line { -public: - static constexpr std::size_t vertexCount = 2; -}; - -class Triangle { -public: - static constexpr std::size_t vertexCount = 3; -}; - -} // namespace gfx -} // namespace mbgl diff --git a/src/mbgl/gfx/program.hpp b/src/mbgl/gfx/program.hpp new file mode 100644 index 0000000000..94ba3b18ef --- /dev/null +++ b/src/mbgl/gfx/program.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <cstddef> + +namespace mbgl { +namespace gfx { + +class Context; +class DrawMode; +class DepthMode; +class StencilMode; +class ColorMode; +class CullFaceMode; +class DrawScope; +class IndexBuffer; +template <class> class UniformValues; +template <class> class AttributeBindings; +template <class> class TextureBindings; + +template <class Name> +class Program { +protected: + Program() = default; + +public: + virtual ~Program() = default; + + Program(Program&&) = delete; + Program(const Program&) = delete; + Program& operator=(Program&&) = delete; + Program& operator=(const Program&) = delete; + + using AttributeList = typename Name::AttributeList; + using UniformList = typename Name::UniformList; + using TextureList = typename Name::TextureList; + + virtual void draw(Context&, + const DrawMode&, + const DepthMode&, + const StencilMode&, + const ColorMode&, + const CullFaceMode&, + const UniformValues<UniformList>&, + DrawScope&, + const AttributeBindings<AttributeList>&, + const TextureBindings<TextureList>&, + const IndexBuffer&, + std::size_t indexOffset, + std::size_t indexLength) = 0; +}; + +} // namespace gfx +} // namespace mbgl diff --git a/src/mbgl/gfx/texture.hpp b/src/mbgl/gfx/texture.hpp index 758bdd1b09..a957c4ebdf 100644 --- a/src/mbgl/gfx/texture.hpp +++ b/src/mbgl/gfx/texture.hpp @@ -52,21 +52,19 @@ public: TextureWrapType wrapY; }; -namespace detail { - template <class> class TextureBindings; template <class... Ts> -class TextureBindings<TypeList<Ts...>> { +class TextureBindings<TypeList<Ts...>> final + : public IndexedTuple<TypeList<Ts...>, TypeList<ExpandToType<Ts, TextureBinding>...>> { + using Base = IndexedTuple<TypeList<Ts...>, TypeList<ExpandToType<Ts, TextureBinding>...>>; + public: - using Type = IndexedTuple<TypeList<Ts...>, TypeList<ExpandToType<Ts, TextureBinding>...>>; + template <class... Args> + TextureBindings(Args&&... args) : Base(std::forward<Args>(args)...) { + } }; -} // namespace detail - -template <class TextureTypeList> -using TextureBindings = typename detail::TextureBindings<TextureTypeList>::Type; - } // namespace gfx } // namespace mbgl diff --git a/src/mbgl/gfx/types.hpp b/src/mbgl/gfx/types.hpp index 4c6a040b1f..da2bc14ddd 100644 --- a/src/mbgl/gfx/types.hpp +++ b/src/mbgl/gfx/types.hpp @@ -5,7 +5,17 @@ namespace mbgl { namespace gfx { +enum class ContextType : uint8_t { + OpenGL, +}; + enum class PrimitiveType : uint8_t { + Point, + Line, + Triangle, +}; + +enum class DrawModeType : uint8_t { Points, Lines, LineLoop, diff --git a/src/mbgl/gfx/uniform.hpp b/src/mbgl/gfx/uniform.hpp index bc6e7c1866..f6896b9138 100644 --- a/src/mbgl/gfx/uniform.hpp +++ b/src/mbgl/gfx/uniform.hpp @@ -31,21 +31,20 @@ namespace mbgl { namespace gfx { -namespace detail { template <class> class UniformValues; template <class... Us> -class UniformValues<TypeList<Us...>> { +class UniformValues<TypeList<Us...>> final + : public IndexedTuple<TypeList<Us...>, TypeList<typename Us::Value...>> { + using Base = IndexedTuple<TypeList<Us...>, TypeList<typename Us::Value...>>; + public: - using Type = IndexedTuple<TypeList<Us...>, TypeList<typename Us::Value...>>; + template <class... Args> + UniformValues(Args&&... args) : Base(std::forward<Args>(args)...) { + } }; -} // namespace detail - -template <class UniformTypeList> -using UniformValues = typename detail::UniformValues<UniformTypeList>::Type; - } // namespace gfx } // namespace mbgl |