diff options
author | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-08-07 19:19:32 +0300 |
---|---|---|
committer | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-08-09 18:02:46 +0300 |
commit | c53896caefc96a8c18ab746026330ddc4fc0338e (patch) | |
tree | 8f562b0c416d6c99f7b565e58b758701f6081677 /src/mbgl/gl | |
parent | 9ecbe3642fb4a53b558598239b59bf1d0224c25b (diff) | |
download | qtlocation-mapboxgl-c53896caefc96a8c18ab746026330ddc4fc0338e.tar.gz |
Bump Mapbox GL Nativeqt-v1.1.0
mapbox-gl-native @ edd7948893fcd40a24d96b790e21d3dd028cecbe
Diffstat (limited to 'src/mbgl/gl')
29 files changed, 1421 insertions, 620 deletions
diff --git a/src/mbgl/gl/attribute.cpp b/src/mbgl/gl/attribute.cpp index 7432fff590..bb5b2ddc34 100644 --- a/src/mbgl/gl/attribute.cpp +++ b/src/mbgl/gl/attribute.cpp @@ -4,25 +4,35 @@ namespace mbgl { namespace gl { -AttributeLocation bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) { +void bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) { + if (location >= MAX_ATTRIBUTES) { + throw gl::Error("too many vertex attributes"); + } MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name)); - return location; } -void bindAttribute(AttributeLocation location, - std::size_t count, - DataType type, - std::size_t vertexSize, - std::size_t vertexOffset, - std::size_t attributeOffset) { - MBGL_CHECK_ERROR(glEnableVertexAttribArray(location)); - MBGL_CHECK_ERROR(glVertexAttribPointer( - location, - static_cast<GLint>(count), - static_cast<GLenum>(type), - GL_FALSE, - static_cast<GLsizei>(vertexSize), - reinterpret_cast<GLvoid*>(attributeOffset + (vertexSize * vertexOffset)))); +std::set<std::string> getActiveAttributes(ProgramID id) { + std::set<std::string> activeAttributes; + + GLint attributeCount; + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, &attributeCount)); + + GLint maxAttributeLength; + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeLength)); + + std::string attributeName; + attributeName.resize(maxAttributeLength); + + GLsizei actualLength; + GLint size; + GLenum type; + + for (int32_t i = 0; i < attributeCount; i++) { + MBGL_CHECK_ERROR(glGetActiveAttrib(id, i, maxAttributeLength, &actualLength, &size, &type, &attributeName[0])); + activeAttributes.emplace(std::string(attributeName, 0, actualLength)); + } + + return activeAttributes; } } // namespace gl diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp index 95945bca64..fa6c2ddeab 100644 --- a/src/mbgl/gl/attribute.hpp +++ b/src/mbgl/gl/attribute.hpp @@ -1,33 +1,108 @@ #pragma once #include <mbgl/gl/types.hpp> +#include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/util/ignore.hpp> #include <mbgl/util/indexed_tuple.hpp> +#include <mbgl/util/optional.hpp> #include <cstddef> +#include <vector> +#include <set> #include <functional> +#include <string> +#include <array> +#include <limits> namespace mbgl { namespace gl { -template <class Tag, class T, std::size_t N> -class Attribute { +static constexpr std::size_t MAX_ATTRIBUTES = 8; + +template <class> struct DataTypeOf; +template <> struct DataTypeOf< int8_t> : std::integral_constant<DataType, DataType::Byte> {}; +template <> struct DataTypeOf<uint8_t> : std::integral_constant<DataType, DataType::UnsignedByte> {}; +template <> struct DataTypeOf< int16_t> : std::integral_constant<DataType, DataType::Short> {}; +template <> struct DataTypeOf<uint16_t> : std::integral_constant<DataType, DataType::UnsignedShort> {}; +template <> struct DataTypeOf< int32_t> : std::integral_constant<DataType, DataType::Integer> {}; +template <> struct DataTypeOf<uint32_t> : std::integral_constant<DataType, DataType::UnsignedInteger> {}; +template <> struct DataTypeOf<float> : std::integral_constant<DataType, DataType::Float> {}; + +class AttributeBinding { public: - using Type = T[N]; + 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); + } +}; - class State { - public: - explicit State(AttributeLocation location_) - : location(location_) {} +using AttributeBindingArray = std::array<optional<AttributeBinding>, MAX_ATTRIBUTES>; + +/* + gl::Attribute<T,N> manages the binding of a vertex buffer to a GL program attribute. + - T is the underlying primitive type (exposed as Attribute<T,N>::ValueType) + - N is the number of components in the attribute declared in the shader (exposed as Attribute<T,N>::Dimensions) +*/ +template <class T, std::size_t N> +class Attribute { +public: + using ValueType = T; + static constexpr size_t Dimensions = N; + using Value = std::array<T, N>; + + 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 <class Vertex, class DrawMode> + static Binding binding(const VertexBuffer<Vertex, DrawMode>& buffer, + std::size_t attributeIndex, + std::size_t attributeSize = N) { + static_assert(std::is_standard_layout<Vertex>::value, "vertex type must use standard layout"); + assert(attributeSize >= 1); + assert(attributeSize <= 4); + assert(Vertex::attributeOffsets[attributeIndex] <= std::numeric_limits<uint32_t>::max()); + static_assert(sizeof(Vertex) <= std::numeric_limits<uint32_t>::max(), "vertex too large"); + return AttributeBinding { + DataTypeOf<T>::value, + static_cast<uint8_t>(attributeSize), + static_cast<uint32_t>(Vertex::attributeOffsets[attributeIndex]), + buffer.buffer, + static_cast<uint32_t>(sizeof(Vertex)), + 0, + }; + } - AttributeLocation location; - static constexpr std::size_t count = N; - static constexpr DataType type = DataTypeOf<T>::value; - }; + static optional<Binding> offsetBinding(const optional<Binding>& 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; + } + } }; -#define MBGL_DEFINE_ATTRIBUTE(type_, n_, name_) \ - struct name_ : ::mbgl::gl::Attribute<name_, type_, n_> { static constexpr auto name = #name_; } +#define MBGL_DEFINE_ATTRIBUTE(type_, n_, name_) \ + struct name_ { \ + static auto name() { return #name_; } \ + using Type = ::mbgl::gl::Attribute<type_, n_>; \ + } namespace detail { @@ -41,10 +116,16 @@ namespace detail { template <class... As> class Vertex; +template <> +class Vertex<> { +public: + using VertexType = Vertex<>; +}; + template <class A1> class Vertex<A1> { public: - typename A1::Type a1; + typename A1::Value a1; using VertexType = Vertex<A1>; static const std::size_t attributeOffsets[1]; @@ -53,8 +134,8 @@ public: template <class A1, class A2> class Vertex<A1, A2> { public: - typename A1::Type a1; - typename A2::Type a2; + typename A1::Value a1; + typename A2::Value a2; using VertexType = Vertex<A1, A2>; static const std::size_t attributeOffsets[2]; @@ -63,9 +144,9 @@ public: template <class A1, class A2, class A3> class Vertex<A1, A2, A3> { public: - typename A1::Type a1; - typename A2::Type a2; - typename A3::Type a3; + typename A1::Value a1; + typename A2::Value a2; + typename A3::Value a3; using VertexType = Vertex<A1, A2, A3>; static const std::size_t attributeOffsets[3]; @@ -74,10 +155,10 @@ public: template <class A1, class A2, class A3, class A4> class Vertex<A1, A2, A3, A4> { public: - typename A1::Type a1; - typename A2::Type a2; - typename A3::Type a3; - typename A4::Type a4; + typename A1::Value a1; + typename A2::Value a2; + typename A3::Value a3; + typename A4::Value a4; using VertexType = Vertex<A1, A2, A3, A4>; static const std::size_t attributeOffsets[4]; @@ -86,11 +167,11 @@ public: template <class A1, class A2, class A3, class A4, class A5> class Vertex<A1, A2, A3, A4, A5> { public: - typename A1::Type a1; - typename A2::Type a2; - typename A3::Type a3; - typename A4::Type a4; - typename A5::Type a5; + typename A1::Value a1; + typename A2::Value a2; + typename A3::Value a3; + typename A4::Value a4; + typename A5::Value a5; using VertexType = Vertex<A1, A2, A3, A4, A5>; static const std::size_t attributeOffsets[5]; @@ -133,36 +214,99 @@ const std::size_t Vertex<A1, A2, A3, A4, A5>::attributeOffsets[5] = { } // namespace detail -AttributeLocation bindAttributeLocation(ProgramID, AttributeLocation, const char * name); - -void bindAttribute(AttributeLocation location, - std::size_t count, - DataType type, - std::size_t vertexSize, - std::size_t vertexOffset, - std::size_t attributeOffset); +void bindAttributeLocation(ProgramID, AttributeLocation, const char * name); +std::set<std::string> getActiveAttributes(ProgramID); template <class... As> class Attributes { public: - using State = IndexedTuple<TypeList<As...>, TypeList<typename As::State...>>; - using Vertex = detail::Vertex<As...>; + using Types = TypeList<As...>; + using Locations = IndexedTuple< + TypeList<As...>, + TypeList<optional<typename As::Type::Location>...>>; + using Bindings = IndexedTuple< + TypeList<As...>, + TypeList<optional<typename As::Type::Binding>...>>; + using NamedLocations = std::vector<std::pair<const std::string, AttributeLocation>>; + + using Vertex = detail::Vertex<typename As::Type...>; + + static Locations bindLocations(const ProgramID& id) { + std::set<std::string> activeAttributes = getActiveAttributes(id); + + AttributeLocation location = 0; + auto maybeBindLocation = [&](const char* name) -> optional<AttributeLocation> { + if (activeAttributes.count(name)) { + bindAttributeLocation(id, location, name); + return location++; + } else { + return {}; + } + }; + + return Locations { maybeBindLocation(As::name())... }; + } + + template <class Program> + 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<AttributeLocation>& location) { + if (location) { + result.emplace_back(name, *location); + } + }; + + util::ignore({ (maybeAddLocation(As::name(), locations.template get<As>()), 0)... }); - static State state(const ProgramID& id) { - return State { typename As::State(bindAttributeLocation(id, TypeIndex<As, As...>::value, As::name))... }; + return result; } - static std::function<void (std::size_t)> binder(const State& state) { - return [&state] (std::size_t vertexOffset) { - util::ignore({ (bindAttribute(state.template get<As>().location, - state.template get<As>().count, - state.template get<As>().type, - sizeof(Vertex), - vertexOffset, - Vertex::attributeOffsets[TypeIndex<As, As...>::value]), 0)... }); + template <class DrawMode> + static Bindings bindings(const VertexBuffer<Vertex, DrawMode>& buffer) { + return Bindings { As::Type::binding(buffer, TypeIndex<As, As...>::value)... }; + } + + static Bindings offsetBindings(const Bindings& bindings, std::size_t vertexOffset) { + return Bindings { As::Type::offsetBinding(bindings.template get<As>(), vertexOffset)... }; + } + + static AttributeBindingArray toBindingArray(const Locations& locations, const Bindings& bindings) { + AttributeBindingArray result; + + auto maybeAddBinding = [&] (const optional<AttributeLocation>& location, + const optional<AttributeBinding>& binding) { + if (location) { + result.at(*location) = binding; + } }; + + util::ignore({ (maybeAddBinding(locations.template get<As>(), bindings.template get<As>()), 0)... }); + + return result; } }; +namespace detail { + +template <class...> +struct ConcatenateAttributes; + +template <class... As, class... Bs> +struct ConcatenateAttributes<TypeList<As...>, TypeList<Bs...>> { + using Type = Attributes<As..., Bs...>; +}; + +} // namespace detail + +template <class A, class B> +using ConcatenateAttributes = typename detail::ConcatenateAttributes< + typename A::Types, + typename B::Types>::Type; + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 5048ffcd66..e18f1e0bcf 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -1,7 +1,8 @@ -#include <mbgl/map/view.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/gl/gl.hpp> -#include <mbgl/gl/vertex_array.hpp> +#include <mbgl/gl/debugging_extension.hpp> +#include <mbgl/gl/vertex_array_extension.hpp> +#include <mbgl/gl/program_binary_extension.hpp> #include <mbgl/util/traits.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/logging.hpp> @@ -14,6 +15,31 @@ namespace gl { static_assert(underlying_type(ShaderType::Vertex) == GL_VERTEX_SHADER, "OpenGL type mismatch"); static_assert(underlying_type(ShaderType::Fragment) == GL_FRAGMENT_SHADER, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::Byte) == GL_BYTE, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::UnsignedByte) == GL_UNSIGNED_BYTE, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::Short) == GL_SHORT, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::UnsignedShort) == GL_UNSIGNED_SHORT, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::Integer) == GL_INT, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::UnsignedInteger) == GL_UNSIGNED_INT, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::Float) == GL_FLOAT, "OpenGL type mismatch"); + +#if not MBGL_USE_GLES2 +static_assert(underlying_type(RenderbufferType::RGBA) == GL_RGBA8, "OpenGL type mismatch"); +#else +static_assert(underlying_type(RenderbufferType::RGBA) == GL_RGBA8_OES, "OpenGL type mismatch"); +#endif // MBGL_USE_GLES2 +#if not MBGL_USE_GLES2 +static_assert(underlying_type(RenderbufferType::DepthStencil) == GL_DEPTH24_STENCIL8, "OpenGL type mismatch"); +#else +static_assert(underlying_type(RenderbufferType::DepthStencil) == GL_DEPTH24_STENCIL8_OES, "OpenGL type mismatch"); +#endif // MBGL_USE_GLES2 +#if not MBGL_USE_GLES2 +static_assert(underlying_type(RenderbufferType::DepthComponent) == GL_DEPTH_COMPONENT, "OpenGL type mismatch"); +#else +static_assert(underlying_type(RenderbufferType::DepthComponent) == GL_DEPTH_COMPONENT16, "OpenGL type mismatch"); +#endif // MBGL_USE_GLES2 + + static_assert(underlying_type(PrimitiveType::Points) == GL_POINTS, "OpenGL type mismatch"); static_assert(underlying_type(PrimitiveType::Lines) == GL_LINES, "OpenGL type mismatch"); static_assert(underlying_type(PrimitiveType::LineLoop) == GL_LINE_LOOP, "OpenGL type mismatch"); @@ -34,15 +60,87 @@ static_assert(std::is_same<std::underlying_type_t<TextureFormat>, GLenum>::value static_assert(underlying_type(TextureFormat::RGBA) == GL_RGBA, "OpenGL type mismatch"); static_assert(underlying_type(TextureFormat::Alpha) == GL_ALPHA, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::Float) == GL_FLOAT, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatVec2) == GL_FLOAT_VEC2, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatVec3) == GL_FLOAT_VEC3, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatVec4) == GL_FLOAT_VEC4, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::Int) == GL_INT, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::IntVec2) == GL_INT_VEC2, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::IntVec3) == GL_INT_VEC3, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::IntVec4) == GL_INT_VEC4, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::Bool) == GL_BOOL, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::BoolVec2) == GL_BOOL_VEC2, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::BoolVec3) == GL_BOOL_VEC3, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::BoolVec4) == GL_BOOL_VEC4, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatMat2) == GL_FLOAT_MAT2, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatMat3) == GL_FLOAT_MAT3, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatMat4) == GL_FLOAT_MAT4, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::Sampler2D) == GL_SAMPLER_2D, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::SamplerCube) == GL_SAMPLER_CUBE, "OpenGL type mismatch"); + +static_assert(underlying_type(BufferUsage::StreamDraw) == GL_STREAM_DRAW, "OpenGL type mismatch"); +static_assert(underlying_type(BufferUsage::StaticDraw) == GL_STATIC_DRAW, "OpenGL type mismatch"); +static_assert(underlying_type(BufferUsage::DynamicDraw) == GL_DYNAMIC_DRAW, "OpenGL type mismatch"); + +static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mismatch"); + +Context::Context() = default; + Context::~Context() { reset(); } +void Context::initializeExtensions(const std::function<gl::ProcAddress(const char*)>& getProcAddress) { + if (const auto* extensions = + reinterpret_cast<const char*>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)))) { + + auto fn = [&]( + std::initializer_list<std::pair<const char*, const char*>> probes) -> ProcAddress { + for (auto probe : probes) { + if (strstr(extensions, probe.first) != nullptr) { + if (ProcAddress ptr = getProcAddress(probe.second)) { + return ptr; + } + } + } + return nullptr; + }; + + debugging = std::make_unique<extension::Debugging>(fn); + if (!disableVAOExtension) { + vertexArray = std::make_unique<extension::VertexArray>(fn); + } +#if MBGL_HAS_BINARY_PROGRAMS + programBinary = std::make_unique<extension::ProgramBinary>(fn); +#endif + + if (!supportsVertexArrays()) { + Log::Warning(Event::OpenGL, "Not using Vertex Array Objects"); + } + } +} + +void Context::enableDebugging() { + if (!debugging || !debugging->debugMessageControl || !debugging->debugMessageCallback) { + return; + } + + // This will enable all messages including performance hints + // MBGL_CHECK_ERROR(debugging->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE)); + + // This will only enable high and medium severity messages + MBGL_CHECK_ERROR(debugging->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE)); + MBGL_CHECK_ERROR(debugging->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, GL_TRUE)); + MBGL_CHECK_ERROR(debugging->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE)); + + MBGL_CHECK_ERROR(debugging->debugMessageCallback(extension::Debugging::DebugCallback, nullptr)); +} + UniqueShader Context::createShader(ShaderType type, const std::string& source) { UniqueShader result { MBGL_CHECK_ERROR(glCreateShader(static_cast<GLenum>(type))), { this } }; const GLchar* sources = source.data(); - const GLsizei lengths = static_cast<GLsizei>(source.length()); + const auto lengths = static_cast<GLsizei>(source.length()); MBGL_CHECK_ERROR(glShaderSource(result, 1, &sources, &lengths)); MBGL_CHECK_ERROR(glCompileShader(result)); @@ -72,9 +170,29 @@ UniqueProgram Context::createProgram(ShaderID vertexShader, ShaderID fragmentSha return result; } +#if MBGL_HAS_BINARY_PROGRAMS +UniqueProgram Context::createProgram(BinaryProgramFormat binaryFormat, + const std::string& binaryProgram) { + assert(supportsProgramBinaries()); + UniqueProgram result{ MBGL_CHECK_ERROR(glCreateProgram()), { this } }; + MBGL_CHECK_ERROR(programBinary->programBinary(result, static_cast<GLenum>(binaryFormat), + binaryProgram.data(), + static_cast<GLint>(binaryProgram.size()))); + verifyProgramLinkage(result); + return result; +} +#else +UniqueProgram Context::createProgram(BinaryProgramFormat, const std::string&) { + throw std::runtime_error("binary programs are not supported"); +} +#endif + void Context::linkProgram(ProgramID program_) { MBGL_CHECK_ERROR(glLinkProgram(program_)); + verifyProgramLinkage(program_); +} +void Context::verifyProgramLinkage(ProgramID program_) { GLint status; MBGL_CHECK_ERROR(glGetProgramiv(program_, GL_LINK_STATUS, &status)); if (status == GL_TRUE) { @@ -92,21 +210,26 @@ void Context::linkProgram(ProgramID program_) { throw std::runtime_error("program failed to link"); } -UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size) { +UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size, const BufferUsage usage) { BufferID id = 0; MBGL_CHECK_ERROR(glGenBuffers(1, &id)); UniqueBuffer result { std::move(id), { this } }; vertexBuffer = result; - MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); + MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, static_cast<GLenum>(usage))); return result; } +void Context::updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size) { + vertexBuffer = buffer; + MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data)); +} + UniqueBuffer Context::createIndexBuffer(const void* data, std::size_t size) { BufferID id = 0; MBGL_CHECK_ERROR(glGenBuffers(1, &id)); UniqueBuffer result { std::move(id), { this } }; - vertexArrayObject = 0; - elementBuffer = result; + bindVertexArray = 0; + globalVertexArrayState.indexBuffer = result; MBGL_CHECK_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); return result; } @@ -122,6 +245,68 @@ UniqueTexture Context::createTexture() { return UniqueTexture{ std::move(id), { this } }; } +bool Context::supportsVertexArrays() const { + return vertexArray && + vertexArray->genVertexArrays && + vertexArray->bindVertexArray && + vertexArray->deleteVertexArrays; +} + +#if MBGL_HAS_BINARY_PROGRAMS +bool Context::supportsProgramBinaries() const { + if (!programBinary || !programBinary->programBinary || !programBinary->getProgramBinary) { + return false; + } + + // Blacklist Adreno 3xx, 4xx, and 5xx GPUs due to known bugs: + // https://bugs.chromium.org/p/chromium/issues/detail?id=510637 + // https://chromium.googlesource.com/chromium/src/gpu/+/master/config/gpu_driver_bug_list.json#2316 + const std::string renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); + if (renderer.find("Adreno (TM) 3") != std::string::npos + || renderer.find("Adreno (TM) 4") != std::string::npos + || renderer.find("Adreno (TM) 5") != std::string::npos) { + return false; + } + + return true; +} + +optional<std::pair<BinaryProgramFormat, std::string>> +Context::getBinaryProgram(ProgramID program_) const { + if (!supportsProgramBinaries()) { + return {}; + } + GLint binaryLength; + MBGL_CHECK_ERROR(glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &binaryLength)); + std::string binary; + binary.resize(binaryLength); + GLenum binaryFormat; + MBGL_CHECK_ERROR(programBinary->getProgramBinary( + program_, binaryLength, &binaryLength, &binaryFormat, const_cast<char*>(binary.data()))); + if (size_t(binaryLength) != binary.size()) { + return {}; + } + return { { binaryFormat, std::move(binary) } }; +} +#else +optional<std::pair<BinaryProgramFormat, std::string>> Context::getBinaryProgram(ProgramID) const { + return {}; +} +#endif + +VertexArray Context::createVertexArray() { + if (supportsVertexArrays()) { + VertexArrayID id = 0; + MBGL_CHECK_ERROR(vertexArray->genVertexArrays(1, &id)); + UniqueVertexArray vao(std::move(id), { this }); + return { UniqueVertexArrayState(new VertexArrayState(std::move(vao), *this), VertexArrayStateDeleter { true })}; + } else { + // On GL implementations which do not support vertex arrays, attribute bindings are global state. + // So return a VertexArray which shares our global state tracking and whose deleter is a no-op. + return { UniqueVertexArrayState(&globalVertexArrayState, VertexArrayStateDeleter { false }) }; + } +} + UniqueFramebuffer Context::createFramebuffer() { FramebufferID id = 0; MBGL_CHECK_ERROR(glGenFramebuffers(1, &id)); @@ -136,6 +321,7 @@ UniqueRenderbuffer Context::createRenderbuffer(const RenderbufferType type, cons bindRenderbuffer = renderbuffer; MBGL_CHECK_ERROR( glRenderbufferStorage(GL_RENDERBUFFER, static_cast<GLenum>(type), size.width, size.height)); + bindRenderbuffer = 0; return renderbuffer; } @@ -143,11 +329,9 @@ std::unique_ptr<uint8_t[]> Context::readFramebuffer(const Size size, const Textu const size_t stride = size.width * (format == TextureFormat::RGBA ? 4 : 1); auto data = std::make_unique<uint8_t[]>(stride * size.height); -#if not MBGL_USE_GLES2 // When reading data from the framebuffer, make sure that we are storing the values // tightly packed into the buffer to avoid buffer overruns. pixelStorePack = { 1 }; -#endif // MBGL_USE_GLES2 MBGL_CHECK_ERROR(glReadPixels(0, 0, size.width, size.height, static_cast<GLenum>(format), GL_UNSIGNED_BYTE, data.get())); @@ -171,7 +355,7 @@ void Context::drawPixels(const Size size, const void* data, TextureFormat format if (format != TextureFormat::RGBA) { format = static_cast<TextureFormat>(GL_LUMINANCE); } - MBGL_CHECK_ERROR(glDrawPixels(size.width, size.height, static_cast<GLenum>(GL_LUMINANCE), + MBGL_CHECK_ERROR(glDrawPixels(size.width, size.height, static_cast<GLenum>(format), GL_UNSIGNED_BYTE, data)); } #endif // MBGL_USE_GLES2 @@ -226,7 +410,7 @@ Framebuffer Context::createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>& color, const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) { if (color.size != depthStencil.size) { - throw new std::runtime_error("Renderbuffer size mismatch"); + throw std::runtime_error("Renderbuffer size mismatch"); } auto fbo = createFramebuffer(); bindFramebuffer = fbo; @@ -250,7 +434,7 @@ Framebuffer Context::createFramebuffer(const Texture& color, const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) { if (color.size != depthStencil.size) { - throw new std::runtime_error("Renderbuffer size mismatch"); + throw std::runtime_error("Renderbuffer size mismatch"); } auto fbo = createFramebuffer(); bindFramebuffer = fbo; @@ -270,9 +454,21 @@ Framebuffer Context::createFramebuffer(const Texture& color) { return { color.size, std::move(fbo) }; } +Framebuffer +Context::createFramebuffer(const Texture& color, + const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) { + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0)); + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthTarget.renderbuffer)); + checkFramebuffer(); + return { depthTarget.size, std::move(fbo) }; +} + UniqueTexture Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit) { auto obj = createTexture(); + pixelStoreUnpack = { 1 }; updateTexture(obj, size, data, format, unit); // We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures. // We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus. @@ -342,8 +538,8 @@ void Context::reset() { } void Context::setDirtyState() { - // Note: does not set viewport/bindFramebuffer to dirty since they are handled separately in - // the view object. + // Note: does not set viewport/scissorTest/bindFramebuffer to dirty + // since they are handled separately in the view object. stencilFunc.setDirty(); stencilMask.setDirty(); stencilTest.setDirty(); @@ -363,12 +559,12 @@ void Context::setDirtyState() { program.setDirty(); lineWidth.setDirty(); activeTexture.setDirty(); + pixelStorePack.setDirty(); + pixelStoreUnpack.setDirty(); #if not MBGL_USE_GLES2 pointSize.setDirty(); pixelZoom.setDirty(); rasterPos.setDirty(); - pixelStorePack.setDirty(); - pixelStoreUnpack.setDirty(); pixelTransferDepth.setDirty(); pixelTransferStencil.setDirty(); #endif // MBGL_USE_GLES2 @@ -376,8 +572,8 @@ void Context::setDirtyState() { tex.setDirty(); } vertexBuffer.setDirty(); - elementBuffer.setDirty(); - vertexArrayObject.setDirty(); + bindVertexArray.setDirty(); + globalVertexArrayState.setDirty(); } void Context::clear(optional<mbgl::Color> color, @@ -407,37 +603,38 @@ void Context::clear(optional<mbgl::Color> color, } #if not MBGL_USE_GLES2 -PrimitiveType Context::operator()(const Points& points) { +void Context::setDrawMode(const Points& points) { pointSize = points.pointSize; - return PrimitiveType::Points; } #else -PrimitiveType Context::operator()(const Points&) { - return PrimitiveType::Points; +void Context::setDrawMode(const Points&) { } #endif // MBGL_USE_GLES2 -PrimitiveType Context::operator()(const Lines& lines) { +void Context::setDrawMode(const Lines& lines) { lineWidth = lines.lineWidth; - return PrimitiveType::Lines; } -PrimitiveType Context::operator()(const LineStrip& lineStrip) { +void Context::setDrawMode(const LineStrip& lineStrip) { lineWidth = lineStrip.lineWidth; - return PrimitiveType::LineStrip; } -PrimitiveType Context::operator()(const Triangles&) { - return PrimitiveType::Triangles; +void Context::setDrawMode(const Triangles&) { } -PrimitiveType Context::operator()(const TriangleStrip&) { - return PrimitiveType::TriangleStrip; +void Context::setDrawMode(const TriangleStrip&) { } void Context::setDepthMode(const DepthMode& depth) { if (depth.func == DepthMode::Always && !depth.mask) { depthTest = false; + + // Workaround for rendering errors on Adreno 2xx GPUs. Depth-related state should + // not matter when the depth test is disabled, but on these GPUs it apparently does. + // https://github.com/mapbox/mapbox-gl-native/issues/9164 + depthFunc = depth.func; + depthMask = depth.mask; + depthRange = depth.range; } else { depthTest = true; depthFunc = depth.func; @@ -474,57 +671,14 @@ void Context::setColorMode(const ColorMode& color) { colorMask = color.mask; } -void Context::draw(const Drawable& drawable) { - if (drawable.segments.empty()) { - return; - } - - PrimitiveType primitiveType = apply_visitor([&] (auto m) { return (*this)(m); }, drawable.drawMode); - - setDepthMode(drawable.depthMode); - setStencilMode(drawable.stencilMode); - setColorMode(drawable.colorMode); - - program = drawable.program; - - drawable.bindUniforms(); - - for (const auto& segment : drawable.segments) { - auto needAttributeBindings = [&] () { - if (!gl::GenVertexArrays || !gl::BindVertexArray) { - return true; - } - - if (segment.vao) { - vertexArrayObject = *segment.vao; - return false; - } - - VertexArrayID id = 0; - MBGL_CHECK_ERROR(gl::GenVertexArrays(1, &id)); - vertexArrayObject = id; - segment.vao = UniqueVertexArray(std::move(id), { this }); - - // If we are initializing a new VAO, we need to force the buffers - // to be rebound. VAOs don't inherit the existing buffer bindings. - vertexBuffer.setDirty(); - elementBuffer.setDirty(); - - return true; - }; - - if (needAttributeBindings()) { - vertexBuffer = drawable.vertexBuffer; - elementBuffer = drawable.indexBuffer; - drawable.bindAttributes(segment.vertexOffset); - } - - MBGL_CHECK_ERROR(glDrawElements( - static_cast<GLenum>(primitiveType), - static_cast<GLsizei>(segment.indexLength), - GL_UNSIGNED_SHORT, - reinterpret_cast<GLvoid*>(sizeof(uint16_t) * segment.indexOffset))); - } +void Context::draw(PrimitiveType primitiveType, + std::size_t indexOffset, + std::size_t indexLength) { + MBGL_CHECK_ERROR(glDrawElements( + static_cast<GLenum>(primitiveType), + static_cast<GLsizei>(indexLength), + GL_UNSIGNED_SHORT, + reinterpret_cast<GLvoid*>(sizeof(uint16_t) * indexOffset))); } void Context::performCleanup() { @@ -545,8 +699,8 @@ void Context::performCleanup() { for (const auto id : abandonedBuffers) { if (vertexBuffer == id) { vertexBuffer.setDirty(); - } else if (elementBuffer == id) { - elementBuffer.setDirty(); + } else if (globalVertexArrayState.indexBuffer == id) { + globalVertexArrayState.indexBuffer.setDirty(); } } MBGL_CHECK_ERROR(glDeleteBuffers(int(abandonedBuffers.size()), abandonedBuffers.data())); @@ -564,13 +718,14 @@ void Context::performCleanup() { } if (!abandonedVertexArrays.empty()) { + assert(supportsVertexArrays()); for (const auto id : abandonedVertexArrays) { - if (vertexArrayObject == id) { - vertexArrayObject.setDirty(); + if (bindVertexArray == id) { + bindVertexArray.setDirty(); } } - MBGL_CHECK_ERROR(gl::DeleteVertexArrays(int(abandonedVertexArrays.size()), - abandonedVertexArrays.data())); + MBGL_CHECK_ERROR(vertexArray->deleteVertexArrays(int(abandonedVertexArrays.size()), + abandonedVertexArrays.data())); abandonedVertexArrays.clear(); } diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 056c262c0f..9923567276 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/gl/features.hpp> #include <mbgl/gl/object.hpp> #include <mbgl/gl/state.hpp> #include <mbgl/gl/value.hpp> @@ -8,12 +9,12 @@ #include <mbgl/gl/framebuffer.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/vertex_array.hpp> #include <mbgl/gl/types.hpp> #include <mbgl/gl/draw_mode.hpp> #include <mbgl/gl/depth_mode.hpp> #include <mbgl/gl/stencil_mode.hpp> #include <mbgl/gl/color_mode.hpp> -#include <mbgl/gl/segment.hpp> #include <mbgl/util/noncopyable.hpp> @@ -22,33 +23,57 @@ #include <vector> #include <array> #include <string> -#include <unordered_map> namespace mbgl { - -class View; - namespace gl { constexpr size_t TextureMax = 64; +using ProcAddress = void (*)(); + +namespace extension { +class VertexArray; +class Debugging; +class ProgramBinary; +} // namespace extension class Context : private util::noncopyable { public: + Context(); ~Context(); + void initializeExtensions(const std::function<gl::ProcAddress(const char*)>&); + + void enableDebugging(); + UniqueShader createShader(ShaderType type, const std::string& source); UniqueProgram createProgram(ShaderID vertexShader, ShaderID fragmentShader); + UniqueProgram createProgram(BinaryProgramFormat binaryFormat, const std::string& binaryProgram); + void verifyProgramLinkage(ProgramID); void linkProgram(ProgramID); UniqueTexture createTexture(); + VertexArray createVertexArray(); + +#if MBGL_HAS_BINARY_PROGRAMS + bool supportsProgramBinaries() const; +#else + constexpr bool supportsProgramBinaries() const { return false; } +#endif + optional<std::pair<BinaryProgramFormat, std::string>> getBinaryProgram(ProgramID) const; template <class Vertex, class DrawMode> - VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v) { + VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v, const BufferUsage usage=BufferUsage::StaticDraw) { return VertexBuffer<Vertex, DrawMode> { v.vertexSize(), - createVertexBuffer(v.data(), v.byteSize()) + createVertexBuffer(v.data(), v.byteSize(), usage) }; } + template <class Vertex, class DrawMode> + void updateVertexBuffer(VertexBuffer<Vertex, DrawMode>& buffer, VertexVector<Vertex, DrawMode>&& v) { + assert(v.vertexSize() == buffer.vertexCount); + updateVertexBuffer(buffer.buffer, v.data(), v.byteSize()); + } + template <class DrawMode> IndexBuffer<DrawMode> createIndexBuffer(IndexVector<DrawMode>&& v) { return IndexBuffer<DrawMode> { @@ -58,7 +83,9 @@ public: template <RenderbufferType type> Renderbuffer<type> createRenderbuffer(const Size size) { - static_assert(type == RenderbufferType::RGBA || type == RenderbufferType::DepthStencil, + static_assert(type == RenderbufferType::RGBA || + type == RenderbufferType::DepthStencil || + type == RenderbufferType::DepthComponent, "invalid renderbuffer type"); return { size, createRenderbuffer(type, size) }; } @@ -69,6 +96,8 @@ public: Framebuffer createFramebuffer(const Texture&, const Renderbuffer<RenderbufferType::DepthStencil>&); Framebuffer createFramebuffer(const Texture&); + Framebuffer createFramebuffer(const Texture&, + const Renderbuffer<RenderbufferType::DepthComponent>&); template <typename Image, TextureFormat format = Image::channels == 4 ? TextureFormat::RGBA @@ -91,7 +120,7 @@ public: template <typename Image> Texture createTexture(const Image& image, TextureUnit unit = 0) { auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha; - return Texture(image.size, [&] { return createTexture(image.size, image.data.get(), format, unit); }); + return { image.size, createTexture(image.size, image.data.get(), format, unit) }; } template <typename Image> @@ -105,7 +134,7 @@ public: Texture createTexture(const Size size, TextureFormat format = TextureFormat::RGBA, TextureUnit unit = 0) { - return Texture(size, [&] { return createTexture(size, nullptr, format, unit); }); + return { size, createTexture(size, nullptr, format, unit) }; } void bindTexture(Texture&, @@ -119,25 +148,20 @@ public: optional<float> depth, optional<int32_t> stencil); - struct Drawable { - DrawMode drawMode; - DepthMode depthMode; - StencilMode stencilMode; - ColorMode colorMode; - gl::ProgramID program; - gl::BufferID vertexBuffer; - gl::BufferID indexBuffer; - const std::vector<Segment>& segments; - std::function<void ()> bindUniforms; - std::function<void (std::size_t)> bindAttributes; - }; - - void draw(const Drawable&); + void setDrawMode(const Points&); + void setDrawMode(const Lines&); + void setDrawMode(const LineStrip&); + void setDrawMode(const Triangles&); + void setDrawMode(const TriangleStrip&); void setDepthMode(const DepthMode&); void setStencilMode(const StencilMode&); void setColorMode(const ColorMode&); + void draw(PrimitiveType, + std::size_t indexOffset, + std::size_t indexLength); + // Actually remove the objects we marked as abandoned with the above methods. // Only call this while the OpenGL context is exclusive to this thread. void performCleanup(); @@ -158,18 +182,39 @@ public: void setDirtyState(); + extension::Debugging* getDebuggingExtension() const { + return debugging.get(); + } + + extension::VertexArray* getVertexArrayExtension() const { + return vertexArray.get(); + } + +private: + std::unique_ptr<extension::Debugging> debugging; + std::unique_ptr<extension::VertexArray> vertexArray; +#if MBGL_HAS_BINARY_PROGRAMS + std::unique_ptr<extension::ProgramBinary> programBinary; +#endif + +public: State<value::ActiveTexture> activeTexture; State<value::BindFramebuffer> bindFramebuffer; State<value::Viewport> viewport; + State<value::ScissorTest> scissorTest; std::array<State<value::BindTexture>, 2> texture; - State<value::BindVertexArray> vertexArrayObject; State<value::Program> program; + State<value::BindVertexBuffer> vertexBuffer; + + State<value::BindVertexArray, const Context&> bindVertexArray { *this }; + VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }), *this }; + + State<value::PixelStorePack> pixelStorePack; + State<value::PixelStoreUnpack> pixelStoreUnpack; #if not MBGL_USE_GLES2 State<value::PixelZoom> pixelZoom; State<value::RasterPos> rasterPos; - State<value::PixelStorePack> pixelStorePack; - State<value::PixelStoreUnpack> pixelStoreUnpack; State<value::PixelTransferDepth> pixelTransferDepth; State<value::PixelTransferStencil> pixelTransferStencil; #endif // MBGL_USE_GLES2 @@ -196,10 +241,9 @@ private: #if not MBGL_USE_GLES2 State<value::PointSize> pointSize; #endif // MBGL_USE_GLES2 - State<value::BindVertexBuffer> vertexBuffer; - State<value::BindElementBuffer> elementBuffer; - UniqueBuffer createVertexBuffer(const void* data, std::size_t size); + UniqueBuffer createVertexBuffer(const void* data, std::size_t size, const BufferUsage usage); + void updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size); UniqueBuffer createIndexBuffer(const void* data, std::size_t size); UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit); void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit); @@ -210,11 +254,7 @@ private: void drawPixels(Size size, const void* data, TextureFormat); #endif // MBGL_USE_GLES2 - PrimitiveType operator()(const Points&); - PrimitiveType operator()(const Lines&); - PrimitiveType operator()(const LineStrip&); - PrimitiveType operator()(const Triangles&); - PrimitiveType operator()(const TriangleStrip&); + bool supportsVertexArrays() const; friend detail::ProgramDeleter; friend detail::ShaderDeleter; @@ -233,6 +273,10 @@ private: std::vector<VertexArrayID> abandonedVertexArrays; std::vector<FramebufferID> abandonedFramebuffers; std::vector<RenderbufferID> abandonedRenderbuffers; + +public: + // For testing + bool disableVAOExtension = false; }; } // namespace gl diff --git a/src/mbgl/gl/debugging.cpp b/src/mbgl/gl/debugging.cpp index f99308de7e..366b4d63c7 100644 --- a/src/mbgl/gl/debugging.cpp +++ b/src/mbgl/gl/debugging.cpp @@ -1,193 +1,33 @@ #include <mbgl/gl/debugging.hpp> -#include <mbgl/gl/gl.hpp> -#include <mbgl/gl/extension.hpp> -#include <mbgl/util/event.hpp> -#include <mbgl/util/logging.hpp> - -#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 -#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 -#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 -#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 -#define GL_DEBUG_SOURCE_API 0x8246 -#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 -#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 -#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 -#define GL_DEBUG_SOURCE_APPLICATION 0x824A -#define GL_DEBUG_SOURCE_OTHER 0x824B -#define GL_DEBUG_TYPE_ERROR 0x824C -#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D -#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E -#define GL_DEBUG_TYPE_PORTABILITY 0x824F -#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 -#define GL_DEBUG_TYPE_OTHER 0x8251 -#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 -#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 -#define GL_DEBUG_LOGGED_MESSAGES 0x9145 -#define GL_DEBUG_SEVERITY_HIGH 0x9146 -#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 -#define GL_DEBUG_SEVERITY_LOW 0x9148 -#define GL_DEBUG_TYPE_MARKER 0x8268 -#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 -#define GL_DEBUG_TYPE_POP_GROUP 0x826A -#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B -#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C -#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D -#define GL_BUFFER 0x82E0 -#define GL_SHADER 0x82E1 -#define GL_PROGRAM 0x82E2 -#define GL_QUERY 0x82E3 -#define GL_PROGRAM_PIPELINE 0x82E4 -#define GL_SAMPLER 0x82E6 -#define GL_MAX_LABEL_LENGTH 0x82E8 -#define GL_DEBUG_OUTPUT 0x92E0 -#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 -#define GL_DISPLAY_LIST 0x82E7 -#define GL_VERTEX_ARRAY 0x8074 -#define GL_TRANSFORM_FEEDBACK 0x8E22 -#define GL_TEXTURE 0x1702 -#define GL_RENDERBUFFER 0x8D41 -#define GL_FRAMEBUFFER 0x8D40 +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/debugging_extension.hpp> namespace mbgl { namespace gl { -namespace debugging { - -typedef void (*GLDEBUGPROC)(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar *message, - const void *userParam); - -static ExtensionFunction< - void (GLenum source, - GLenum type, - GLenum severity, - GLsizei count, - const GLuint *ids, - GLboolean enabled)> - DebugMessageControl({ - {"GL_KHR_debug", "glDebugMessageControl"}, - {"GL_ARB_debug_output", "glDebugMessageControlARB"} - }); - -static ExtensionFunction< - void (GLDEBUGPROC callback, - const void *userParam)> - DebugMessageCallback({ - {"GL_KHR_debug", "glDebugMessageCallback"}, - {"GL_ARB_debug_output", "glDebugMessageCallbackARB"} - }); - -void debugCallback(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei /*length*/, - const GLchar* message, - const void* /*userParam*/) -{ - std::string strSource; - switch (source) { - case GL_DEBUG_SOURCE_API: strSource = "DEBUG_SOURCE_API"; break; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM: strSource = "DEBUG_SOURCE_WINDOW_SYSTEM"; break; - case GL_DEBUG_SOURCE_SHADER_COMPILER: strSource = "DEBUG_SOURCE_SHADER_COMPILER"; break; - case GL_DEBUG_SOURCE_THIRD_PARTY: strSource = "DEBUG_SOURCE_THIRD_PARTY"; break; - case GL_DEBUG_SOURCE_APPLICATION: strSource = "DEBUG_SOURCE_APPLICATION"; break; - case GL_DEBUG_SOURCE_OTHER: strSource = "DEBUG_SOURCE_OTHER"; break; - default: strSource = "(unknown)"; break; - } - - std::string strType; - switch (type) { - case GL_DEBUG_TYPE_ERROR: strType = "DEBUG_TYPE_ERROR"; break; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: strType = "DEBUG_TYPE_DEPRECATED_BEHAVIOR"; break; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: strType = "DEBUG_TYPE_UNDEFINED_BEHAVIOR"; break; - case GL_DEBUG_TYPE_PERFORMANCE: strType = "DEBUG_TYPE_PERFORMANCE"; break; - case GL_DEBUG_TYPE_PORTABILITY: strType = "DEBUG_TYPE_PORTABILITY"; break; - case GL_DEBUG_TYPE_OTHER: strType = "DEBUG_TYPE_OTHER"; break; - case GL_DEBUG_TYPE_MARKER: strType = "DEBUG_TYPE_MARKER"; break; - case GL_DEBUG_TYPE_PUSH_GROUP: strType = "DEBUG_TYPE_OTHER"; break; - case GL_DEBUG_TYPE_POP_GROUP: strType = "DEBUG_TYPE_POP_GROUP"; break; - default: strSource = "(unknown)"; break; - } - - std::string strSeverity; - mbgl::EventSeverity evtSeverity; - switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: strSeverity = "DEBUG_SEVERITY_HIGH"; evtSeverity = mbgl::EventSeverity::Error; break; - case GL_DEBUG_SEVERITY_MEDIUM: strSeverity = "DEBUG_SEVERITY_MEDIUM"; evtSeverity = mbgl::EventSeverity::Warning; break; - case GL_DEBUG_SEVERITY_LOW: strSeverity = "DEBUG_SEVERITY_LOW"; evtSeverity = mbgl::EventSeverity::Info; break; - case GL_DEBUG_SEVERITY_NOTIFICATION: strSeverity = "DEBUG_SEVERITY_NOTIFICATION"; evtSeverity = mbgl::EventSeverity::Debug; break; - default: strSource = "(unknown)"; evtSeverity = mbgl::EventSeverity::Debug; break; - } - - mbgl::Log::Record(evtSeverity, mbgl::Event::OpenGL, "GL_%s GL_%s %u GL_%s - %s", strSource.c_str(), strType.c_str(), id, strSeverity.c_str(), message); -} - -void enable() { - if (!DebugMessageControl || !DebugMessageCallback) { - return; - } - - // This will enable all messages including performance hints - //MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE)); - - // This will only enable high and medium severity messages - MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE)); - MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, GL_TRUE)); - MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE)); - - MBGL_CHECK_ERROR(DebugMessageCallback(debugCallback, nullptr)); -} #ifndef NDEBUG -static ExtensionFunction< - void (GLenum source, - GLuint id, - GLsizei length, - const GLchar *message)> - PushDebugGroup({ - {"GL_KHR_debug", "glPushDebugGroup"} - }); - -static ExtensionFunction< - void ()> - PopDebugGroup({ - {"GL_KHR_debug", "glPopDebugGroup"} - }); - -static ExtensionFunction< - void (GLsizei length, - const GLchar *marker)> - PushGroupMarkerEXT({ - {"GL_EXT_debug_marker", "glPushGroupMarkerEXT"} - }); -static ExtensionFunction< - void ()> - PopGroupMarkerEXT({ - {"GL_EXT_debug_marker", "glPopGroupMarkerEXT"} - }); - -group::group(const std::string& str) { - if (PushDebugGroup) { - PushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(str.size()), str.c_str()); - } else if (PushGroupMarkerEXT) { - PushGroupMarkerEXT(GLsizei(str.size() + 1), str.c_str()); +DebugGroup::DebugGroup(const Context& context_, const std::string& name) : context(context_) { + if (auto debugging = context.getDebuggingExtension()) { + if (debugging->pushDebugGroup) { + MBGL_CHECK_ERROR(debugging->pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(name.size()), name.c_str())); + } else if (debugging->pushGroupMarkerEXT) { + MBGL_CHECK_ERROR(debugging->pushGroupMarkerEXT(GLsizei(name.size() + 1), name.c_str())); + } } } -group::~group() { - if (PopDebugGroup) { - PopDebugGroup(); - } else if (PopGroupMarkerEXT) { - PopGroupMarkerEXT(); +DebugGroup::~DebugGroup() { + if (auto debugging = context.getDebuggingExtension()) { + if (debugging->popDebugGroup) { + MBGL_CHECK_ERROR(debugging->popDebugGroup()); + } else if (debugging->popGroupMarkerEXT) { + MBGL_CHECK_ERROR(debugging->popGroupMarkerEXT()); + } } } + #endif -} // namespace debugging } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/debugging.hpp b/src/mbgl/gl/debugging.hpp index fe363701ad..d24b727295 100644 --- a/src/mbgl/gl/debugging.hpp +++ b/src/mbgl/gl/debugging.hpp @@ -1,26 +1,34 @@ #pragma once +#include <mbgl/util/noncopyable.hpp> + #include <string> namespace mbgl { namespace gl { -namespace debugging { -void enable(); +class Context; #ifndef NDEBUG -struct group { - group(const std::string&); - ~group(); + +class DebugGroup : private util::noncopyable { +public: + DebugGroup(const Context&, const std::string&); + ~DebugGroup(); + +private: + const Context& context; }; #define __MBGL_DEBUG_GROUP_NAME2(counter) __MBGL_DEBUG_GROUP_##counter #define __MBGL_DEBUG_GROUP_NAME(counter) __MBGL_DEBUG_GROUP_NAME2(counter) -#define MBGL_DEBUG_GROUP(string) ::mbgl::gl::debugging::group __MBGL_DEBUG_GROUP_NAME(__LINE__)(string); +#define MBGL_DEBUG_GROUP(context, name) const ::mbgl::gl::DebugGroup __MBGL_DEBUG_GROUP_NAME(__LINE__)(context, name); + #else -#define MBGL_DEBUG_GROUP(string) + +#define MBGL_DEBUG_GROUP(context, name) + #endif -} // namespace debugging } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/debugging_extension.cpp b/src/mbgl/gl/debugging_extension.cpp new file mode 100644 index 0000000000..5afa4f50fc --- /dev/null +++ b/src/mbgl/gl/debugging_extension.cpp @@ -0,0 +1,55 @@ +#include <mbgl/gl/debugging_extension.hpp> +#include <mbgl/util/logging.hpp> + +namespace mbgl { +namespace gl { +namespace extension { + +void Debugging::DebugCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei /* length */, + const GLchar* message, + const void* /* userParam */) { + std::string strSource; + switch (source) { + case GL_DEBUG_SOURCE_API: strSource = "DEBUG_SOURCE_API"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: strSource = "DEBUG_SOURCE_WINDOW_SYSTEM"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: strSource = "DEBUG_SOURCE_SHADER_COMPILER"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: strSource = "DEBUG_SOURCE_THIRD_PARTY"; break; + case GL_DEBUG_SOURCE_APPLICATION: strSource = "DEBUG_SOURCE_APPLICATION"; break; + case GL_DEBUG_SOURCE_OTHER: strSource = "DEBUG_SOURCE_OTHER"; break; + default: strSource = "(unknown)"; break; + } + + std::string strType; + switch (type) { + case GL_DEBUG_TYPE_ERROR: strType = "DEBUG_TYPE_ERROR"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: strType = "DEBUG_TYPE_DEPRECATED_BEHAVIOR"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: strType = "DEBUG_TYPE_UNDEFINED_BEHAVIOR"; break; + case GL_DEBUG_TYPE_PERFORMANCE: strType = "DEBUG_TYPE_PERFORMANCE"; break; + case GL_DEBUG_TYPE_PORTABILITY: strType = "DEBUG_TYPE_PORTABILITY"; break; + case GL_DEBUG_TYPE_OTHER: strType = "DEBUG_TYPE_OTHER"; break; + case GL_DEBUG_TYPE_MARKER: strType = "DEBUG_TYPE_MARKER"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: strType = "DEBUG_TYPE_OTHER"; break; + case GL_DEBUG_TYPE_POP_GROUP: strType = "DEBUG_TYPE_POP_GROUP"; break; + default: strSource = "(unknown)"; break; + } + + std::string strSeverity; + mbgl::EventSeverity evtSeverity; + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: strSeverity = "DEBUG_SEVERITY_HIGH"; evtSeverity = mbgl::EventSeverity::Error; break; + case GL_DEBUG_SEVERITY_MEDIUM: strSeverity = "DEBUG_SEVERITY_MEDIUM"; evtSeverity = mbgl::EventSeverity::Warning; break; + case GL_DEBUG_SEVERITY_LOW: strSeverity = "DEBUG_SEVERITY_LOW"; evtSeverity = mbgl::EventSeverity::Info; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: strSeverity = "DEBUG_SEVERITY_NOTIFICATION"; evtSeverity = mbgl::EventSeverity::Debug; break; + default: strSource = "(unknown)"; evtSeverity = mbgl::EventSeverity::Debug; break; + } + + mbgl::Log::Record(evtSeverity, mbgl::Event::OpenGL, "GL_%s GL_%s %u GL_%s - %s", strSource.c_str(), strType.c_str(), id, strSeverity.c_str(), message); +} + +} // namespace extension +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/debugging_extension.hpp b/src/mbgl/gl/debugging_extension.hpp new file mode 100644 index 0000000000..5657bbde88 --- /dev/null +++ b/src/mbgl/gl/debugging_extension.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include <mbgl/gl/extension.hpp> +#include <mbgl/gl/gl.hpp> + +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_DISPLAY_LIST 0x82E7 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TEXTURE 0x1702 +#define GL_RENDERBUFFER 0x8D41 +#define GL_FRAMEBUFFER 0x8D40 + +namespace mbgl { +namespace gl { +namespace extension { + +class Debugging { +public: + using Callback = void (*)(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam); + + static void DebugCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei /* length */, + const GLchar* message, + const void* /* userParam */); + + template <typename Fn> + Debugging(const Fn& loadExtension) + : +#ifndef NDEBUG + pushDebugGroup( + loadExtension({ { "GL_KHR_debug", "glPushDebugGroup" } })), + popDebugGroup( + loadExtension({ { "GL_KHR_debug", "glPopDebugGroup" } })), + pushGroupMarkerEXT( + loadExtension({ { "GL_EXT_debug_marker", "glPushGroupMarkerEXT" } })), + popGroupMarkerEXT( + loadExtension({ { "GL_EXT_debug_marker", "glPopGroupMarkerEXT" } })), +#endif + debugMessageControl( + loadExtension({ { "GL_KHR_debug", "glDebugMessageControl" }, + { "GL_ARB_debug_output", "glDebugMessageControlARB" } })), + debugMessageCallback( + loadExtension({ { "GL_KHR_debug", "glDebugMessageCallback" }, + { "GL_ARB_debug_output", "glDebugMessageCallbackARB" } })) { + } + +#ifndef NDEBUG + const ExtensionFunction<void(GLenum source, + GLuint id, + GLsizei length, + const GLchar* message)> pushDebugGroup; + + const ExtensionFunction<void()> popDebugGroup; + + const ExtensionFunction<void(GLsizei length, + const GLchar* marker)> pushGroupMarkerEXT; + + const ExtensionFunction<void()> popGroupMarkerEXT; +#endif + + const ExtensionFunction<void(GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint* ids, + GLboolean enabled)> debugMessageControl; + + const ExtensionFunction<void(Callback callback, + const void* userParam)> debugMessageCallback; +}; + +} // namespace extension +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/draw_mode.hpp b/src/mbgl/gl/draw_mode.hpp index ab86d5e469..275eb25b89 100644 --- a/src/mbgl/gl/draw_mode.hpp +++ b/src/mbgl/gl/draw_mode.hpp @@ -1,7 +1,7 @@ #pragma once +#include <mbgl/gl/types.hpp> #include <mbgl/gl/primitives.hpp> -#include <mbgl/util/variant.hpp> #include <cassert> @@ -11,7 +11,9 @@ namespace gl { 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_) {} @@ -21,7 +23,9 @@ public: class Lines { public: using Primitive = Line; + static constexpr std::size_t bufferGroupSize = 2; + static constexpr PrimitiveType primitiveType = PrimitiveType::Lines; explicit Lines(float lineWidth_) : lineWidth(lineWidth_) { assert(lineWidth > 0); @@ -35,7 +39,9 @@ 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; + static constexpr std::size_t bufferGroupSize = 1; + static constexpr PrimitiveType primitiveType = PrimitiveType::LineStrip; explicit LineStrip(float lineWidth_) : lineWidth(lineWidth_) { assert(lineWidth > 0); @@ -47,7 +53,9 @@ public: class Triangles { public: using Primitive = Triangle; + static constexpr std::size_t bufferGroupSize = 3; + static constexpr PrimitiveType primitiveType = PrimitiveType::Triangles; }; class TriangleStrip { @@ -55,7 +63,9 @@ 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; + static constexpr std::size_t bufferGroupSize = 1; + static constexpr PrimitiveType primitiveType = PrimitiveType::TriangleStrip; }; // Special draw mode for use with VertexVector<Indexed, Vertex>, in which @@ -65,12 +75,5 @@ public: static constexpr std::size_t bufferGroupSize = 1; }; -using DrawMode = variant< - Points, - Lines, - LineStrip, - Triangles, - TriangleStrip>; - } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/extension.cpp b/src/mbgl/gl/extension.cpp deleted file mode 100644 index e6b4d9156e..0000000000 --- a/src/mbgl/gl/extension.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include <mbgl/gl/extension.hpp> -#include <mbgl/gl/gl.hpp> - -#include <mutex> -#include <string> -#include <vector> -#include <cstring> - -namespace mbgl { -namespace gl { -namespace detail { - -using Probes = std::vector<ExtensionFunctionBase::Probe>; -using ExtensionFunctions = std::vector<std::pair<glProc*, Probes>>; -ExtensionFunctions& extensionFunctions() { - static ExtensionFunctions functions; - return functions; -} - -ExtensionFunctionBase::ExtensionFunctionBase(std::initializer_list<Probe> probes) { - extensionFunctions().emplace_back(&ptr, probes); -} - -} // namespace detail - -static std::once_flag initializeExtensionsOnce; - -void InitializeExtensions(glProc (*getProcAddress)(const char*)) { - std::call_once(initializeExtensionsOnce, [getProcAddress] { - if (const char* extensions = - reinterpret_cast<const char*>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)))) { - for (auto fn : detail::extensionFunctions()) { - for (auto probe : fn.second) { - if (strstr(extensions, probe.first) != nullptr) { - *fn.first = getProcAddress(probe.second); - break; - } - } - } - } - }); -} - -} // namespace gl -} // namespace mbgl diff --git a/src/mbgl/gl/extension.hpp b/src/mbgl/gl/extension.hpp index ea5a1ae5f5..8710314ef2 100644 --- a/src/mbgl/gl/extension.hpp +++ b/src/mbgl/gl/extension.hpp @@ -2,31 +2,21 @@ #include <initializer_list> #include <utility> +#include <functional> namespace mbgl { namespace gl { -using glProc = void (*)(); -void InitializeExtensions(glProc (*getProcAddress)(const char*)); - -namespace detail { - -class ExtensionFunctionBase { -public: - using Probe = std::pair<const char*, const char*>; - ExtensionFunctionBase(std::initializer_list<Probe>); - glProc ptr; -}; - -} // namespace detail +using ProcAddress = void (*)(); template <class> class ExtensionFunction; template <class R, class... Args> -class ExtensionFunction<R(Args...)> : protected detail::ExtensionFunctionBase { +class ExtensionFunction<R(Args...)> { public: - using detail::ExtensionFunctionBase::ExtensionFunctionBase; + ExtensionFunction(const ProcAddress ptr_) : ptr(ptr_) { + } explicit operator bool() const { return ptr; @@ -35,6 +25,9 @@ public: R operator()(Args... args) const { return (*reinterpret_cast<R (*)(Args...)>(ptr))(std::forward<Args>(args)...); } + +private: + const ProcAddress ptr; }; } // namespace gl diff --git a/src/mbgl/gl/features.hpp b/src/mbgl/gl/features.hpp new file mode 100644 index 0000000000..1757093967 --- /dev/null +++ b/src/mbgl/gl/features.hpp @@ -0,0 +1,7 @@ +#pragma once + +#if __APPLE__ + #define MBGL_HAS_BINARY_PROGRAMS 0 +#else + #define MBGL_HAS_BINARY_PROGRAMS 1 +#endif diff --git a/src/mbgl/gl/gl.hpp b/src/mbgl/gl/gl.hpp new file mode 100644 index 0000000000..3e21731330 --- /dev/null +++ b/src/mbgl/gl/gl.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include <stdexcept> +#include <limits> + +#if __APPLE__ + #include "TargetConditionals.h" + #if TARGET_OS_IPHONE + #include <OpenGLES/ES2/gl.h> + #include <OpenGLES/ES2/glext.h> + #elif TARGET_IPHONE_SIMULATOR + #include <OpenGLES/ES2/gl.h> + #include <OpenGLES/ES2/glext.h> + #elif TARGET_OS_MAC + #include <OpenGL/OpenGL.h> + #include <OpenGL/gl.h> + #include <OpenGL/glext.h> + #else + #error Unsupported Apple platform + #endif +#elif __ANDROID__ || MBGL_USE_GLES2 + #define GL_GLEXT_PROTOTYPES + #include <GLES2/gl2.h> + #include <GLES2/gl2ext.h> +#elif __QT__ && QT_VERSION >= 0x050000 + #define GL_GLEXT_PROTOTYPES + #include <QtGui/qopengl.h> +#else + #define GL_GLEXT_PROTOTYPES + #include <GL/gl.h> + #include <GL/glext.h> +#endif + +namespace mbgl { +namespace gl { + +struct Error : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +void checkError(const char *cmd, const char *file, int line); + +#ifndef NDEBUG +#define MBGL_CHECK_ERROR(cmd) ([&]() { struct __MBGL_C_E { ~__MBGL_C_E() noexcept(false) { ::mbgl::gl::checkError(#cmd, __FILE__, __LINE__); } } __MBGL_C_E; return cmd; }()) +#else +#define MBGL_CHECK_ERROR(cmd) (cmd) +#endif + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/index_buffer.hpp b/src/mbgl/gl/index_buffer.hpp index b3610f4154..01b6396e00 100644 --- a/src/mbgl/gl/index_buffer.hpp +++ b/src/mbgl/gl/index_buffer.hpp @@ -24,7 +24,9 @@ public: std::size_t byteSize() const { return v.size() * sizeof(uint16_t); } bool empty() const { return v.empty(); } + void clear() { v.clear(); } const uint16_t* data() const { return v.data(); } + const std::vector<uint16_t>& vector() const { return v; } private: std::vector<uint16_t> v; diff --git a/src/mbgl/gl/object.cpp b/src/mbgl/gl/object.cpp index e2d476e0c0..2c5f1bca1f 100644 --- a/src/mbgl/gl/object.cpp +++ b/src/mbgl/gl/object.cpp @@ -33,7 +33,9 @@ void TextureDeleter::operator()(TextureID id) const { void VertexArrayDeleter::operator()(VertexArrayID id) const { assert(context); - context->abandonedVertexArrays.push_back(id); + if (id != 0) { + context->abandonedVertexArrays.push_back(id); + } } void FramebufferDeleter::operator()(FramebufferID id) const { diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index 33387e9d4e..3b54ec194a 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -5,9 +5,16 @@ #include <mbgl/gl/context.hpp> #include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/vertex_array.hpp> #include <mbgl/gl/attribute.hpp> #include <mbgl/gl/uniform.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/programs/binary_program.hpp> +#include <mbgl/programs/program_parameters.hpp> +#include <mbgl/shaders/shaders.hpp> + #include <string> namespace mbgl { @@ -20,17 +27,85 @@ public: using Attributes = As; using Uniforms = Us; - using Vertex = typename Attributes::Vertex; using UniformValues = typename Uniforms::Values; - - static_assert(std::is_standard_layout<Vertex>::value, "vertex type must use standard layout"); + using AttributeBindings = typename Attributes::Bindings; Program(Context& context, const std::string& vertexSource, const std::string& fragmentSource) - : vertexShader(context.createShader(ShaderType::Vertex, vertexSource)), - fragmentShader(context.createShader(ShaderType::Fragment, fragmentSource)), - program(context.createProgram(vertexShader, fragmentShader)), - attributesState(Attributes::state(program)), - uniformsState((context.linkProgram(program), Uniforms::state(program))) {} + : program( + context.createProgram(context.createShader(ShaderType::Vertex, vertexSource), + context.createShader(ShaderType::Fragment, fragmentSource))), + uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))), + attributeLocations(Attributes::bindLocations(program)) { + // Re-link program after manually binding only active attributes in Attributes::bindLocations + context.linkProgram(program); + } + + template <class BinaryProgram> + Program(Context& context, const BinaryProgram& binaryProgram) + : program(context.createProgram(binaryProgram.format(), binaryProgram.code())), + uniformsState(Uniforms::loadNamedLocations(binaryProgram)), + attributeLocations(Attributes::loadNamedLocations(binaryProgram)) { + } + + static Program createProgram(gl::Context& context, + const ProgramParameters& programParameters, + const char* name, + const char* vertexSource_, + const char* fragmentSource_) { + const std::string vertexSource = shaders::vertexSource(programParameters, vertexSource_); + const std::string fragmentSource = shaders::fragmentSource(programParameters, fragmentSource_); + +#if MBGL_HAS_BINARY_PROGRAMS + optional<std::string> cachePath = programParameters.cachePath(name); + if (cachePath && context.supportsProgramBinaries()) { + const std::string identifier = shaders::programIdentifier(vertexSource, fragmentSource); + + try { + if (auto cachedBinaryProgram = util::readFile(*cachePath)) { + const BinaryProgram binaryProgram(std::move(*cachedBinaryProgram)); + if (binaryProgram.identifier() == identifier) { + return Program { context, binaryProgram }; + } else { + Log::Warning(Event::OpenGL, + "Cached program %s changed. Recompilation required.", + name); + } + } + } catch (std::runtime_error& error) { + Log::Warning(Event::OpenGL, "Could not load cached program: %s", + error.what()); + } + + // Compile the shader + Program result{ context, vertexSource, fragmentSource }; + + try { + if (const auto binaryProgram = + result.template get<BinaryProgram>(context, identifier)) { + util::write_file(*cachePath, binaryProgram->serialize()); + Log::Warning(Event::OpenGL, "Caching program in: %s", (*cachePath).c_str()); + } + } catch (std::runtime_error& error) { + Log::Warning(Event::OpenGL, "Failed to cache program: %s", error.what()); + } + + return std::move(result); + } +#endif + + (void)name; + return Program { context, vertexSource, fragmentSource }; + } + + template <class BinaryProgram> + optional<BinaryProgram> get(Context& context, const std::string& identifier) const { + if (auto binaryProgram = context.getBinaryProgram(program)) { + return BinaryProgram{ binaryProgram->first, std::move(binaryProgram->second), + identifier, Attributes::getNamedLocations(attributeLocations), + Uniforms::getNamedLocations(uniformsState) }; + } + return {}; + } template <class DrawMode> void draw(Context& context, @@ -38,32 +113,37 @@ public: DepthMode depthMode, StencilMode stencilMode, ColorMode colorMode, - UniformValues&& uniformValues, - const VertexBuffer<Vertex>& vertexBuffer, + const UniformValues& uniformValues, + VertexArray& vertexArray, + const AttributeBindings& attributeBindings, const IndexBuffer<DrawMode>& indexBuffer, - const SegmentVector<Attributes>& segments) { + std::size_t indexOffset, + std::size_t indexLength) { static_assert(std::is_same<Primitive, typename DrawMode::Primitive>::value, "incompatible draw mode"); - context.draw({ - std::move(drawMode), - std::move(depthMode), - std::move(stencilMode), - std::move(colorMode), - program, - vertexBuffer.buffer, - indexBuffer.buffer, - segments, - Uniforms::binder(uniformsState, std::move(uniformValues)), - Attributes::binder(attributesState) - }); + + context.setDrawMode(drawMode); + context.setDepthMode(depthMode); + context.setStencilMode(stencilMode); + context.setColorMode(colorMode); + + context.program = program; + + Uniforms::bind(uniformsState, uniformValues); + + vertexArray.bind(context, + indexBuffer.buffer, + Attributes::toBindingArray(attributeLocations, attributeBindings)); + + context.draw(drawMode.primitiveType, + indexOffset, + indexLength); } private: - UniqueShader vertexShader; - UniqueShader fragmentShader; UniqueProgram program; - typename Attributes::State attributesState; typename Uniforms::State uniformsState; + typename Attributes::Locations attributeLocations; }; } // namespace gl diff --git a/src/mbgl/gl/program_binary_extension.hpp b/src/mbgl/gl/program_binary_extension.hpp new file mode 100644 index 0000000000..a4aa1eeefc --- /dev/null +++ b/src/mbgl/gl/program_binary_extension.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <mbgl/gl/features.hpp> +#include <mbgl/gl/extension.hpp> +#include <mbgl/gl/gl.hpp> + +#if MBGL_HAS_BINARY_PROGRAMS + +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF + +namespace mbgl { +namespace gl { +namespace extension { + +class ProgramBinary { +public: + template <typename Fn> + ProgramBinary(const Fn& loadExtension) + : getProgramBinary(loadExtension({ + { "GL_OES_get_program_binary", "glGetProgramBinaryOES" }, + { "GL_ARB_get_program_binary", "glGetProgramBinary" }, + })), + programBinary(loadExtension({ + { "GL_OES_get_program_binary", "glProgramBinaryOES" }, + { "GL_ARB_get_program_binary", "glProgramBinary" }, + })) { + } + + const ExtensionFunction<void( + GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary)> + getProgramBinary; + + const ExtensionFunction<void( + GLuint program, GLenum binaryFormat, const GLvoid* binary, GLint length)> + programBinary; +}; + +} // namespace extension +} // namespace gl +} // namespace mbgl + +#endif diff --git a/src/mbgl/gl/segment.hpp b/src/mbgl/gl/segment.hpp deleted file mode 100644 index 8f74afd237..0000000000 --- a/src/mbgl/gl/segment.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include <mbgl/util/optional.hpp> - -#include <cstddef> - -namespace mbgl { -namespace gl { - -class Segment { -public: - Segment(std::size_t vertexOffset_, - std::size_t indexOffset_, - std::size_t vertexLength_ = 0, - std::size_t indexLength_ = 0) - : vertexOffset(vertexOffset_), - indexOffset(indexOffset_), - vertexLength(vertexLength_), - indexLength(indexLength_) {} - - const std::size_t vertexOffset; - const std::size_t indexOffset; - - std::size_t vertexLength; - std::size_t indexLength; - -private: - friend class Context; - mutable optional<UniqueVertexArray> vao; -}; - -template <class Attributes> -class SegmentVector : public std::vector<Segment> { -public: - SegmentVector() = default; -}; - -} // namespace gl -} // namespace mbgl diff --git a/src/mbgl/gl/state.hpp b/src/mbgl/gl/state.hpp index efe869e5b9..17503cc043 100644 --- a/src/mbgl/gl/state.hpp +++ b/src/mbgl/gl/state.hpp @@ -1,5 +1,7 @@ #pragma once +#include <tuple> + namespace mbgl { namespace gl { @@ -12,13 +14,16 @@ namespace gl { // static void Set(const Type& value); // static Type Get(); // }; -template <typename T> +template <typename T, typename... Args> class State { public: + State(Args&&... args) : params(std::forward_as_tuple(::std::forward<Args>(args)...)) { + } + void operator=(const typename T::Type& value) { if (*this != value) { setCurrentValue(value); - T::Set(currentValue); + set(std::index_sequence_for<Args...>{}); } } @@ -50,23 +55,15 @@ public: } private: - typename T::Type currentValue = T::Default; - bool dirty = true; -}; - -// Helper struct that stores the current state and restores it upon destruction. You should not use -// this code normally, except for debugging purposes. -template <typename T> -class PreserveState { -public: - PreserveState() : value(T::Get()) { - } - ~PreserveState() { - T::Set(value); + template <std::size_t... I> + void set(std::index_sequence<I...>) { + T::Set(currentValue, std::get<I>(params)...); } private: - const typename T::Type value; + typename T::Type currentValue = T::Default; + bool dirty = true; + const std::tuple<Args...> params; }; } // namespace gl diff --git a/src/mbgl/gl/texture.hpp b/src/mbgl/gl/texture.hpp index ffa08ec80a..625e69233a 100644 --- a/src/mbgl/gl/texture.hpp +++ b/src/mbgl/gl/texture.hpp @@ -3,21 +3,29 @@ #include <mbgl/gl/object.hpp> #include <mbgl/util/size.hpp> -#include <functional> - namespace mbgl { namespace gl { class Texture { public: - Texture(Size size_, std::function<UniqueTexture()> getTexture) : size(size_), texture(getTexture()) {} + Texture(Size size_, UniqueTexture texture_, + TextureFilter filter_ = TextureFilter::Nearest, + TextureMipMap mipmap_ = TextureMipMap::No, + TextureWrap wrapX_ = TextureWrap::Clamp, + TextureWrap wrapY_ = TextureWrap::Clamp) + : size(std::move(size_)), + texture(std::move(texture_)), + filter(filter_), + mipmap(mipmap_), + wrapX(wrapX_), + wrapY(wrapY_) {} Size size; UniqueTexture texture; - TextureFilter filter = TextureFilter::Nearest; - TextureMipMap mipmap = TextureMipMap::No; - TextureWrap wrapX = TextureWrap::Clamp; - TextureWrap wrapY = TextureWrap::Clamp; + TextureFilter filter; + TextureMipMap mipmap; + TextureWrap wrapX; + TextureWrap wrapY; }; } // namespace gl diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index 577629d5d3..da08195e58 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -15,8 +15,16 @@ using VertexArrayID = uint32_t; using FramebufferID = uint32_t; using RenderbufferID = uint32_t; -using AttributeLocation = int32_t; +// OpenGL does not formally define a type for attribute locations, but most APIs use +// GLuint. The exception is glGetAttribLocation, which returns GLint so that -1 can +// be used as an error indicator. +using AttributeLocation = uint32_t; + +// OpenGL does not formally define a type for uniform locations, but all APIs use GLint. +// The value -1 is special, typically used as a placeholder for an unused uniform and +// "silently ignored". using UniformLocation = int32_t; + using TextureUnit = uint8_t; enum class ShaderType : uint32_t { @@ -24,7 +32,7 @@ enum class ShaderType : uint32_t { Fragment = 0x8B30 }; -enum class DataType : uint32_t { +enum class DataType : uint16_t { Byte = 0x1400, UnsignedByte = 0x1401, Short = 0x1402, @@ -34,19 +42,14 @@ enum class DataType : uint32_t { Float = 0x1406 }; -template <typename T> struct DataTypeOf; - -template <> struct DataTypeOf<int8_t> : std::integral_constant<DataType, DataType::Byte> {}; -template <> struct DataTypeOf<uint8_t> : std::integral_constant<DataType, DataType::UnsignedByte> {}; -template <> struct DataTypeOf<int16_t> : std::integral_constant<DataType, DataType::Short> {}; -template <> struct DataTypeOf<uint16_t> : std::integral_constant<DataType, DataType::UnsignedShort> {}; -template <> struct DataTypeOf<int32_t> : std::integral_constant<DataType, DataType::Integer> {}; -template <> struct DataTypeOf<uint32_t> : std::integral_constant<DataType, DataType::UnsignedInteger> {}; -template <> struct DataTypeOf<float> : std::integral_constant<DataType, DataType::Float> {}; - enum class RenderbufferType : uint32_t { RGBA = 0x8058, DepthStencil = 0x88F0, +#if not MBGL_USE_GLES2 + DepthComponent = 0x1902, // GL_DEPTH_COMPONENT +#else + DepthComponent = 0x81A5, // GL_DEPTH_COMPONENT16 +#endif // MBGL_USE_GLES2 }; enum class TextureMipMap : bool { No = false, Yes = true }; @@ -71,8 +74,6 @@ enum class PrimitiveType { TriangleFan = 0x0006 }; -#if not MBGL_USE_GLES2 - struct PixelStorageType { int32_t alignment; }; @@ -81,7 +82,33 @@ constexpr bool operator!=(const PixelStorageType& a, const PixelStorageType& b) return a.alignment != b.alignment; } -#endif // MBGL_USE_GLES2 +using BinaryProgramFormat = uint32_t; + +enum class UniformDataType : uint32_t { + Float = 0x1406, + FloatVec2 = 0x8B50, + FloatVec3 = 0x8B51, + FloatVec4 = 0x8B52, + Int = 0x1404, + IntVec2 = 0x8B53, + IntVec3 = 0x8B54, + IntVec4 = 0x8B55, + Bool = 0x8B56, + BoolVec2 = 0x8B57, + BoolVec3 = 0x8B58, + BoolVec4 = 0x8B59, + FloatMat2 = 0x8B5A, + FloatMat3 = 0x8B5B, + FloatMat4 = 0x8B5C, + Sampler2D = 0x8B5E, + SamplerCube = 0x8B60, +}; + +enum class BufferUsage : uint32_t { + StreamDraw = 0x88E0, + StaticDraw = 0x88E4, + DynamicDraw = 0x88E8, +}; } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/uniform.cpp b/src/mbgl/gl/uniform.cpp index 7b674f2cde..2946980c19 100644 --- a/src/mbgl/gl/uniform.cpp +++ b/src/mbgl/gl/uniform.cpp @@ -4,6 +4,8 @@ #include <mbgl/util/size.hpp> #include <mbgl/util/convert.hpp> +#include <memory> + namespace mbgl { namespace gl { @@ -79,5 +81,96 @@ void bindUniform<std::array<uint16_t, 2>>(UniformLocation location, const std::a // Add more as needed. +#ifndef NDEBUG + +ActiveUniforms activeUniforms(ProgramID id) { + ActiveUniforms active; + + GLint count; + GLint maxLength; + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &count)); + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength)); + + auto name = std::make_unique<GLchar[]>(maxLength); + GLsizei length; + GLint size; + GLenum type; + for (GLint index = 0; index < count; index++) { + MBGL_CHECK_ERROR( + glGetActiveUniform(id, index, maxLength, &length, &size, &type, name.get())); + active.emplace( + std::string{ name.get(), static_cast<size_t>(length) }, + ActiveUniform{ static_cast<size_t>(size), static_cast<UniformDataType>(type) }); + } + + return active; +} + +template <> +bool verifyUniform<float>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::Float); + return true; +} + +template <> +bool verifyUniform<std::array<float, 2>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec2); + return true; +} + +template <> +bool verifyUniform<std::array<float, 3>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec3); + return true; +} + +template <> +bool verifyUniform<std::array<double, 16>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatMat4); + return true; +} + +template <> +bool verifyUniform<bool>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::Bool || + uniform.type == UniformDataType::Int || + uniform.type == UniformDataType::Float)); + return true; +} + +template <> +bool verifyUniform<uint8_t>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::Int || + uniform.type == UniformDataType::Float || + uniform.type == UniformDataType::Sampler2D)); + return true; +} + +template <> +bool verifyUniform<Color>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec4); + return true; +} + +template <> +bool verifyUniform<Size>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec2); + return true; +} + +template <> +bool verifyUniform<std::array<uint16_t, 2>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::IntVec2 || + uniform.type == UniformDataType::FloatVec2)); + return true; +} + +// Add more as needed. + +#endif + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/uniform.hpp b/src/mbgl/gl/uniform.hpp index 5c0980baf0..5a78068fc8 100644 --- a/src/mbgl/gl/uniform.hpp +++ b/src/mbgl/gl/uniform.hpp @@ -6,6 +6,8 @@ #include <mbgl/util/indexed_tuple.hpp> #include <array> +#include <vector> +#include <map> #include <functional> namespace mbgl { @@ -21,17 +23,35 @@ public: T t; }; +class ActiveUniform { +public: + std::size_t size; + UniformDataType type; +}; + +#ifndef NDEBUG + +template <class T> +bool verifyUniform(const ActiveUniform&); + +using ActiveUniforms = std::map<std::string, ActiveUniform>; +ActiveUniforms activeUniforms(ProgramID); + +#endif + template <class Tag, class T> class Uniform { public: using Value = UniformValue<Tag, T>; + using Type = T; + class State { public: State(UniformLocation location_) : location(std::move(location_)) {} void operator=(const Value& value) { - if (!current || *current != value.t) { + if (location >= 0 && (!current || *current != value.t)) { current = value.t; bindUniform(location, value.t); } @@ -52,32 +72,71 @@ template <class Tag, class T, size_t N> using UniformMatrix = Uniform<Tag, std::array<T, N*N>>; #define MBGL_DEFINE_UNIFORM_SCALAR(type_, name_) \ - struct name_ : ::mbgl::gl::UniformScalar<name_, type_> { static constexpr auto name = #name_; } + struct name_ : ::mbgl::gl::UniformScalar<name_, type_> { static auto name() { return #name_; } } #define MBGL_DEFINE_UNIFORM_VECTOR(type_, n_, name_) \ - struct name_ : ::mbgl::gl::UniformVector<name_, type_, n_> { static constexpr auto name = #name_; } + struct name_ : ::mbgl::gl::UniformVector<name_, type_, n_> { static auto name() { return #name_; } } #define MBGL_DEFINE_UNIFORM_MATRIX(type_, n_, name_) \ - struct name_ : ::mbgl::gl::UniformMatrix<name_, type_, n_> { static constexpr auto name = #name_; } + struct name_ : ::mbgl::gl::UniformMatrix<name_, type_, n_> { static auto name() { return #name_; } } UniformLocation uniformLocation(ProgramID, const char * name); template <class... Us> class Uniforms { public: + using Types = TypeList<Us...>; using State = IndexedTuple<TypeList<Us...>, TypeList<typename Us::State...>>; using Values = IndexedTuple<TypeList<Us...>, TypeList<typename Us::Value...>>; + using NamedLocations = std::vector<std::pair<const std::string, UniformLocation>>; + + static State bindLocations(const ProgramID& id) { +#ifndef NDEBUG + // Verify active uniform types match the enum + const auto active = activeUniforms(id); + + util::ignore( + { // Some shader programs have uniforms declared, but not used, so they're not active. + // Therefore, we'll only verify them when they are indeed active. + (active.find(Us::name()) != active.end() + ? verifyUniform<typename Us::Type>(active.at(Us::name())) + : false)... }); +#endif + + return State { { uniformLocation(id, Us::name()) }... }; + } - static State state(const ProgramID& id) { - return State(typename Us::State(uniformLocation(id, Us::name))...); + template <class Program> + static State loadNamedLocations(const Program& program) { + return State(typename Us::State(program.uniformLocation(Us::name()))...); } - static std::function<void ()> binder(State& state, Values&& values_) { - return [&state, values = std::move(values_)] () mutable { - util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... }); - }; + static NamedLocations getNamedLocations(const State& state) { + return NamedLocations{ { Us::name(), state.template get<Us>().location }... }; + } + + static void bind(State& state, const Values& values) { + util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... }); } }; + +namespace detail { + +template <class...> +struct ConcatenateUniforms; + +template <class... As, class... Bs> +struct ConcatenateUniforms<TypeList<As...>, TypeList<Bs...>> { + using Type = Uniforms<As..., Bs...>; +}; + +} // namespace detail + +template <class A, class B> +using ConcatenateUniforms = typename detail::ConcatenateUniforms< + typename A::Types, + typename B::Types>::Type; + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp index 603d82dfd5..89014fe6bc 100644 --- a/src/mbgl/gl/value.cpp +++ b/src/mbgl/gl/value.cpp @@ -1,6 +1,7 @@ #include <mbgl/gl/value.hpp> #include <mbgl/gl/gl.hpp> -#include <mbgl/gl/vertex_array.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/vertex_array_extension.hpp> namespace mbgl { namespace gl { @@ -266,6 +267,18 @@ Viewport::Type Viewport::Get() { { static_cast<uint32_t>(viewport[2]), static_cast<uint32_t>(viewport[3]) } }; } +const constexpr ScissorTest::Type ScissorTest::Default; + +void ScissorTest::Set(const Type& value) { + MBGL_CHECK_ERROR(value ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST)); +} + +ScissorTest::Type ScissorTest::Get() { + Type scissorTest; + MBGL_CHECK_ERROR(scissorTest = glIsEnabled(GL_SCISSOR_TEST)); + return scissorTest; +} + const constexpr BindFramebuffer::Type BindFramebuffer::Default; void BindFramebuffer::Set(const Type& value) { @@ -328,15 +341,17 @@ BindElementBuffer::Type BindElementBuffer::Get() { const constexpr BindVertexArray::Type BindVertexArray::Default; -void BindVertexArray::Set(const Type& value) { - if (gl::BindVertexArray) { - MBGL_CHECK_ERROR(gl::BindVertexArray(value)); +void BindVertexArray::Set(const Type& value, const Context& context) { + if (auto vertexArray = context.getVertexArrayExtension()) { + if (vertexArray->bindVertexArray) { + MBGL_CHECK_ERROR(vertexArray->bindVertexArray(value)); + } } } -BindVertexArray::Type BindVertexArray::Get() { +BindVertexArray::Type BindVertexArray::Get(const Context& context) { GLint binding = 0; - if (gl::BindVertexArray) { + if (context.getVertexArrayExtension()) { #ifdef GL_VERTEX_ARRAY_BINDING MBGL_CHECK_ERROR(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &binding)); #elif GL_VERTEX_ARRAY_BINDING_OES @@ -350,6 +365,52 @@ BindVertexArray::Type BindVertexArray::Get() { return binding; } +const optional<AttributeBinding> VertexAttribute::Default {}; + +void VertexAttribute::Set(const optional<AttributeBinding>& binding, Context& context, AttributeLocation location) { + if (binding) { + context.vertexBuffer = binding->vertexBuffer; + MBGL_CHECK_ERROR(glEnableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttribPointer( + location, + static_cast<GLint>(binding->attributeSize), + static_cast<GLenum>(binding->attributeType), + static_cast<GLboolean>(false), + static_cast<GLsizei>(binding->vertexSize), + reinterpret_cast<GLvoid*>(binding->attributeOffset + (binding->vertexSize * binding->vertexOffset)))); + } else { + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + } +} + +const constexpr PixelStorePack::Type PixelStorePack::Default; + +void PixelStorePack::Set(const Type& value) { + assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || + value.alignment == 8); + MBGL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, value.alignment)); +} + +PixelStorePack::Type PixelStorePack::Get() { + Type value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_PACK_ALIGNMENT, &value.alignment)); + return value; +} + +const constexpr PixelStoreUnpack::Type PixelStoreUnpack::Default; + +void PixelStoreUnpack::Set(const Type& value) { + assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || + value.alignment == 8); + MBGL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, value.alignment)); +} + +PixelStoreUnpack::Type PixelStoreUnpack::Get() { + Type value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_UNPACK_ALIGNMENT, &value.alignment)); + return value; +} + #if not MBGL_USE_GLES2 const constexpr PointSize::Type PointSize::Default; @@ -389,34 +450,6 @@ RasterPos::Type RasterPos::Get() { return { pos[0], pos[1], pos[2], pos[3] }; } -const constexpr PixelStorePack::Type PixelStorePack::Default; - -void PixelStorePack::Set(const Type& value) { - assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || - value.alignment == 8); - MBGL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, value.alignment)); -} - -PixelStorePack::Type PixelStorePack::Get() { - Type value; - MBGL_CHECK_ERROR(glGetIntegerv(GL_PACK_ALIGNMENT, &value.alignment)); - return value; -} - -const constexpr PixelStoreUnpack::Type PixelStoreUnpack::Default; - -void PixelStoreUnpack::Set(const Type& value) { - assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || - value.alignment == 8); - MBGL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, value.alignment)); -} - -PixelStoreUnpack::Type PixelStoreUnpack::Get() { - Type value; - MBGL_CHECK_ERROR(glGetIntegerv(GL_UNPACK_ALIGNMENT, &value.alignment)); - return value; -} - const constexpr PixelTransferDepth::Type PixelTransferDepth::Default; void PixelTransferDepth::Set(const Type& value) { diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp index eccd3e7373..19e9af194f 100644 --- a/src/mbgl/gl/value.hpp +++ b/src/mbgl/gl/value.hpp @@ -4,12 +4,16 @@ #include <mbgl/gl/depth_mode.hpp> #include <mbgl/gl/stencil_mode.hpp> #include <mbgl/gl/color_mode.hpp> +#include <mbgl/gl/attribute.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/size.hpp> #include <mbgl/util/range.hpp> namespace mbgl { namespace gl { + +class Context; + namespace value { struct ClearDepth { @@ -179,6 +183,13 @@ struct Viewport { static Type Get(); }; +struct ScissorTest { + using Type = bool; + static const constexpr Type Default = false; + static void Set(const Type&); + static Type Get(); +}; + constexpr bool operator!=(const Viewport::Type& a, const Viewport::Type& b) { return a.x != b.x || a.y != b.y || a.size != b.size; } @@ -225,6 +236,26 @@ struct BindElementBuffer { struct BindVertexArray { using Type = gl::VertexArrayID; static const constexpr Type Default = 0; + static void Set(const Type&, const Context&); + static Type Get(const Context&); +}; + +struct VertexAttribute { + using Type = optional<gl::AttributeBinding>; + static const Type Default; + static void Set(const Type&, Context&, AttributeLocation); +}; + +struct PixelStorePack { + using Type = PixelStorageType; + static const constexpr Type Default = { 4 }; + static void Set(const Type&); + static Type Get(); +}; + +struct PixelStoreUnpack { + using Type = PixelStorageType; + static const constexpr Type Default = { 4 }; static void Set(const Type&); static Type Get(); }; @@ -268,20 +299,6 @@ constexpr bool operator!=(const RasterPos::Type& a, const RasterPos::Type& b) { return a.x != b.x || a.y != b.y || a.z != b.z || a.w != b.w; } -struct PixelStorePack { - using Type = PixelStorageType; - static const constexpr Type Default = { 4 }; - static void Set(const Type&); - static Type Get(); -}; - -struct PixelStoreUnpack { - using Type = PixelStorageType; - static const constexpr Type Default = { 4 }; - static void Set(const Type&); - static Type Get(); -}; - struct PixelTransferDepth { struct Type { float scale; diff --git a/src/mbgl/gl/vertex_array.cpp b/src/mbgl/gl/vertex_array.cpp index df63bbc4b7..68a500ac45 100644 --- a/src/mbgl/gl/vertex_array.cpp +++ b/src/mbgl/gl/vertex_array.cpp @@ -1,22 +1,18 @@ #include <mbgl/gl/vertex_array.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/gl.hpp> namespace mbgl { namespace gl { -ExtensionFunction<void(GLuint array)> - BindVertexArray({ { "GL_ARB_vertex_array_object", "glBindVertexArray" }, - { "GL_OES_vertex_array_object", "glBindVertexArrayOES" }, - { "GL_APPLE_vertex_array_object", "glBindVertexArrayAPPLE" } }); +void VertexArray::bind(Context& context, BufferID indexBuffer, const AttributeBindingArray& bindings) { + context.bindVertexArray = state->vertexArray; + state->indexBuffer = indexBuffer; -ExtensionFunction<void(GLsizei n, const GLuint* arrays)> - DeleteVertexArrays({ { "GL_ARB_vertex_array_object", "glDeleteVertexArrays" }, - { "GL_OES_vertex_array_object", "glDeleteVertexArraysOES" }, - { "GL_APPLE_vertex_array_object", "glDeleteVertexArraysAPPLE" } }); - -ExtensionFunction<void(GLsizei n, GLuint* arrays)> - GenVertexArrays({ { "GL_ARB_vertex_array_object", "glGenVertexArrays" }, - { "GL_OES_vertex_array_object", "glGenVertexArraysOES" }, - { "GL_APPLE_vertex_array_object", "glGenVertexArraysAPPLE" } }); + for (AttributeLocation location = 0; location < MAX_ATTRIBUTES; ++location) { + state->bindings[location] = bindings[location]; + } +} } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/vertex_array.hpp b/src/mbgl/gl/vertex_array.hpp index 6215e56f21..46c67017bb 100644 --- a/src/mbgl/gl/vertex_array.hpp +++ b/src/mbgl/gl/vertex_array.hpp @@ -1,14 +1,73 @@ #pragma once -#include <mbgl/gl/extension.hpp> -#include <mbgl/gl/gl.hpp> +#include <mbgl/gl/object.hpp> +#include <mbgl/gl/attribute.hpp> +#include <mbgl/gl/state.hpp> +#include <mbgl/gl/value.hpp> + +#include <array> +#include <memory> namespace mbgl { namespace gl { -extern ExtensionFunction<void(GLuint array)> BindVertexArray; -extern ExtensionFunction<void(GLsizei n, const GLuint* arrays)> DeleteVertexArrays; -extern ExtensionFunction<void(GLsizei n, GLuint* arrays)> GenVertexArrays; +class Context; + +class VertexArrayState { +public: + VertexArrayState(UniqueVertexArray vertexArray_, Context& context) + : vertexArray(std::move(vertexArray_)), + bindings(makeBindings(context, std::make_index_sequence<MAX_ATTRIBUTES>())) { + } + + void setDirty() { + indexBuffer.setDirty(); + for (auto& binding : bindings) { + binding.setDirty(); + } + } + + UniqueVertexArray vertexArray; + State<value::BindElementBuffer> indexBuffer; + + using AttributeState = State<value::VertexAttribute, Context&, AttributeLocation>; + std::array<AttributeState, MAX_ATTRIBUTES> bindings; + +private: + template <std::size_t... I> + std::array<AttributeState, MAX_ATTRIBUTES> makeBindings(Context& context, std::index_sequence<I...>) { + return {{ AttributeState { context, I }... }}; + } +}; + +class VertexArrayStateDeleter { +public: + VertexArrayStateDeleter(bool destroy_) + : destroy(destroy_) {} + + void operator()(VertexArrayState* ptr) const { + if (destroy) { + delete ptr; + } + } + +private: + bool destroy; +}; + +using UniqueVertexArrayState = std::unique_ptr<VertexArrayState, VertexArrayStateDeleter>; + +class VertexArray { +public: + VertexArray(UniqueVertexArrayState state_) + : state(std::move(state_)) { + } + + void bind(Context&, BufferID indexBuffer, const AttributeBindingArray&); + +private: + UniqueVertexArrayState state; +}; } // namespace gl -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/src/mbgl/gl/vertex_array_extension.hpp b/src/mbgl/gl/vertex_array_extension.hpp new file mode 100644 index 0000000000..707a20e6f0 --- /dev/null +++ b/src/mbgl/gl/vertex_array_extension.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include <mbgl/gl/extension.hpp> +#include <mbgl/gl/gl.hpp> + +namespace mbgl { +namespace gl { +namespace extension { + +class VertexArray { +public: + template <typename Fn> + VertexArray(const Fn& loadExtension) + : bindVertexArray( + loadExtension({ { "GL_ARB_vertex_array_object", "glBindVertexArray" }, + { "GL_OES_vertex_array_object", "glBindVertexArrayOES" }, + { "GL_APPLE_vertex_array_object", "glBindVertexArrayAPPLE" } })), + deleteVertexArrays( + loadExtension({ { "GL_ARB_vertex_array_object", "glDeleteVertexArrays" }, + { "GL_OES_vertex_array_object", "glDeleteVertexArraysOES" }, + { "GL_APPLE_vertex_array_object", "glDeleteVertexArraysAPPLE" } })), + genVertexArrays( + loadExtension({ { "GL_ARB_vertex_array_object", "glGenVertexArrays" }, + { "GL_OES_vertex_array_object", "glGenVertexArraysOES" }, + { "GL_APPLE_vertex_array_object", "glGenVertexArraysAPPLE" } })) { + } + + const ExtensionFunction<void(GLuint array)> bindVertexArray; + + const ExtensionFunction<void(GLsizei n, const GLuint* arrays)> deleteVertexArrays; + + const ExtensionFunction<void(GLsizei n, GLuint* arrays)> genVertexArrays; +}; + +} // namespace extension +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/vertex_buffer.hpp b/src/mbgl/gl/vertex_buffer.hpp index c9bc01f3e8..9f8b156b25 100644 --- a/src/mbgl/gl/vertex_buffer.hpp +++ b/src/mbgl/gl/vertex_buffer.hpp @@ -26,7 +26,9 @@ public: std::size_t byteSize() const { return v.size() * sizeof(Vertex); } bool empty() const { return v.empty(); } + void clear() { v.clear(); } const Vertex* data() const { return v.data(); } + const std::vector<Vertex>& vector() const { return v; } private: std::vector<Vertex> v; |