summaryrefslogtreecommitdiff
path: root/platform/gfx/gl/src/mbgl/gl
diff options
context:
space:
mode:
Diffstat (limited to 'platform/gfx/gl/src/mbgl/gl')
-rw-r--r--platform/gfx/gl/src/mbgl/gl/attribute.cpp19
-rw-r--r--platform/gfx/gl/src/mbgl/gl/attribute.hpp115
-rw-r--r--platform/gfx/gl/src/mbgl/gl/command_encoder.cpp60
-rw-r--r--platform/gfx/gl/src/mbgl/gl/command_encoder.hpp33
-rw-r--r--platform/gfx/gl/src/mbgl/gl/context.cpp755
-rw-r--r--platform/gfx/gl/src/mbgl/gl/context.hpp235
-rw-r--r--platform/gfx/gl/src/mbgl/gl/debugging_extension.cpp55
-rw-r--r--platform/gfx/gl/src/mbgl/gl/debugging_extension.hpp122
-rw-r--r--platform/gfx/gl/src/mbgl/gl/defines.hpp179
-rw-r--r--platform/gfx/gl/src/mbgl/gl/draw_scope_resource.hpp18
-rw-r--r--platform/gfx/gl/src/mbgl/gl/enum.cpp319
-rw-r--r--platform/gfx/gl/src/mbgl/gl/enum.hpp21
-rw-r--r--platform/gfx/gl/src/mbgl/gl/extension.hpp34
-rw-r--r--platform/gfx/gl/src/mbgl/gl/framebuffer.hpp16
-rw-r--r--platform/gfx/gl/src/mbgl/gl/index_buffer_resource.hpp18
-rw-r--r--platform/gfx/gl/src/mbgl/gl/object.cpp52
-rw-r--r--platform/gfx/gl/src/mbgl/gl/object.hpp60
-rw-r--r--platform/gfx/gl/src/mbgl/gl/offscreen_texture.cpp75
-rw-r--r--platform/gfx/gl/src/mbgl/gl/offscreen_texture.hpp24
-rw-r--r--platform/gfx/gl/src/mbgl/gl/program.hpp141
-rw-r--r--platform/gfx/gl/src/mbgl/gl/render_pass.cpp28
-rw-r--r--platform/gfx/gl/src/mbgl/gl/render_pass.hpp31
-rw-r--r--platform/gfx/gl/src/mbgl/gl/renderbuffer_resource.hpp19
-rw-r--r--platform/gfx/gl/src/mbgl/gl/renderer_backend.cpp69
-rw-r--r--platform/gfx/gl/src/mbgl/gl/state.hpp70
-rw-r--r--platform/gfx/gl/src/mbgl/gl/texture.cpp54
-rw-r--r--platform/gfx/gl/src/mbgl/gl/texture.hpp49
-rw-r--r--platform/gfx/gl/src/mbgl/gl/texture_resource.hpp22
-rw-r--r--platform/gfx/gl/src/mbgl/gl/types.hpp62
-rw-r--r--platform/gfx/gl/src/mbgl/gl/uniform.cpp202
-rw-r--r--platform/gfx/gl/src/mbgl/gl/uniform.hpp95
-rw-r--r--platform/gfx/gl/src/mbgl/gl/upload_pass.cpp117
-rw-r--r--platform/gfx/gl/src/mbgl/gl/upload_pass.hpp42
-rw-r--r--platform/gfx/gl/src/mbgl/gl/value.cpp602
-rw-r--r--platform/gfx/gl/src/mbgl/gl/value.hpp357
-rw-r--r--platform/gfx/gl/src/mbgl/gl/vertex_array.cpp24
-rw-r--r--platform/gfx/gl/src/mbgl/gl/vertex_array.hpp71
-rw-r--r--platform/gfx/gl/src/mbgl/gl/vertex_array_extension.hpp38
-rw-r--r--platform/gfx/gl/src/mbgl/gl/vertex_buffer_resource.hpp18
39 files changed, 4321 insertions, 0 deletions
diff --git a/platform/gfx/gl/src/mbgl/gl/attribute.cpp b/platform/gfx/gl/src/mbgl/gl/attribute.cpp
new file mode 100644
index 0000000000..d5240b5b7a
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/attribute.cpp
@@ -0,0 +1,19 @@
+#include <mbgl/gl/attribute.hpp>
+#include <mbgl/gl/context.hpp>
+
+namespace mbgl {
+namespace gl {
+
+using namespace platform;
+
+optional<AttributeLocation> queryLocation(ProgramID id, const char* name) {
+ GLint attributeLocation = MBGL_CHECK_ERROR(glGetAttribLocation(id, name));
+ if (attributeLocation != -1) {
+ return attributeLocation;
+ } else {
+ return {};
+ }
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/attribute.hpp b/platform/gfx/gl/src/mbgl/gl/attribute.hpp
new file mode 100644
index 0000000000..d578bdb361
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/attribute.hpp
@@ -0,0 +1,115 @@
+#pragma once
+
+#include <mbgl/gfx/attribute.hpp>
+#include <mbgl/gl/types.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/util/literal.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <vector>
+#include <string>
+
+namespace mbgl {
+namespace gl {
+
+using AttributeBindingArray = std::vector<optional<gfx::AttributeBinding>>;
+using NamedAttributeLocations = std::vector<std::pair<const std::string, AttributeLocation>>;
+
+optional<AttributeLocation> queryLocation(ProgramID id, const char* name);
+
+template <class>
+class AttributeLocations;
+
+template <class... As>
+class AttributeLocations<TypeList<As...>> final {
+private:
+ using Locations =
+ IndexedTuple<TypeList<As...>, TypeList<ExpandToType<As, optional<AttributeLocation>>...>>;
+
+ Locations locations;
+
+public:
+ AttributeLocations() = default;
+
+ void queryLocations(const ProgramID& id) {
+ locations = Locations{
+ queryLocation(id, concat_literals<&string_literal<'a', '_'>::value, &As::name>::value())... };
+ using TypeOfFirst = typename std::tuple_element<0, std::tuple<As...>>::type;
+ auto first = locations.template get<TypeOfFirst>();
+ assert(first && first.value() == 0);
+ }
+
+ static constexpr const char* getFirstAttribName() {
+ // Static assert that attribute list starts with position: we bind it on location 0.
+ using TypeOfFirst = typename std::tuple_element<0, std::tuple<As...>>::type;
+ static_assert(std::is_same<attributes::pos, TypeOfFirst>::value || std::is_same<attributes::pos_offset, TypeOfFirst>::value ||
+ std::is_same<attributes::pos_normal, TypeOfFirst>::value, "Program must start with position related attribute.");
+ return concat_literals<&string_literal<'a', '_'>::value, TypeOfFirst::name>::value();
+ }
+
+ NamedAttributeLocations getNamedLocations() const {
+ NamedAttributeLocations result;
+
+ auto maybeAddLocation = [&] (const std::string& name, const optional<AttributeLocation>& location) {
+ if (location) {
+ result.emplace_back(name, *location);
+ }
+ };
+
+ util::ignore({ (maybeAddLocation(concat_literals<&string_literal<'a', '_'>::value, &As::name>::value(),
+ locations.template get<As>()),
+ 0)... });
+
+ return result;
+ }
+
+ AttributeBindingArray toBindingArray(const gfx::AttributeBindings<TypeList<As...>>& bindings) const {
+ AttributeBindingArray result;
+ result.resize(sizeof...(As));
+
+ auto maybeAddBinding = [&] (const optional<AttributeLocation>& location,
+ const optional<gfx::AttributeBinding>& binding) {
+ if (location) {
+ result.at(*location) = binding;
+ }
+ };
+
+ util::ignore({ (maybeAddBinding(locations.template get<As>(), bindings.template get<As>()), 0)... });
+
+ return result;
+ }
+};
+
+template <class>
+class AttributeKey;
+
+constexpr auto attributeDefinePrefix() {
+ return "#define HAS_UNIFORM_u_";
+}
+
+template <class... As>
+class AttributeKey<TypeList<As...>> final {
+public:
+ static_assert(sizeof...(As) <= 32, "attribute count exceeds 32");
+
+ static uint32_t compute(const gfx::AttributeBindings<TypeList<As...>>& bindings) {
+ uint32_t value = 0;
+ util::ignore(
+ { (bindings.template get<As>() ? (void)(value |= 1 << TypeIndex<As, As...>::value)
+ : (void)0,
+ 0)... });
+ return value;
+ }
+
+ static std::string defines(const gfx::AttributeBindings<TypeList<As...>>& bindings) {
+ std::string result;
+ util::ignore({ (!bindings.template get<As>()
+ ? (void)(result += concat_literals<&attributeDefinePrefix, &As::name, &string_literal<'\n'>::value>::value())
+ : (void)0,
+ 0)... });
+ return result;
+ }
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/command_encoder.cpp b/platform/gfx/gl/src/mbgl/gl/command_encoder.cpp
new file mode 100644
index 0000000000..a7df14df09
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/command_encoder.cpp
@@ -0,0 +1,60 @@
+#include <mbgl/gl/command_encoder.hpp>
+#include <mbgl/gl/upload_pass.hpp>
+#include <mbgl/gl/render_pass.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/renderable_resource.hpp>
+#include <mbgl/gl/debugging_extension.hpp>
+#include <mbgl/platform/gl_functions.hpp>
+
+#include <cstring>
+
+namespace mbgl {
+namespace gl {
+
+CommandEncoder::~CommandEncoder() {
+ const auto debugGroup(createDebugGroup("cleanup"));
+ context.performCleanup();
+}
+
+std::unique_ptr<gfx::UploadPass>
+CommandEncoder::createUploadPass(const char* name) {
+ return std::make_unique<gl::UploadPass>(*this, name);
+}
+
+std::unique_ptr<gfx::RenderPass>
+CommandEncoder::createRenderPass(const char* name, const gfx::RenderPassDescriptor& descriptor) {
+ return std::make_unique<gl::RenderPass>(*this, name, descriptor);
+}
+
+void CommandEncoder::present(gfx::Renderable& renderable) {
+ renderable.getResource<gl::RenderableResource>().swap();
+}
+
+void CommandEncoder::pushDebugGroup(const char* name) {
+ (void)name;
+#ifndef NDEBUG
+ if (auto debugging = context.getDebuggingExtension()) {
+ if (debugging->pushDebugGroup) {
+ MBGL_CHECK_ERROR(debugging->pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0,
+ platform::GLsizei(strlen(name)), name));
+ } else if (debugging->pushGroupMarkerEXT) {
+ MBGL_CHECK_ERROR(debugging->pushGroupMarkerEXT(platform::GLsizei(strlen(name) + 1), name));
+ }
+ }
+#endif
+}
+
+void CommandEncoder::popDebugGroup() {
+#ifndef NDEBUG
+ if (auto debugging = context.getDebuggingExtension()) {
+ if (debugging->popDebugGroup) {
+ MBGL_CHECK_ERROR(debugging->popDebugGroup());
+ } else if (debugging->popGroupMarkerEXT) {
+ MBGL_CHECK_ERROR(debugging->popGroupMarkerEXT());
+ }
+ }
+#endif
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/command_encoder.hpp b/platform/gfx/gl/src/mbgl/gl/command_encoder.hpp
new file mode 100644
index 0000000000..f47aad4946
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/command_encoder.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <mbgl/gfx/command_encoder.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class Context;
+
+class CommandEncoder final : public gfx::CommandEncoder {
+public:
+ explicit CommandEncoder(gl::Context& context_) : context(context_) {
+ }
+
+ ~CommandEncoder() override;
+
+ friend class UploadPass;
+ friend class RenderPass;
+
+ std::unique_ptr<gfx::UploadPass> createUploadPass(const char* name) override;
+ std::unique_ptr<gfx::RenderPass> createRenderPass(const char* name, const gfx::RenderPassDescriptor&) override;
+ void present(gfx::Renderable&) override;
+
+private:
+ void pushDebugGroup(const char* name) override;
+ void popDebugGroup() override;
+
+public:
+ gl::Context& context;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/context.cpp b/platform/gfx/gl/src/mbgl/gl/context.cpp
new file mode 100644
index 0000000000..4d9c2055de
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/context.cpp
@@ -0,0 +1,755 @@
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/enum.hpp>
+#include <mbgl/gl/renderer_backend.hpp>
+#include <mbgl/gl/texture_resource.hpp>
+#include <mbgl/gl/renderbuffer_resource.hpp>
+#include <mbgl/gl/draw_scope_resource.hpp>
+#include <mbgl/gl/texture.hpp>
+#include <mbgl/gl/offscreen_texture.hpp>
+#include <mbgl/gl/command_encoder.hpp>
+#include <mbgl/gl/debugging_extension.hpp>
+#include <mbgl/gl/vertex_array_extension.hpp>
+#include <mbgl/util/traits.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/logging.hpp>
+
+#include <cstring>
+
+namespace mbgl {
+namespace gl {
+
+using namespace platform;
+
+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(std::is_same<ProgramID, GLuint>::value, "OpenGL type mismatch");
+static_assert(std::is_same<ShaderID, GLuint>::value, "OpenGL type mismatch");
+static_assert(std::is_same<BufferID, GLuint>::value, "OpenGL type mismatch");
+static_assert(std::is_same<TextureID, GLuint>::value, "OpenGL type mismatch");
+static_assert(std::is_same<VertexArrayID, GLuint>::value, "OpenGL type mismatch");
+static_assert(std::is_same<FramebufferID, GLuint>::value, "OpenGL type mismatch");
+static_assert(std::is_same<RenderbufferID, GLuint>::value, "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");
+
+Context::Context(RendererBackend& backend_)
+ : gfx::Context([] {
+ GLint value;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value));
+ return value;
+ }()), backend(backend_) {
+}
+
+Context::~Context() {
+ if (cleanupOnDestruction) {
+ 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;
+ };
+
+ static const std::string renderer = []() {
+ std::string r = reinterpret_cast<const char*>(MBGL_CHECK_ERROR(glGetString(GL_RENDERER)));
+ Log::Info(Event::General, "GPU Identifier: %s", r.c_str());
+ return r;
+ }();
+
+ // Block ANGLE on Direct3D since the debugging extension is causing crashes
+ if (!(renderer.find("ANGLE") != std::string::npos
+ && renderer.find("Direct3D") != std::string::npos)) {
+ debugging = std::make_unique<extension::Debugging>(fn);
+ }
+
+ // Block Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data
+ // Block ARM Mali-T720 (in some MT8163 chipsets) as it crashes on glBindVertexArray
+ // Block ANGLE on Direct3D as the combination of Qt + Windows + ANGLE leads to crashes
+ if (renderer.find("Adreno (TM) 2") == std::string::npos
+ && renderer.find("Adreno (TM) 3") == std::string::npos
+ && (!(renderer.find("ANGLE") != std::string::npos
+ && renderer.find("Direct3D") != std::string::npos))
+ && renderer.find("Mali-T720") == std::string::npos
+ && renderer.find("Sapphire 650") == std::string::npos
+ && !disableVAOExtension) {
+ vertexArray = std::make_unique<extension::VertexArray>(fn);
+ }
+
+#if MBGL_USE_GLES2
+ constexpr const char* halfFloatExtensionName = "OES_texture_half_float";
+ constexpr const char* halfFloatColorBufferExtensionName = "EXT_color_buffer_half_float";
+#else
+ constexpr const char* halfFloatExtensionName = "ARB_half_float_pixel";
+ constexpr const char* halfFloatColorBufferExtensionName = "ARB_color_buffer_float";
+#endif
+ if (strstr(extensions, halfFloatExtensionName) != nullptr &&
+ strstr(extensions, halfFloatColorBufferExtensionName) != nullptr) {
+
+ supportsHalfFloatTextures = true;
+ }
+
+ 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::initializer_list<const char*>& sources) {
+ UniqueShader result { MBGL_CHECK_ERROR(glCreateShader(static_cast<GLenum>(type))), { this } };
+
+ MBGL_CHECK_ERROR(glShaderSource(result, static_cast<GLsizei>(sources.size()), sources.begin(), nullptr));
+ MBGL_CHECK_ERROR(glCompileShader(result));
+
+ GLint status = 0;
+ MBGL_CHECK_ERROR(glGetShaderiv(result, GL_COMPILE_STATUS, &status));
+ if (status != 0) {
+ return result;
+ }
+
+ GLint logLength;
+ MBGL_CHECK_ERROR(glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength));
+ if (logLength > 0) {
+ const auto log = std::make_unique<GLchar[]>(logLength);
+ MBGL_CHECK_ERROR(glGetShaderInfoLog(result, logLength, &logLength, log.get()));
+ Log::Error(Event::Shader, "Shader failed to compile: %s", log.get());
+ }
+
+ throw std::runtime_error("shader failed to compile");
+}
+
+UniqueProgram Context::createProgram(ShaderID vertexShader, ShaderID fragmentShader, const char* location0AttribName) {
+ UniqueProgram result { MBGL_CHECK_ERROR(glCreateProgram()), { this } };
+
+ MBGL_CHECK_ERROR(glAttachShader(result, vertexShader));
+ MBGL_CHECK_ERROR(glAttachShader(result, fragmentShader));
+
+ // It is important to have attribute at position 0 enabled: conveniently,
+ // position attribute is always first and always enabled. The integrity of
+ // this assumption is verified in AttributeLocations::queryLocations and
+ // AttributeLocations::getFirstAttribName.
+ MBGL_CHECK_ERROR(glBindAttribLocation(result, 0, location0AttribName));
+
+ linkProgram(result);
+
+ return result;
+}
+
+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) {
+ return;
+ }
+
+ GLint logLength;
+ MBGL_CHECK_ERROR(glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &logLength));
+ const auto log = std::make_unique<GLchar[]>(logLength);
+ if (logLength > 0) {
+ MBGL_CHECK_ERROR(glGetProgramInfoLog(program_, logLength, &logLength, log.get()));
+ Log::Error(Event::Shader, "Program failed to link: %s", log.get());
+ }
+
+ throw std::runtime_error("program failed to link");
+}
+
+UniqueTexture Context::createUniqueTexture() {
+ if (pooledTextures.empty()) {
+ pooledTextures.resize(TextureMax);
+ MBGL_CHECK_ERROR(glGenTextures(TextureMax, pooledTextures.data()));
+ }
+
+ TextureID id = pooledTextures.back();
+ pooledTextures.pop_back();
+ return UniqueTexture{ std::move(id), { this } };
+}
+
+bool Context::supportsVertexArrays() const {
+ return vertexArray &&
+ vertexArray->genVertexArrays &&
+ vertexArray->bindVertexArray &&
+ vertexArray->deleteVertexArrays;
+}
+
+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)), 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));
+ return UniqueFramebuffer{ std::move(id), { this } };
+}
+
+std::unique_ptr<gfx::TextureResource> Context::createTextureResource(
+ const Size size, const gfx::TexturePixelType format, const gfx::TextureChannelDataType type) {
+ auto obj = createUniqueTexture();
+ std::unique_ptr<gfx::TextureResource> resource =
+ std::make_unique<gl::TextureResource>(std::move(obj));
+
+ // Always use texture unit 0 for manipulating it.
+ activeTextureUnit = 0;
+ texture[0] = static_cast<gl::TextureResource&>(*resource).texture;
+
+ // Creates an empty texture with the specified size and format.
+ MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, Enum<gfx::TexturePixelType>::to(format),
+ size.width, size.height, 0,
+ Enum<gfx::TexturePixelType>::to(format),
+ Enum<gfx::TextureChannelDataType>::to(type), nullptr));
+
+ // 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.
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+
+ return resource;
+}
+
+std::unique_ptr<gfx::RenderbufferResource>
+Context::createRenderbufferResource(const gfx::RenderbufferPixelType type, const Size size) {
+ RenderbufferID id = 0;
+ MBGL_CHECK_ERROR(glGenRenderbuffers(1, &id));
+ UniqueRenderbuffer renderbuffer{ std::move(id), { this } };
+
+ bindRenderbuffer = renderbuffer;
+ MBGL_CHECK_ERROR(
+ glRenderbufferStorage(GL_RENDERBUFFER, Enum<gfx::RenderbufferPixelType>::to(type), size.width, size.height));
+ bindRenderbuffer = 0;
+ return std::make_unique<gl::RenderbufferResource>(std::move(renderbuffer));
+}
+
+
+std::unique_ptr<uint8_t[]> Context::readFramebuffer(const Size size, const gfx::TexturePixelType format, const bool flip) {
+ const size_t stride = size.width * (format == gfx::TexturePixelType::RGBA ? 4 : 1);
+ auto data = std::make_unique<uint8_t[]>(stride * size.height);
+
+ // 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 };
+
+ MBGL_CHECK_ERROR(glReadPixels(0, 0, size.width, size.height,
+ Enum<gfx::TexturePixelType>::to(format), GL_UNSIGNED_BYTE,
+ data.get()));
+
+ if (flip) {
+ auto tmp = std::make_unique<uint8_t[]>(stride);
+ uint8_t* rgba = data.get();
+ for (int i = 0, j = size.height - 1; i < j; i++, j--) {
+ std::memcpy(tmp.get(), rgba + i * stride, stride);
+ std::memcpy(rgba + i * stride, rgba + j * stride, stride);
+ std::memcpy(rgba + j * stride, tmp.get(), stride);
+ }
+ }
+
+ return data;
+}
+
+#if not MBGL_USE_GLES2
+void Context::drawPixels(const Size size, const void* data, gfx::TexturePixelType format) {
+ pixelStoreUnpack = { 1 };
+ // TODO
+ if (format != gfx::TexturePixelType::RGBA) {
+ format = gfx::TexturePixelType::Luminance;
+ }
+ MBGL_CHECK_ERROR(glDrawPixels(size.width, size.height, Enum<gfx::TexturePixelType>::to(format),
+ GL_UNSIGNED_BYTE, data));
+}
+#endif // MBGL_USE_GLES2
+
+namespace {
+
+void checkFramebuffer() {
+ GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ switch (status) {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ throw std::runtime_error("Couldn't create framebuffer: incomplete attachment");
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ throw std::runtime_error("Couldn't create framebuffer: incomplete missing attachment");
+#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
+ throw std::runtime_error("Couldn't create framebuffer: incomplete draw buffer");
+#endif
+#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
+ throw std::runtime_error("Couldn't create framebuffer: incomplete read buffer");
+#endif
+#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ throw std::runtime_error("Couldn't create framebuffer: incomplete dimensions");
+#endif
+
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ throw std::runtime_error("Couldn't create framebuffer: unsupported");
+ default:
+ throw std::runtime_error("Couldn't create framebuffer: other");
+ }
+ }
+}
+
+void bindDepthStencilRenderbuffer(
+ const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>& depthStencil) {
+ auto& depthStencilResource = depthStencil.getResource<gl::RenderbufferResource>();
+#ifdef GL_DEPTH_STENCIL_ATTACHMENT
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depthStencilResource.renderbuffer));
+#else
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
+ depthStencilResource.renderbuffer));
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depthStencilResource.renderbuffer));
+#endif
+}
+
+} // namespace
+
+Framebuffer
+Context::createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>& color,
+ const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>& depthStencil) {
+ if (color.getSize() != depthStencil.getSize()) {
+ throw std::runtime_error("Renderbuffer size mismatch");
+ }
+ auto fbo = createFramebuffer();
+ bindFramebuffer = fbo;
+
+ auto& colorResource = color.getResource<gl::RenderbufferResource>();
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, colorResource.renderbuffer));
+ bindDepthStencilRenderbuffer(depthStencil);
+ checkFramebuffer();
+ return { color.getSize(), std::move(fbo) };
+}
+
+Framebuffer Context::createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>& color) {
+ auto fbo = createFramebuffer();
+ bindFramebuffer = fbo;
+ auto& colorResource = color.getResource<gl::RenderbufferResource>();
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, colorResource.renderbuffer));
+ checkFramebuffer();
+ return { color.getSize(), std::move(fbo) };
+}
+
+Framebuffer
+Context::createFramebuffer(const gfx::Texture& color,
+ const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>& depthStencil) {
+ if (color.size != depthStencil.getSize()) {
+ throw std::runtime_error("Renderbuffer size mismatch");
+ }
+ auto fbo = createFramebuffer();
+ bindFramebuffer = fbo;
+ MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ color.getResource<gl::TextureResource>().texture, 0));
+ bindDepthStencilRenderbuffer(depthStencil);
+ checkFramebuffer();
+ return { color.size, std::move(fbo) };
+}
+
+Framebuffer Context::createFramebuffer(const gfx::Texture& color) {
+ auto fbo = createFramebuffer();
+ bindFramebuffer = fbo;
+ MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ color.getResource<gl::TextureResource>().texture, 0));
+ checkFramebuffer();
+ return { color.size, std::move(fbo) };
+}
+
+Framebuffer
+Context::createFramebuffer(const gfx::Texture& color,
+ const gfx::Renderbuffer<gfx::RenderbufferPixelType::Depth>& depth) {
+ if (color.size != depth.getSize()) {
+ throw std::runtime_error("Renderbuffer size mismatch");
+ }
+ auto fbo = createFramebuffer();
+ bindFramebuffer = fbo;
+ MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ color.getResource<gl::TextureResource>().texture, 0));
+
+ auto& depthResource = depth.getResource<gl::RenderbufferResource>();
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
+ depthResource.renderbuffer));
+ checkFramebuffer();
+ return { depth.getSize(), std::move(fbo) };
+}
+
+std::unique_ptr<gfx::OffscreenTexture>
+Context::createOffscreenTexture(const Size size, const gfx::TextureChannelDataType type) {
+ return std::make_unique<gl::OffscreenTexture>(*this, size, type);
+}
+
+std::unique_ptr<gfx::DrawScopeResource> Context::createDrawScopeResource() {
+ return std::make_unique<gl::DrawScopeResource>(createVertexArray());
+}
+
+void Context::reset() {
+ std::copy(pooledTextures.begin(), pooledTextures.end(), std::back_inserter(abandonedTextures));
+ pooledTextures.resize(0);
+ performCleanup();
+}
+
+void Context::setDirtyState() {
+ // Note: does not set viewport/scissorTest/bindFramebuffer to dirty
+ // since they are handled separately in the view object.
+ stencilFunc.setDirty();
+ stencilMask.setDirty();
+ stencilTest.setDirty();
+ stencilOp.setDirty();
+ depthRange.setDirty();
+ depthMask.setDirty();
+ depthTest.setDirty();
+ depthFunc.setDirty();
+ blend.setDirty();
+ blendEquation.setDirty();
+ blendFunc.setDirty();
+ blendColor.setDirty();
+ colorMask.setDirty();
+ clearDepth.setDirty();
+ clearColor.setDirty();
+ clearStencil.setDirty();
+ cullFace.setDirty();
+ cullFaceSide.setDirty();
+ cullFaceWinding.setDirty();
+ program.setDirty();
+ lineWidth.setDirty();
+ activeTextureUnit.setDirty();
+ pixelStorePack.setDirty();
+ pixelStoreUnpack.setDirty();
+#if not MBGL_USE_GLES2
+ pointSize.setDirty();
+ pixelZoom.setDirty();
+ rasterPos.setDirty();
+ pixelTransferDepth.setDirty();
+ pixelTransferStencil.setDirty();
+#endif // MBGL_USE_GLES2
+ for (auto& tex : texture) {
+ tex.setDirty();
+ }
+ vertexBuffer.setDirty();
+ bindVertexArray.setDirty();
+ globalVertexArrayState.setDirty();
+}
+
+void Context::clear(optional<mbgl::Color> color,
+ optional<float> depth,
+ optional<int32_t> stencil) {
+ GLbitfield mask = 0;
+
+ if (color) {
+ mask |= GL_COLOR_BUFFER_BIT;
+ clearColor = *color;
+ colorMask = value::ColorMask::Default;
+ }
+
+ if (depth) {
+ mask |= GL_DEPTH_BUFFER_BIT;
+ clearDepth = *depth;
+ depthMask = value::DepthMask::Default;
+ }
+
+ if (stencil) {
+ mask |= GL_STENCIL_BUFFER_BIT;
+ clearStencil = *stencil;
+ stencilMask = value::StencilMask::Default;
+ }
+
+ MBGL_CHECK_ERROR(glClear(mask));
+}
+
+void Context::setCullFaceMode(const gfx::CullFaceMode& mode) {
+ cullFace = mode.enabled;
+
+ // These shouldn't need to be updated when face culling is disabled, but we
+ // might end up having the same isssues with Adreno 2xx GPUs as noted in
+ // Context::setDepthMode.
+ cullFaceSide = mode.side;
+ cullFaceWinding = mode.winding;
+}
+
+void Context::setDepthMode(const gfx::DepthMode& depth) {
+ if (depth.func == gfx::DepthFunctionType::Always && depth.mask != gfx::DepthMaskType::ReadWrite) {
+ 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;
+ depthMask = depth.mask;
+ depthRange = depth.range;
+ }
+}
+
+void Context::setStencilMode(const gfx::StencilMode& stencil) {
+ if (stencil.test.is<gfx::StencilMode::Always>() && !stencil.mask) {
+ stencilTest = false;
+ } else {
+ stencilTest = true;
+ stencilMask = stencil.mask;
+ stencilOp = { stencil.fail, stencil.depthFail, stencil.pass };
+ apply_visitor([&] (const auto& test) {
+ stencilFunc = { test.func, stencil.ref, test.mask };
+ }, stencil.test);
+ }
+}
+
+void Context::setColorMode(const gfx::ColorMode& color) {
+ if (color.blendFunction.is<gfx::ColorMode::Replace>()) {
+ blend = false;
+ } else {
+ blend = true;
+ blendColor = color.blendColor;
+ apply_visitor([&] (const auto& blendFunction) {
+ blendEquation = gfx::ColorBlendEquationType(blendFunction.equation);
+ blendFunc = { blendFunction.srcFactor, blendFunction.dstFactor };
+ }, color.blendFunction);
+ }
+
+ colorMask = color.mask;
+}
+
+std::unique_ptr<gfx::CommandEncoder> Context::createCommandEncoder() {
+ backend.updateAssumedState();
+ if (backend.contextIsShared()) {
+ setDirtyState();
+ }
+ return std::make_unique<gl::CommandEncoder>(*this);
+}
+
+void Context::draw(const gfx::DrawMode& drawMode,
+ std::size_t indexOffset,
+ std::size_t indexLength) {
+ switch (drawMode.type) {
+ case gfx::DrawModeType::Points:
+#if not MBGL_USE_GLES2
+ // In OpenGL ES 2, the point size is set in the vertex shader.
+ pointSize = drawMode.size;
+#endif // MBGL_USE_GLES2
+ break;
+ case gfx::DrawModeType::Lines:
+ case gfx::DrawModeType::LineLoop:
+ case gfx::DrawModeType::LineStrip:
+ lineWidth = drawMode.size;
+ break;
+ default:
+ break;
+ }
+
+ MBGL_CHECK_ERROR(glDrawElements(
+ Enum<gfx::DrawModeType>::to(drawMode.type),
+ static_cast<GLsizei>(indexLength),
+ GL_UNSIGNED_SHORT,
+ reinterpret_cast<GLvoid*>(sizeof(uint16_t) * indexOffset)));
+}
+
+void Context::performCleanup() {
+ // TODO: Find a better way to unbind VAOs after we're done with them without introducing
+ // unnecessary bind(0)/bind(N) sequences.
+ {
+ activeTextureUnit = 1;
+ texture[1] = 0;
+ activeTextureUnit = 0;
+ texture[0] = 0;
+
+ bindVertexArray = 0;
+ }
+
+ for (auto id : abandonedPrograms) {
+ if (program == id) {
+ program.setDirty();
+ }
+ MBGL_CHECK_ERROR(glDeleteProgram(id));
+ }
+ abandonedPrograms.clear();
+
+ for (auto id : abandonedShaders) {
+ MBGL_CHECK_ERROR(glDeleteShader(id));
+ }
+ abandonedShaders.clear();
+
+ if (!abandonedBuffers.empty()) {
+ for (const auto id : abandonedBuffers) {
+ if (vertexBuffer == id) {
+ vertexBuffer.setDirty();
+ } else if (globalVertexArrayState.indexBuffer == id) {
+ globalVertexArrayState.indexBuffer.setDirty();
+ }
+ }
+ MBGL_CHECK_ERROR(glDeleteBuffers(int(abandonedBuffers.size()), abandonedBuffers.data()));
+ abandonedBuffers.clear();
+ }
+
+ if (!abandonedTextures.empty()) {
+ for (const auto id : abandonedTextures) {
+ for (auto& binding : texture) {
+ if (binding == id) {
+ binding.setDirty();
+ }
+ }
+ }
+ MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data()));
+ abandonedTextures.clear();
+ }
+
+ if (!abandonedVertexArrays.empty()) {
+ assert(supportsVertexArrays());
+ for (const auto id : abandonedVertexArrays) {
+ if (bindVertexArray == id) {
+ bindVertexArray.setDirty();
+ }
+ }
+ MBGL_CHECK_ERROR(vertexArray->deleteVertexArrays(int(abandonedVertexArrays.size()),
+ abandonedVertexArrays.data()));
+ abandonedVertexArrays.clear();
+ }
+
+ if (!abandonedFramebuffers.empty()) {
+ for (const auto id : abandonedFramebuffers) {
+ if (bindFramebuffer == id) {
+ bindFramebuffer.setDirty();
+ }
+ }
+ MBGL_CHECK_ERROR(
+ glDeleteFramebuffers(int(abandonedFramebuffers.size()), abandonedFramebuffers.data()));
+ abandonedFramebuffers.clear();
+ }
+
+ if (!abandonedRenderbuffers.empty()) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffers(int(abandonedRenderbuffers.size()),
+ abandonedRenderbuffers.data()));
+ abandonedRenderbuffers.clear();
+ }
+}
+
+void Context::reduceMemoryUsage() {
+ performCleanup();
+
+ // Ensure that all pending actions are executed to ensure that they happen before the app goes
+ // to the background.
+ MBGL_CHECK_ERROR(glFinish());
+}
+
+#if not defined(NDEBUG)
+void Context::visualizeStencilBuffer() {
+#if not MBGL_USE_GLES2
+ setStencilMode(gfx::StencilMode::disabled());
+ setDepthMode(gfx::DepthMode::disabled());
+ setColorMode(gfx::ColorMode::unblended());
+ program = 0;
+
+ // Reset the value in case someone else changed it, or it's dirty.
+ pixelTransferStencil = gl::value::PixelTransferStencil::Default;
+
+ // Read the stencil buffer
+ const auto viewportValue = viewport.getCurrentValue();
+ auto image = readFramebuffer<AlphaImage, gfx::TexturePixelType::Stencil>(viewportValue.size, false);
+
+ // Scale the Stencil buffer to cover the entire color space.
+ auto it = image.data.get();
+ auto end = it + viewportValue.size.width * viewportValue.size.height;
+ const auto factor = 255.0f / *std::max_element(it, end);
+ for (; it != end; ++it) {
+ *it *= factor;
+ }
+
+ pixelZoom = { 1, 1 };
+ rasterPos = { -1, -1, 0, 1 };
+ drawPixels(image);
+#endif
+}
+
+void Context::visualizeDepthBuffer(const float depthRangeSize) {
+ (void)depthRangeSize;
+#if not MBGL_USE_GLES2
+ setStencilMode(gfx::StencilMode::disabled());
+ setDepthMode(gfx::DepthMode::disabled());
+ setColorMode(gfx::ColorMode::unblended());
+ program = 0;
+
+ // Scales the values in the depth buffer so that they cover the entire grayscale range. This
+ // makes it easier to spot tiny differences.
+ const float base = 1.0f / (1.0f - depthRangeSize);
+ pixelTransferDepth = { base, 1.0f - base };
+
+ // Read the stencil buffer
+ auto viewportValue = viewport.getCurrentValue();
+ auto image = readFramebuffer<AlphaImage, gfx::TexturePixelType::Depth>(viewportValue.size, false);
+
+ pixelZoom = { 1, 1 };
+ rasterPos = { -1, -1, 0, 1 };
+ drawPixels(image);
+#endif
+}
+
+#endif
+
+void Context::clearStencilBuffer(const int32_t bits) {
+ MBGL_CHECK_ERROR(glClearStencil(bits));
+ MBGL_CHECK_ERROR(glClear(GL_STENCIL_BUFFER_BIT));
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/context.hpp b/platform/gfx/gl/src/mbgl/gl/context.hpp
new file mode 100644
index 0000000000..70f12e5a8d
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/context.hpp
@@ -0,0 +1,235 @@
+#pragma once
+
+#include <mbgl/gfx/context.hpp>
+#include <mbgl/gl/object.hpp>
+#include <mbgl/gl/state.hpp>
+#include <mbgl/gl/value.hpp>
+#include <mbgl/gl/framebuffer.hpp>
+#include <mbgl/gl/vertex_array.hpp>
+#include <mbgl/gl/types.hpp>
+#include <mbgl/gfx/texture.hpp>
+#include <mbgl/gfx/draw_mode.hpp>
+#include <mbgl/gfx/depth_mode.hpp>
+#include <mbgl/gfx/stencil_mode.hpp>
+#include <mbgl/gfx/color_mode.hpp>
+#include <mbgl/platform/gl_functions.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+
+#include <functional>
+#include <memory>
+#include <vector>
+#include <array>
+#include <string>
+
+namespace mbgl {
+namespace gl {
+
+constexpr size_t TextureMax = 64;
+using ProcAddress = void (*)();
+class RendererBackend;
+
+namespace extension {
+class VertexArray;
+class Debugging;
+} // namespace extension
+
+class Context final : public gfx::Context {
+public:
+ Context(RendererBackend&);
+ ~Context() override;
+ Context(const Context&) = delete;
+ Context& operator=(const Context& other) = delete;
+
+ std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override;
+
+ void initializeExtensions(const std::function<gl::ProcAddress(const char*)>&);
+
+ void enableDebugging();
+
+ UniqueShader createShader(ShaderType type, const std::initializer_list<const char*>& sources);
+ UniqueProgram createProgram(ShaderID vertexShader, ShaderID fragmentShader, const char* location0AttribName);
+ void verifyProgramLinkage(ProgramID);
+ void linkProgram(ProgramID);
+ UniqueTexture createUniqueTexture();
+
+ Framebuffer createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>&,
+ const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>&);
+ Framebuffer createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>&);
+ Framebuffer createFramebuffer(const gfx::Texture&,
+ const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>&);
+ Framebuffer createFramebuffer(const gfx::Texture&);
+ Framebuffer createFramebuffer(const gfx::Texture&,
+ const gfx::Renderbuffer<gfx::RenderbufferPixelType::Depth>&);
+
+ template <typename Image,
+ gfx::TexturePixelType format = Image::channels == 4 ? gfx::TexturePixelType::RGBA
+ : gfx::TexturePixelType::Alpha>
+ Image readFramebuffer(const Size size, bool flip = true) {
+ static_assert(Image::channels == (format == gfx::TexturePixelType::RGBA ? 4 : 1),
+ "image format mismatch");
+ return { size, readFramebuffer(size, format, flip) };
+ }
+
+#if not MBGL_USE_GLES2
+ template <typename Image>
+ void drawPixels(const Image& image) {
+ auto format = image.channels == 4 ? gfx::TexturePixelType::RGBA : gfx::TexturePixelType::Alpha;
+ drawPixels(image.size, image.data.get(), format);
+ }
+#endif // MBGL_USE_GLES2
+
+ void clear(optional<mbgl::Color> color,
+ optional<float> depth,
+ optional<int32_t> stencil);
+
+ void setDepthMode(const gfx::DepthMode&);
+ void setStencilMode(const gfx::StencilMode&);
+ void setColorMode(const gfx::ColorMode&);
+ void setCullFaceMode(const gfx::CullFaceMode&);
+
+ void draw(const gfx::DrawMode&,
+ 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() override;
+
+ void reduceMemoryUsage() override;
+
+ // Drain pools and remove abandoned objects, in preparation for destroying the store.
+ // Only call this while the OpenGL context is exclusive to this thread.
+ void reset();
+
+ bool empty() const {
+ return pooledTextures.empty()
+ && abandonedPrograms.empty()
+ && abandonedShaders.empty()
+ && abandonedBuffers.empty()
+ && abandonedTextures.empty()
+ && abandonedVertexArrays.empty()
+ && abandonedFramebuffers.empty();
+ }
+
+ void setDirtyState();
+
+ extension::Debugging* getDebuggingExtension() const {
+ return debugging.get();
+ }
+
+ extension::VertexArray* getVertexArrayExtension() const {
+ return vertexArray.get();
+ }
+
+ void setCleanupOnDestruction(bool cleanup) {
+ cleanupOnDestruction = cleanup;
+ }
+
+private:
+ RendererBackend& backend;
+ bool cleanupOnDestruction = true;
+
+ std::unique_ptr<extension::Debugging> debugging;
+ std::unique_ptr<extension::VertexArray> vertexArray;
+
+public:
+ State<value::ActiveTextureUnit> activeTextureUnit;
+ State<value::BindFramebuffer> bindFramebuffer;
+ State<value::Viewport> viewport;
+ State<value::ScissorTest> scissorTest;
+ std::array<State<value::BindTexture>, 2> texture;
+ State<value::Program> program;
+ State<value::BindVertexBuffer> vertexBuffer;
+
+ State<value::BindVertexArray, const Context&> bindVertexArray { *this };
+ VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }) };
+
+ State<value::PixelStorePack> pixelStorePack;
+ State<value::PixelStoreUnpack> pixelStoreUnpack;
+
+#if not MBGL_USE_GLES2
+ State<value::PixelZoom> pixelZoom;
+ State<value::RasterPos> rasterPos;
+ State<value::PixelTransferDepth> pixelTransferDepth;
+ State<value::PixelTransferStencil> pixelTransferStencil;
+#endif // MBGL_USE_GLES2
+
+private:
+ State<value::StencilFunc> stencilFunc;
+ State<value::StencilMask> stencilMask;
+ State<value::StencilTest> stencilTest;
+ State<value::StencilOp> stencilOp;
+ State<value::DepthRange> depthRange;
+ State<value::DepthMask> depthMask;
+ State<value::DepthTest> depthTest;
+ State<value::DepthFunc> depthFunc;
+ State<value::Blend> blend;
+ State<value::BlendEquation> blendEquation;
+ State<value::BlendFunc> blendFunc;
+ State<value::BlendColor> blendColor;
+ State<value::ColorMask> colorMask;
+ State<value::ClearDepth> clearDepth;
+ State<value::ClearColor> clearColor;
+ State<value::ClearStencil> clearStencil;
+ State<value::LineWidth> lineWidth;
+ State<value::BindRenderbuffer> bindRenderbuffer;
+ State<value::CullFace> cullFace;
+ State<value::CullFaceSide> cullFaceSide;
+ State<value::CullFaceWinding> cullFaceWinding;
+#if not MBGL_USE_GLES2
+ State<value::PointSize> pointSize;
+#endif // MBGL_USE_GLES2
+
+ std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(
+ Size, gfx::TextureChannelDataType = gfx::TextureChannelDataType::UnsignedByte) override;
+
+ std::unique_ptr<gfx::TextureResource>
+ createTextureResource(Size, gfx::TexturePixelType, gfx::TextureChannelDataType) override;
+
+ std::unique_ptr<gfx::RenderbufferResource> createRenderbufferResource(gfx::RenderbufferPixelType, Size size) override;
+
+ std::unique_ptr<gfx::DrawScopeResource> createDrawScopeResource() override;
+
+ UniqueFramebuffer createFramebuffer();
+ std::unique_ptr<uint8_t[]> readFramebuffer(Size, gfx::TexturePixelType, bool flip);
+#if not MBGL_USE_GLES2
+ void drawPixels(Size size, const void* data, gfx::TexturePixelType);
+#endif // MBGL_USE_GLES2
+
+ VertexArray createVertexArray();
+ bool supportsVertexArrays() const;
+
+ friend detail::ProgramDeleter;
+ friend detail::ShaderDeleter;
+ friend detail::BufferDeleter;
+ friend detail::TextureDeleter;
+ friend detail::VertexArrayDeleter;
+ friend detail::FramebufferDeleter;
+ friend detail::RenderbufferDeleter;
+
+ std::vector<TextureID> pooledTextures;
+
+ std::vector<ProgramID> abandonedPrograms;
+ std::vector<ShaderID> abandonedShaders;
+ std::vector<BufferID> abandonedBuffers;
+ std::vector<TextureID> abandonedTextures;
+ std::vector<VertexArrayID> abandonedVertexArrays;
+ std::vector<FramebufferID> abandonedFramebuffers;
+ std::vector<RenderbufferID> abandonedRenderbuffers;
+
+public:
+ // For testing
+ bool disableVAOExtension = false;
+
+#if not defined(NDEBUG)
+public:
+ void visualizeStencilBuffer() override;
+ void visualizeDepthBuffer(float depthRangeSize) override;
+#endif
+
+ void clearStencilBuffer(int32_t) override;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/debugging_extension.cpp b/platform/gfx/gl/src/mbgl/gl/debugging_extension.cpp
new file mode 100644
index 0000000000..5afa4f50fc
--- /dev/null
+++ b/platform/gfx/gl/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/platform/gfx/gl/src/mbgl/gl/debugging_extension.hpp b/platform/gfx/gl/src/mbgl/gl/debugging_extension.hpp
new file mode 100644
index 0000000000..0fa5a7b424
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/debugging_extension.hpp
@@ -0,0 +1,122 @@
+#pragma once
+
+#include <mbgl/gl/extension.hpp>
+#include <mbgl/platform/gl_functions.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 {
+
+using namespace platform;
+
+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/platform/gfx/gl/src/mbgl/gl/defines.hpp b/platform/gfx/gl/src/mbgl/gl/defines.hpp
new file mode 100644
index 0000000000..75325dfb75
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/defines.hpp
@@ -0,0 +1,179 @@
+#pragma once
+
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_TEXTURE 0x84E0
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ALPHA 0x1906
+#define GL_ALWAYS 0x0207
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ARRAY_BUFFER_BINDING 0x8894
+#define GL_BACK 0x0405
+#define GL_BLEND 0x0BE2
+#define GL_BLEND_COLOR 0x8005
+#define GL_BLEND_DST_ALPHA 0x80CA
+#define GL_BLEND_EQUATION_RGB 0x8009
+#define GL_BLEND_SRC_ALPHA 0x80CB
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_BYTE 0x1400
+#define GL_CCW 0x0901
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#define GL_COLOR_BUFFER_BIT 0x00004000
+#define GL_COLOR_CLEAR_VALUE 0x0C22
+#define GL_COLOR_WRITEMASK 0x0C23
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_CULL_FACE 0x0B44
+#define GL_CULL_FACE_MODE 0x0B45
+#define GL_CURRENT_PROGRAM 0x8B8D
+#define GL_CW 0x0900
+#define GL_DECR 0x1E03
+#define GL_DECR_WRAP 0x8508
+#define GL_DEPTH24_STENCIL8_OES 0x88F0
+#define GL_DEPTH_ATTACHMENT 0x8D00
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_DEPTH_CLEAR_VALUE 0x0B73
+#define GL_DEPTH_COMPONENT 0x1902
+#define GL_DEPTH_COMPONENT16 0x81A5
+#define GL_DEPTH_FUNC 0x0B74
+#define GL_DEPTH_RANGE 0x0B70
+#define GL_DEPTH_TEST 0x0B71
+#define GL_DEPTH_WRITEMASK 0x0B72
+#define GL_DONT_CARE 0x1100
+#define GL_DST_ALPHA 0x0304
+#define GL_DST_COLOR 0x0306
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+#define GL_EQUAL 0x0202
+#define GL_EXTENSIONS 0x1F03
+#define GL_FALSE 0
+#define GL_FLOAT 0x1406
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_FRAMEBUFFER 0x8D40
+#define GL_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+#define GL_FRONT 0x0404
+#define GL_FRONT_AND_BACK 0x0408
+#define GL_FRONT_FACE 0x0B46
+#define GL_FUNC_ADD 0x8006
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_GEQUAL 0x0206
+#define GL_GREATER 0x0204
+#define GL_INCR 0x1E02
+#define GL_INCR_WRAP 0x8507
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_INT 0x1404
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_INVALID_ENUM 0x0500
+#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506
+#define GL_INVALID_OPERATION 0x0502
+#define GL_INVALID_VALUE 0x0501
+#define GL_INVERT 0x150A
+#define GL_KEEP 0x1E00
+#define GL_LEQUAL 0x0203
+#define GL_LESS 0x0201
+#define GL_LINEAR 0x2601
+#define GL_LINEAR_MIPMAP_NEAREST 0x2701
+#define GL_LINE_LOOP 0x0002
+#define GL_LINES 0x0001
+#define GL_LINE_STRIP 0x0003
+#define GL_LINE_WIDTH 0x0B21
+#define GL_LINK_STATUS 0x8B82
+#define GL_LUMINANCE 0x1909
+#define GL_MAX_VERTEX_ATTRIBS 0x8869
+#define GL_NEAREST 0x2600
+#define GL_NEAREST_MIPMAP_NEAREST 0x2700
+#define GL_NEVER 0x0200
+#define GL_NO_ERROR 0
+#define GL_NOTEQUAL 0x0205
+#define GL_ONE 1
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_ONE_MINUS_DST_ALPHA 0x0305
+#define GL_ONE_MINUS_DST_COLOR 0x0307
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_ONE_MINUS_SRC_COLOR 0x0301
+#define GL_OUT_OF_MEMORY 0x0505
+#define GL_PACK_ALIGNMENT 0x0D05
+#define GL_POINTS 0x0000
+#define GL_RENDERBUFFER 0x8D41
+#define GL_RENDERBUFFER_BINDING 0x8CA7
+#define GL_RENDERER 0x1F01
+#define GL_REPEAT 0x2901
+#define GL_REPLACE 0x1E01
+#define GL_RGBA 0x1908
+#define GL_RGBA8_OES 0x8058
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_SCISSOR_TEST 0x0C11
+#define GL_SHORT 0x1402
+#define GL_SRC_ALPHA 0x0302
+#define GL_SRC_ALPHA_SATURATE 0x0308
+#define GL_SRC_COLOR 0x0300
+#define GL_STATIC_DRAW 0x88E4
+#define GL_STENCIL_ATTACHMENT 0x8D20
+#define GL_STENCIL_BUFFER_BIT 0x00000400
+#define GL_STENCIL_CLEAR_VALUE 0x0B91
+#define GL_STENCIL_FAIL 0x0B94
+#define GL_STENCIL_FUNC 0x0B92
+#define GL_STENCIL_INDEX 0x1901
+#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
+#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96
+#define GL_STENCIL_REF 0x0B97
+#define GL_STENCIL_TEST 0x0B90
+#define GL_STENCIL_VALUE_MASK 0x0B93
+#define GL_STENCIL_WRITEMASK 0x0B98
+#define GL_STREAM_DRAW 0x88E0
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_TEXTURE_BINDING_2D 0x8069
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+#define GL_TRIANGLE_FAN 0x0006
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_TRUE 1
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_UNSIGNED_INT 0x1405
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_VIEWPORT 0x0BA2
+#define GL_ZERO 0
+
+#ifdef MBGL_USE_GLES2
+#define GL_HALF_FLOAT 0x8D61
+#else
+#define GL_CURRENT_RASTER_POSITION 0x0B07
+#define GL_DEPTH24_STENCIL8 0x88F0
+#define GL_DEPTH_BIAS 0x0D1F
+#define GL_DEPTH_SCALE 0x0D1E
+#define GL_HALF_FLOAT 0x140B
+#define GL_INDEX_OFFSET 0x0D13
+#define GL_INDEX_SHIFT 0x0D12
+#define GL_POINT_SIZE 0x0B11
+#define GL_RGBA8 0x8058
+#define GL_ZOOM_X 0x0D16
+#define GL_ZOOM_Y 0x0D17
+#endif \ No newline at end of file
diff --git a/platform/gfx/gl/src/mbgl/gl/draw_scope_resource.hpp b/platform/gfx/gl/src/mbgl/gl/draw_scope_resource.hpp
new file mode 100644
index 0000000000..4c446cd4fe
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/draw_scope_resource.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mbgl/gfx/draw_scope.hpp>
+#include <mbgl/gl/vertex_array.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class DrawScopeResource : public gfx::DrawScopeResource {
+public:
+ DrawScopeResource(VertexArray&& vertexArray_) : vertexArray(std::move(vertexArray_)) {
+ }
+
+ VertexArray vertexArray;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/enum.cpp b/platform/gfx/gl/src/mbgl/gl/enum.cpp
new file mode 100644
index 0000000000..5f8cd77ad6
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/enum.cpp
@@ -0,0 +1,319 @@
+#include <mbgl/gl/enum.hpp>
+#include <mbgl/gfx/types.hpp>
+#include <mbgl/gl/defines.hpp>
+
+namespace mbgl {
+namespace gl {
+
+template <>
+platform::GLenum Enum<gfx::DrawModeType>::to(const gfx::DrawModeType value) {
+ switch (value) {
+ case gfx::DrawModeType::Points: return GL_POINTS;
+ case gfx::DrawModeType::Lines: return GL_LINES;
+ case gfx::DrawModeType::LineLoop: return GL_LINE_LOOP;
+ case gfx::DrawModeType::LineStrip: return GL_LINE_STRIP;
+ case gfx::DrawModeType::Triangles: return GL_TRIANGLES;
+ case gfx::DrawModeType::TriangleStrip: return GL_TRIANGLE_STRIP;
+ case gfx::DrawModeType::TriangleFan: return GL_TRIANGLE_FAN;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::ColorBlendEquationType Enum<gfx::ColorBlendEquationType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_FUNC_ADD: return gfx::ColorBlendEquationType::Add;
+ case GL_FUNC_SUBTRACT: return gfx::ColorBlendEquationType::Subtract;
+ case GL_FUNC_REVERSE_SUBTRACT: return gfx::ColorBlendEquationType::ReverseSubtract;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::ColorBlendEquationType>::to(const gfx::ColorBlendEquationType value) {
+ switch (value) {
+ case gfx::ColorBlendEquationType::Add: return GL_FUNC_ADD;
+ case gfx::ColorBlendEquationType::Subtract: return GL_FUNC_SUBTRACT;
+ case gfx::ColorBlendEquationType::ReverseSubtract: return GL_FUNC_REVERSE_SUBTRACT;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::ColorBlendFactorType Enum<gfx::ColorBlendFactorType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_ZERO: return gfx::ColorBlendFactorType::Zero;
+ case GL_ONE: return gfx::ColorBlendFactorType::One;
+ case GL_SRC_COLOR: return gfx::ColorBlendFactorType::SrcColor;
+ case GL_ONE_MINUS_SRC_COLOR: return gfx::ColorBlendFactorType::OneMinusSrcColor;
+ case GL_DST_COLOR: return gfx::ColorBlendFactorType::DstColor;
+ case GL_ONE_MINUS_DST_COLOR: return gfx::ColorBlendFactorType::OneMinusDstColor;
+ case GL_SRC_ALPHA: return gfx::ColorBlendFactorType::SrcAlpha;
+ case GL_ONE_MINUS_SRC_ALPHA: return gfx::ColorBlendFactorType::OneMinusSrcAlpha;
+ case GL_DST_ALPHA: return gfx::ColorBlendFactorType::DstAlpha;
+ case GL_ONE_MINUS_DST_ALPHA: return gfx::ColorBlendFactorType::OneMinusDstAlpha;
+ case GL_CONSTANT_COLOR: return gfx::ColorBlendFactorType::ConstantColor;
+ case GL_ONE_MINUS_CONSTANT_COLOR: return gfx::ColorBlendFactorType::OneMinusConstantColor;
+ case GL_CONSTANT_ALPHA: return gfx::ColorBlendFactorType::ConstantAlpha;
+ case GL_ONE_MINUS_CONSTANT_ALPHA: return gfx::ColorBlendFactorType::OneMinusConstantAlpha;
+ case GL_SRC_ALPHA_SATURATE: return gfx::ColorBlendFactorType::SrcAlphaSaturate;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::ColorBlendFactorType>::to(const gfx::ColorBlendFactorType value) {
+ switch (value) {
+ case gfx::ColorBlendFactorType::Zero: return GL_ZERO;
+ case gfx::ColorBlendFactorType::One: return GL_ONE;
+ case gfx::ColorBlendFactorType::SrcColor: return GL_SRC_COLOR;
+ case gfx::ColorBlendFactorType::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR;
+ case gfx::ColorBlendFactorType::DstColor: return GL_DST_COLOR;
+ case gfx::ColorBlendFactorType::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR;
+ case gfx::ColorBlendFactorType::SrcAlpha: return GL_SRC_ALPHA;
+ case gfx::ColorBlendFactorType::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA;
+ case gfx::ColorBlendFactorType::DstAlpha: return GL_DST_ALPHA;
+ case gfx::ColorBlendFactorType::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA;
+ case gfx::ColorBlendFactorType::ConstantColor: return GL_CONSTANT_COLOR;
+ case gfx::ColorBlendFactorType::OneMinusConstantColor: return GL_ONE_MINUS_CONSTANT_COLOR;
+ case gfx::ColorBlendFactorType::ConstantAlpha: return GL_CONSTANT_ALPHA;
+ case gfx::ColorBlendFactorType::OneMinusConstantAlpha: return GL_ONE_MINUS_CONSTANT_ALPHA;
+ case gfx::ColorBlendFactorType::SrcAlphaSaturate: return GL_SRC_ALPHA_SATURATE;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::DepthFunctionType Enum<gfx::DepthFunctionType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_NEVER: return gfx::DepthFunctionType::Never;
+ case GL_LESS: return gfx::DepthFunctionType::Less;
+ case GL_EQUAL: return gfx::DepthFunctionType::Equal;
+ case GL_LEQUAL: return gfx::DepthFunctionType::LessEqual;
+ case GL_GREATER: return gfx::DepthFunctionType::Greater;
+ case GL_NOTEQUAL: return gfx::DepthFunctionType::NotEqual;
+ case GL_GEQUAL: return gfx::DepthFunctionType::GreaterEqual;
+ case GL_ALWAYS: return gfx::DepthFunctionType::Always;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::DepthFunctionType>::to(const gfx::DepthFunctionType value) {
+ switch (value) {
+ case gfx::DepthFunctionType::Never: return GL_NEVER;
+ case gfx::DepthFunctionType::Less: return GL_LESS;
+ case gfx::DepthFunctionType::Equal: return GL_EQUAL;
+ case gfx::DepthFunctionType::LessEqual: return GL_LEQUAL;
+ case gfx::DepthFunctionType::Greater: return GL_GREATER;
+ case gfx::DepthFunctionType::NotEqual: return GL_NOTEQUAL;
+ case gfx::DepthFunctionType::GreaterEqual: return GL_GEQUAL;
+ case gfx::DepthFunctionType::Always: return GL_ALWAYS;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::DepthMaskType Enum<gfx::DepthMaskType>::from(const platform::GLboolean value) {
+ return value ? gfx::DepthMaskType::ReadWrite : gfx::DepthMaskType::ReadOnly;
+}
+
+template <>
+platform::GLboolean Enum<gfx::DepthMaskType>::to(const gfx::DepthMaskType value) {
+ return value == gfx::DepthMaskType::ReadWrite ? GL_TRUE : GL_FALSE;
+}
+
+template <>
+gfx::StencilFunctionType Enum<gfx::StencilFunctionType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_NEVER: return gfx::StencilFunctionType::Never;
+ case GL_LESS: return gfx::StencilFunctionType::Less;
+ case GL_EQUAL: return gfx::StencilFunctionType::Equal;
+ case GL_LEQUAL: return gfx::StencilFunctionType::LessEqual;
+ case GL_GREATER: return gfx::StencilFunctionType::Greater;
+ case GL_NOTEQUAL: return gfx::StencilFunctionType::NotEqual;
+ case GL_GEQUAL: return gfx::StencilFunctionType::GreaterEqual;
+ case GL_ALWAYS: return gfx::StencilFunctionType::Always;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::StencilFunctionType>::to(const gfx::StencilFunctionType value) {
+ switch (value) {
+ case gfx::StencilFunctionType::Never: return GL_NEVER;
+ case gfx::StencilFunctionType::Less: return GL_LESS;
+ case gfx::StencilFunctionType::Equal: return GL_EQUAL;
+ case gfx::StencilFunctionType::LessEqual: return GL_LEQUAL;
+ case gfx::StencilFunctionType::Greater: return GL_GREATER;
+ case gfx::StencilFunctionType::NotEqual: return GL_NOTEQUAL;
+ case gfx::StencilFunctionType::GreaterEqual: return GL_GEQUAL;
+ case gfx::StencilFunctionType::Always: return GL_ALWAYS;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::StencilOpType Enum<gfx::StencilOpType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_KEEP: return gfx::StencilOpType::Keep;
+ case GL_ZERO: return gfx::StencilOpType::Zero;
+ case GL_REPLACE: return gfx::StencilOpType::Replace;
+ case GL_INCR: return gfx::StencilOpType::Increment;
+ case GL_INCR_WRAP: return gfx::StencilOpType::IncrementWrap;
+ case GL_DECR: return gfx::StencilOpType::Decrement;
+ case GL_DECR_WRAP: return gfx::StencilOpType::DecrementWrap;
+ case GL_INVERT: return gfx::StencilOpType::Invert;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::StencilOpType>::to(const gfx::StencilOpType value) {
+ switch (value) {
+ case gfx::StencilOpType::Keep: return GL_KEEP;
+ case gfx::StencilOpType::Zero: return GL_ZERO;
+ case gfx::StencilOpType::Replace: return GL_REPLACE;
+ case gfx::StencilOpType::Increment: return GL_INCR;
+ case gfx::StencilOpType::IncrementWrap: return GL_INCR_WRAP;
+ case gfx::StencilOpType::Decrement: return GL_DECR;
+ case gfx::StencilOpType::DecrementWrap: return GL_DECR_WRAP;
+ case gfx::StencilOpType::Invert: return GL_INVERT;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::CullFaceSideType Enum<gfx::CullFaceSideType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_FRONT: return gfx::CullFaceSideType::Front;
+ case GL_BACK: return gfx::CullFaceSideType::Back;
+ case GL_FRONT_AND_BACK: return gfx::CullFaceSideType::FrontAndBack;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::CullFaceSideType>::to(const gfx::CullFaceSideType value) {
+ switch (value) {
+ case gfx::CullFaceSideType::Front: return GL_FRONT;
+ case gfx::CullFaceSideType::Back: return GL_BACK;
+ case gfx::CullFaceSideType::FrontAndBack: return GL_FRONT_AND_BACK;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::CullFaceWindingType Enum<gfx::CullFaceWindingType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_CW: return gfx::CullFaceWindingType::Clockwise;
+ case GL_CCW: return gfx::CullFaceWindingType::CounterClockwise;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::CullFaceWindingType>::to(const gfx::CullFaceWindingType value) {
+ switch (value) {
+ case gfx::CullFaceWindingType::Clockwise: return GL_CW;
+ case gfx::CullFaceWindingType::CounterClockwise: return GL_CCW;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::BufferUsageType Enum<gfx::BufferUsageType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_STREAM_DRAW: return gfx::BufferUsageType::StreamDraw;
+ case GL_STATIC_DRAW: return gfx::BufferUsageType::StaticDraw;
+ case GL_DYNAMIC_DRAW: return gfx::BufferUsageType::DynamicDraw;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::BufferUsageType>::to(const gfx::BufferUsageType value) {
+ switch (value) {
+ case gfx::BufferUsageType::StreamDraw: return GL_STREAM_DRAW;
+ case gfx::BufferUsageType::StaticDraw: return GL_STATIC_DRAW;
+ case gfx::BufferUsageType::DynamicDraw: return GL_DYNAMIC_DRAW;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::TexturePixelType Enum<gfx::TexturePixelType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_RGBA: return gfx::TexturePixelType::RGBA;
+ case GL_ALPHA: return gfx::TexturePixelType::Alpha;
+ case GL_STENCIL_INDEX: return gfx::TexturePixelType::Stencil;
+ case GL_DEPTH_COMPONENT: return gfx::TexturePixelType::Depth;
+ case GL_LUMINANCE: return gfx::TexturePixelType::Luminance;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::TexturePixelType>::to(const gfx::TexturePixelType value) {
+ switch (value) {
+ case gfx::TexturePixelType::RGBA: return GL_RGBA;
+ case gfx::TexturePixelType::Alpha: return GL_ALPHA;
+ case gfx::TexturePixelType::Stencil: return GL_STENCIL_INDEX;
+ case gfx::TexturePixelType::Depth: return GL_DEPTH_COMPONENT;
+ case gfx::TexturePixelType::Luminance: return GL_LUMINANCE;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::TextureChannelDataType Enum<gfx::TextureChannelDataType>::from(const platform::GLint value) {
+ switch (value) {
+ case GL_UNSIGNED_BYTE: return gfx::TextureChannelDataType::UnsignedByte;
+ case GL_HALF_FLOAT: return gfx::TextureChannelDataType::HalfFloat;
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::TextureChannelDataType>::to(const gfx::TextureChannelDataType value) {
+ switch (value) {
+ case gfx::TextureChannelDataType::UnsignedByte: return GL_UNSIGNED_BYTE;
+ case gfx::TextureChannelDataType::HalfFloat: return GL_HALF_FLOAT;
+ }
+ return GL_INVALID_ENUM;
+}
+
+template <>
+gfx::RenderbufferPixelType Enum<gfx::RenderbufferPixelType>::from(const platform::GLint value) {
+ switch (value) {
+#if not MBGL_USE_GLES2
+ case GL_RGBA8: return gfx::RenderbufferPixelType::RGBA;
+ case GL_DEPTH_COMPONENT: return gfx::RenderbufferPixelType::Depth;
+ case GL_DEPTH24_STENCIL8: return gfx::RenderbufferPixelType::DepthStencil;
+#else
+ case GL_RGBA8_OES: return gfx::RenderbufferPixelType::RGBA;
+ case GL_DEPTH_COMPONENT16: return gfx::RenderbufferPixelType::Depth;
+ case GL_DEPTH24_STENCIL8_OES: return gfx::RenderbufferPixelType::DepthStencil;
+#endif
+ }
+ return {};
+}
+
+template <>
+platform::GLenum Enum<gfx::RenderbufferPixelType>::to(const gfx::RenderbufferPixelType value) {
+ switch (value) {
+#if not MBGL_USE_GLES2
+ case gfx::RenderbufferPixelType::RGBA: return GL_RGBA8;
+ case gfx::RenderbufferPixelType::Depth: return GL_DEPTH_COMPONENT;
+ case gfx::RenderbufferPixelType::DepthStencil: return GL_DEPTH24_STENCIL8;
+#else
+ case gfx::RenderbufferPixelType::RGBA: return GL_RGBA8_OES;
+ case gfx::RenderbufferPixelType::Depth: return GL_DEPTH_COMPONENT16;
+ case gfx::RenderbufferPixelType::DepthStencil: return GL_DEPTH24_STENCIL8_OES;
+#endif
+ }
+ return GL_INVALID_ENUM;
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/enum.hpp b/platform/gfx/gl/src/mbgl/gl/enum.hpp
new file mode 100644
index 0000000000..6aa29efdb1
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/enum.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <mbgl/platform/gl_functions.hpp>
+
+#include <type_traits>
+
+namespace mbgl {
+namespace gl {
+
+template <typename T>
+class Enum {
+public:
+ using InType = std::conditional_t<std::is_same<std::underlying_type_t<T>, bool>::value, platform::GLboolean, platform::GLint>;
+ using OutType = std::conditional_t<std::is_same<std::underlying_type_t<T>, bool>::value, platform::GLboolean, platform::GLenum>;
+
+ static T from(const InType);
+ static OutType to(T);
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/extension.hpp b/platform/gfx/gl/src/mbgl/gl/extension.hpp
new file mode 100644
index 0000000000..8710314ef2
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/extension.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <initializer_list>
+#include <utility>
+#include <functional>
+
+namespace mbgl {
+namespace gl {
+
+using ProcAddress = void (*)();
+
+template <class>
+class ExtensionFunction;
+
+template <class R, class... Args>
+class ExtensionFunction<R(Args...)> {
+public:
+ ExtensionFunction(const ProcAddress ptr_) : ptr(ptr_) {
+ }
+
+ explicit operator bool() const {
+ return ptr;
+ }
+
+ R operator()(Args... args) const {
+ return (*reinterpret_cast<R (*)(Args...)>(ptr))(std::forward<Args>(args)...);
+ }
+
+private:
+ const ProcAddress ptr;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/framebuffer.hpp b/platform/gfx/gl/src/mbgl/gl/framebuffer.hpp
new file mode 100644
index 0000000000..91ed467b40
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/framebuffer.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <mbgl/gl/object.hpp>
+#include <mbgl/util/size.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class Framebuffer {
+public:
+ Size size;
+ gl::UniqueFramebuffer framebuffer;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/index_buffer_resource.hpp b/platform/gfx/gl/src/mbgl/gl/index_buffer_resource.hpp
new file mode 100644
index 0000000000..2da25fdb96
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/index_buffer_resource.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mbgl/gfx/index_buffer.hpp>
+#include <mbgl/gl/object.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class IndexBufferResource : public gfx::IndexBufferResource {
+public:
+ IndexBufferResource(UniqueBuffer&& buffer_) : buffer(std::move(buffer_)) {
+ }
+
+ UniqueBuffer buffer;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/object.cpp b/platform/gfx/gl/src/mbgl/gl/object.cpp
new file mode 100644
index 0000000000..ec2998a27d
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/object.cpp
@@ -0,0 +1,52 @@
+#include <mbgl/gl/object.hpp>
+#include <mbgl/gl/context.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+namespace gl {
+namespace detail {
+
+void ProgramDeleter::operator()(ProgramID id) const {
+ assert(context);
+ context->abandonedPrograms.push_back(id);
+}
+
+void ShaderDeleter::operator()(ShaderID id) const {
+ assert(context);
+ context->abandonedShaders.push_back(id);
+}
+
+void BufferDeleter::operator()(BufferID id) const {
+ context.abandonedBuffers.push_back(id);
+}
+
+void TextureDeleter::operator()(TextureID id) const {
+ assert(context);
+ if (context->pooledTextures.size() >= TextureMax) {
+ context->abandonedTextures.push_back(id);
+ } else {
+ context->pooledTextures.push_back(id);
+ }
+}
+
+void VertexArrayDeleter::operator()(VertexArrayID id) const {
+ assert(context);
+ if (id != 0) {
+ context->abandonedVertexArrays.push_back(id);
+ }
+}
+
+void FramebufferDeleter::operator()(FramebufferID id) const {
+ assert(context);
+ context->abandonedFramebuffers.push_back(id);
+}
+
+void RenderbufferDeleter::operator()(RenderbufferID id) const {
+ assert(context);
+ context->abandonedRenderbuffers.push_back(id);
+}
+
+} // namespace detail
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/object.hpp b/platform/gfx/gl/src/mbgl/gl/object.hpp
new file mode 100644
index 0000000000..9598e0c59e
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/object.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include <mbgl/gl/types.hpp>
+
+#include <unique_resource.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class Context;
+
+namespace detail {
+
+struct ProgramDeleter {
+ Context* context;
+ void operator()(ProgramID) const;
+};
+
+struct ShaderDeleter {
+ Context* context;
+ void operator()(ShaderID) const;
+};
+
+struct BufferDeleter {
+ Context& context;
+ void operator()(BufferID) const;
+};
+
+struct TextureDeleter {
+ Context* context;
+ void operator()(TextureID) const;
+};
+
+struct VertexArrayDeleter {
+ Context* context;
+ void operator()(VertexArrayID) const;
+};
+
+struct FramebufferDeleter {
+ Context* context;
+ void operator()(FramebufferID) const;
+};
+
+struct RenderbufferDeleter {
+ Context* context;
+ void operator()(RenderbufferID) const;
+};
+
+} // namespace detail
+
+using UniqueProgram = std_experimental::unique_resource<ProgramID, detail::ProgramDeleter>;
+using UniqueShader = std_experimental::unique_resource<ShaderID, detail::ShaderDeleter>;
+using UniqueBuffer = std_experimental::unique_resource<BufferID, detail::BufferDeleter>;
+using UniqueTexture = std_experimental::unique_resource<TextureID, detail::TextureDeleter>;
+using UniqueVertexArray = std_experimental::unique_resource<VertexArrayID, detail::VertexArrayDeleter>;
+using UniqueFramebuffer = std_experimental::unique_resource<FramebufferID, detail::FramebufferDeleter>;
+using UniqueRenderbuffer = std_experimental::unique_resource<RenderbufferID, detail::RenderbufferDeleter>;
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/offscreen_texture.cpp b/platform/gfx/gl/src/mbgl/gl/offscreen_texture.cpp
new file mode 100644
index 0000000000..92f80a87b4
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/offscreen_texture.cpp
@@ -0,0 +1,75 @@
+#include <mbgl/gl/offscreen_texture.hpp>
+#include <mbgl/gl/renderable_resource.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/framebuffer.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class OffscreenTextureResource final : public gl::RenderableResource {
+public:
+ OffscreenTextureResource(gl::Context& context_,
+ const Size size_,
+ const gfx::TextureChannelDataType type_)
+ : context(context_), size(size_), type(type_) {
+ assert(!size.isEmpty());
+ }
+
+ void bind() override {
+ if (!framebuffer) {
+ assert(!texture);
+ texture = context.createTexture(size, gfx::TexturePixelType::RGBA, type);
+ framebuffer = context.createFramebuffer(*texture);
+ } else {
+ context.bindFramebuffer = framebuffer->framebuffer;
+ }
+
+ context.activeTextureUnit = 0;
+ context.scissorTest = false;
+ context.viewport = { 0, 0, size };
+ }
+
+ PremultipliedImage readStillImage() {
+ assert(framebuffer);
+ context.bindFramebuffer = framebuffer->framebuffer;
+ return context.readFramebuffer<PremultipliedImage>(size);
+ }
+
+ gfx::Texture& getTexture() {
+ assert(texture);
+ return *texture;
+ }
+
+private:
+ gl::Context& context;
+ const Size size;
+ optional<gfx::Texture> texture;
+ const gfx::TextureChannelDataType type;
+ optional<gl::Framebuffer> framebuffer;
+};
+
+OffscreenTexture::OffscreenTexture(gl::Context& context,
+ const Size size_,
+ const gfx::TextureChannelDataType type)
+ : gfx::OffscreenTexture(size, std::make_unique<OffscreenTextureResource>(context, size_, type)) {
+}
+
+bool OffscreenTexture::isRenderable() {
+ try {
+ getResource<OffscreenTextureResource>().bind();
+ return true;
+ } catch (const std::runtime_error& ex) {
+ return false;
+ }
+}
+
+PremultipliedImage OffscreenTexture::readStillImage() {
+ return getResource<OffscreenTextureResource>().readStillImage();
+}
+
+gfx::Texture& OffscreenTexture::getTexture() {
+ return getResource<OffscreenTextureResource>().getTexture();
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/offscreen_texture.hpp b/platform/gfx/gl/src/mbgl/gl/offscreen_texture.hpp
new file mode 100644
index 0000000000..5f3863d3e9
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/offscreen_texture.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <mbgl/gfx/offscreen_texture.hpp>
+#include <mbgl/gfx/types.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class Context;
+
+class OffscreenTexture final : public gfx::OffscreenTexture {
+public:
+ OffscreenTexture(gl::Context&,
+ Size size,
+ gfx::TextureChannelDataType type = gfx::TextureChannelDataType::UnsignedByte);
+
+ bool isRenderable() override;
+
+ PremultipliedImage readStillImage() override;
+ gfx::Texture& getTexture() override;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/program.hpp b/platform/gfx/gl/src/mbgl/gl/program.hpp
new file mode 100644
index 0000000000..6cfe05bf54
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/program.hpp
@@ -0,0 +1,141 @@
+#pragma once
+
+#include <mbgl/gfx/program.hpp>
+#include <mbgl/gl/types.hpp>
+#include <mbgl/gl/object.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/draw_scope_resource.hpp>
+#include <mbgl/gfx/vertex_buffer.hpp>
+#include <mbgl/gfx/index_buffer.hpp>
+#include <mbgl/gfx/uniform.hpp>
+#include <mbgl/gl/vertex_array.hpp>
+#include <mbgl/gl/attribute.hpp>
+#include <mbgl/gl/uniform.hpp>
+#include <mbgl/gl/texture.hpp>
+#include <mbgl/util/io.hpp>
+
+#include <mbgl/util/logging.hpp>
+#include <mbgl/programs/program_parameters.hpp>
+#include <mbgl/programs/gl/shader_source.hpp>
+#include <mbgl/programs/gl/shaders.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace gl {
+
+template <class Name>
+class Program final : public gfx::Program<Name> {
+public:
+ using AttributeList = typename Name::AttributeList;
+ using UniformList = typename Name::UniformList;
+ using TextureList = typename Name::TextureList;
+
+ Program(ProgramParameters programParameters_)
+ : programParameters(std::move(programParameters_)) {
+ }
+
+ const ProgramParameters programParameters;
+
+ static constexpr const auto vertexOffset = programs::gl::ShaderSource<Name>::vertexOffset;
+ static constexpr const auto fragmentOffset = programs::gl::ShaderSource<Name>::fragmentOffset;
+
+ class Instance {
+ public:
+ Instance(Context& context,
+ const std::initializer_list<const char*>& vertexSource,
+ const std::initializer_list<const char*>& fragmentSource)
+ : program(context.createProgram(
+ context.createShader(ShaderType::Vertex, vertexSource),
+ context.createShader(ShaderType::Fragment, fragmentSource),
+ attributeLocations.getFirstAttribName())) {
+ attributeLocations.queryLocations(program);
+ uniformStates.queryLocations(program);
+ // Texture units are specified via uniforms as well, so we need query their locations
+ textureStates.queryLocations(program);
+ }
+
+ static std::unique_ptr<Instance>
+ createInstance(gl::Context& context,
+ const ProgramParameters& programParameters,
+ const std::string& additionalDefines) {
+ // Compile the shader
+ const std::initializer_list<const char*> vertexSource = {
+ programParameters.getDefines().c_str(),
+ additionalDefines.c_str(),
+ (programs::gl::shaderSource() + programs::gl::vertexPreludeOffset),
+ (programs::gl::shaderSource() + vertexOffset)
+ };
+ const std::initializer_list<const char*> fragmentSource = {
+ programParameters.getDefines().c_str(),
+ additionalDefines.c_str(),
+ (programs::gl::shaderSource() + programs::gl::fragmentPreludeOffset),
+ (programs::gl::shaderSource() + fragmentOffset)
+ };
+ auto result = std::make_unique<Instance>(context, vertexSource, fragmentSource);
+
+ return std::move(result);
+ }
+
+ UniqueProgram program;
+ gl::AttributeLocations<AttributeList> attributeLocations;
+ gl::UniformStates<UniformList> uniformStates;
+ gl::TextureStates<TextureList> textureStates;
+ };
+
+ void draw(gfx::Context& genericContext,
+ gfx::RenderPass&,
+ const gfx::DrawMode& drawMode,
+ const gfx::DepthMode& depthMode,
+ const gfx::StencilMode& stencilMode,
+ const gfx::ColorMode& colorMode,
+ const gfx::CullFaceMode& cullFaceMode,
+ const gfx::UniformValues<UniformList>& uniformValues,
+ gfx::DrawScope& drawScope,
+ const gfx::AttributeBindings<AttributeList>& attributeBindings,
+ const gfx::TextureBindings<TextureList>& textureBindings,
+ const gfx::IndexBuffer& indexBuffer,
+ std::size_t indexOffset,
+ std::size_t indexLength) override {
+ auto& context = static_cast<gl::Context&>(genericContext);
+
+ context.setDepthMode(depthMode);
+ context.setStencilMode(stencilMode);
+ context.setColorMode(colorMode);
+ context.setCullFaceMode(cullFaceMode);
+
+ const uint32_t key = gl::AttributeKey<AttributeList>::compute(attributeBindings);
+ auto it = instances.find(key);
+ if (it == instances.end()) {
+ it = instances
+ .emplace(key,
+ Instance::createInstance(
+ context,
+ programParameters,
+ gl::AttributeKey<AttributeList>::defines(attributeBindings)))
+ .first;
+ }
+
+ auto& instance = *it->second;
+ context.program = instance.program;
+
+ instance.uniformStates.bind(uniformValues);
+
+ instance.textureStates.bind(context, textureBindings);
+
+ auto& vertexArray = drawScope.getResource<gl::DrawScopeResource>().vertexArray;
+ vertexArray.bind(context,
+ indexBuffer,
+ instance.attributeLocations.toBindingArray(attributeBindings));
+
+ context.draw(drawMode,
+ indexOffset,
+ indexLength);
+ }
+
+private:
+ std::map<uint32_t, std::unique_ptr<Instance>> instances;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/render_pass.cpp b/platform/gfx/gl/src/mbgl/gl/render_pass.cpp
new file mode 100644
index 0000000000..b327f7954f
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/render_pass.cpp
@@ -0,0 +1,28 @@
+#include <mbgl/gl/render_pass.hpp>
+#include <mbgl/gl/command_encoder.hpp>
+#include <mbgl/gl/renderable_resource.hpp>
+#include <mbgl/gl/context.hpp>
+
+namespace mbgl {
+namespace gl {
+
+RenderPass::RenderPass(gl::CommandEncoder& commandEncoder_,
+ const char* name,
+ const gfx::RenderPassDescriptor& descriptor)
+ : commandEncoder(commandEncoder_), debugGroup(commandEncoder.createDebugGroup(name)) {
+ descriptor.renderable.getResource<gl::RenderableResource>().bind();
+ const auto clearDebugGroup(commandEncoder.createDebugGroup("clear"));
+ commandEncoder.context.clear(descriptor.clearColor, descriptor.clearDepth,
+ descriptor.clearStencil);
+}
+
+void RenderPass::pushDebugGroup(const char* name) {
+ commandEncoder.pushDebugGroup(name);
+}
+
+void RenderPass::popDebugGroup() {
+ commandEncoder.popDebugGroup();
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/render_pass.hpp b/platform/gfx/gl/src/mbgl/gl/render_pass.hpp
new file mode 100644
index 0000000000..85a56243a6
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/render_pass.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/gfx/render_pass.hpp>
+
+namespace mbgl {
+namespace gfx {
+
+class CommandEncoder;
+
+} // namespace gfx
+
+namespace gl {
+
+class CommandEncoder;
+class Context;
+
+class RenderPass final : public gfx::RenderPass {
+public:
+ RenderPass(gl::CommandEncoder&, const char* name, const gfx::RenderPassDescriptor&);
+
+private:
+ void pushDebugGroup(const char* name) override;
+ void popDebugGroup() override;
+
+private:
+ gl::CommandEncoder& commandEncoder;
+ const gfx::DebugGroup<gfx::CommandEncoder> debugGroup;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/renderbuffer_resource.hpp b/platform/gfx/gl/src/mbgl/gl/renderbuffer_resource.hpp
new file mode 100644
index 0000000000..52865b42f7
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/renderbuffer_resource.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <mbgl/gfx/renderbuffer.hpp>
+#include <mbgl/gl/object.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class RenderbufferResource final : public gfx::RenderbufferResource {
+public:
+ explicit RenderbufferResource(UniqueRenderbuffer renderbuffer_)
+ : renderbuffer(std::move(renderbuffer_)) {
+ }
+
+ UniqueRenderbuffer renderbuffer;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/renderer_backend.cpp b/platform/gfx/gl/src/mbgl/gl/renderer_backend.cpp
new file mode 100644
index 0000000000..fe0ca4b5b2
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/renderer_backend.cpp
@@ -0,0 +1,69 @@
+#include <mbgl/gl/renderer_backend.hpp>
+#include <mbgl/gfx/backend_scope.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/extension.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+namespace gl {
+
+RendererBackend::RendererBackend(const gfx::ContextMode contextMode_)
+ : gfx::RendererBackend(contextMode_) {
+}
+
+std::unique_ptr<gfx::Context> RendererBackend::createContext() {
+ auto result = std::make_unique<gl::Context>(*this);
+ result->enableDebugging();
+ result->initializeExtensions(
+ std::bind(&RendererBackend::getExtensionFunctionPointer, this, std::placeholders::_1));
+ // Needs move to placate GCC 4.9
+ return std::move(result);
+}
+
+PremultipliedImage RendererBackend::readFramebuffer(const Size& size) {
+ return getContext<gl::Context>().readFramebuffer<PremultipliedImage>(size);
+}
+
+void RendererBackend::assumeFramebufferBinding(const gl::FramebufferID fbo) {
+ getContext<gl::Context>().bindFramebuffer.setCurrentValue(fbo);
+ if (fbo != ImplicitFramebufferBinding) {
+ assert(gl::value::BindFramebuffer::Get() == getContext<gl::Context>().bindFramebuffer.getCurrentValue());
+ }
+}
+
+void RendererBackend::assumeViewport(int32_t x, int32_t y, const Size& size) {
+ getContext<gl::Context>().viewport.setCurrentValue({ x, y, size });
+ assert(gl::value::Viewport::Get() == getContext<gl::Context>().viewport.getCurrentValue());
+}
+
+void RendererBackend::assumeScissorTest(bool enabled) {
+ getContext<gl::Context>().scissorTest.setCurrentValue(enabled);
+ assert(gl::value::ScissorTest::Get() == getContext<gl::Context>().scissorTest.getCurrentValue());
+}
+
+bool RendererBackend::implicitFramebufferBound() {
+ return getContext<gl::Context>().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding;
+}
+
+void RendererBackend::setFramebufferBinding(const gl::FramebufferID fbo) {
+ getContext<gl::Context>().bindFramebuffer = fbo;
+ if (fbo != ImplicitFramebufferBinding) {
+ assert(gl::value::BindFramebuffer::Get() == getContext<gl::Context>().bindFramebuffer.getCurrentValue());
+ }
+}
+
+void RendererBackend::setViewport(int32_t x, int32_t y, const Size& size) {
+ getContext<gl::Context>().viewport = { x, y, size };
+ assert(gl::value::Viewport::Get() == getContext<gl::Context>().viewport.getCurrentValue());
+}
+
+void RendererBackend::setScissorTest(bool enabled) {
+ getContext<gl::Context>().scissorTest = enabled;
+ assert(gl::value::ScissorTest::Get() == getContext<gl::Context>().scissorTest.getCurrentValue());
+}
+
+RendererBackend::~RendererBackend() = default;
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/state.hpp b/platform/gfx/gl/src/mbgl/gl/state.hpp
new file mode 100644
index 0000000000..17503cc043
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/state.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <tuple>
+
+namespace mbgl {
+namespace gl {
+
+// Wraps a piece of OpenGL state and remember its value to avoid redundant state calls.
+// Wrapped types need to implement to the Value class interface:
+//
+// class Value {
+// using Type = ...;
+// static const constexpr Type Default = ...;
+// static void Set(const Type& value);
+// static Type Get();
+// };
+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);
+ set(std::index_sequence_for<Args...>{});
+ }
+ }
+
+ bool operator==(const typename T::Type& value) const {
+ return !(*this != value);
+ }
+
+ bool operator!=(const typename T::Type& value) const {
+ return dirty || currentValue != value;
+ }
+
+ void setCurrentValue(const typename T::Type& value) {
+ dirty = false;
+ currentValue = value;
+ }
+
+ // Mark the state as dirty. This means that the next time we are assigning a value to this
+ // piece of OpenGL state will always result in an actual OpenGL call.
+ void setDirty() {
+ dirty = true;
+ }
+
+ typename T::Type getCurrentValue() const {
+ return currentValue;
+ }
+
+ bool isDirty() const {
+ return dirty;
+ }
+
+private:
+ template <std::size_t... I>
+ void set(std::index_sequence<I...>) {
+ T::Set(currentValue, std::get<I>(params)...);
+ }
+
+private:
+ typename T::Type currentValue = T::Default;
+ bool dirty = true;
+ const std::tuple<Args...> params;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/texture.cpp b/platform/gfx/gl/src/mbgl/gl/texture.cpp
new file mode 100644
index 0000000000..ef4d083677
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/texture.cpp
@@ -0,0 +1,54 @@
+#include <mbgl/gl/texture.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/texture_resource.hpp>
+#include <mbgl/gl/defines.hpp>
+
+namespace mbgl {
+namespace gl {
+
+using namespace platform;
+
+void bindTexture(gl::Context& context, const uint8_t unit, const gfx::TextureBinding& binding) {
+ auto& resource = static_cast<gl::TextureResource&>(*binding.resource);
+ if (binding.filter != resource.filter || binding.mipmap != resource.mipmap ||
+ binding.wrapX != resource.wrapX || binding.wrapY != resource.wrapY) {
+ context.activeTextureUnit = unit;
+ context.texture[unit] = resource.texture;
+
+ if (binding.filter != resource.filter || binding.mipmap != resource.mipmap) {
+ MBGL_CHECK_ERROR(glTexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ binding.filter == gfx::TextureFilterType::Linear
+ ? (binding.mipmap == gfx::TextureMipMapType::Yes ? GL_LINEAR_MIPMAP_NEAREST
+ : GL_LINEAR)
+ : (binding.mipmap == gfx::TextureMipMapType::Yes ? GL_NEAREST_MIPMAP_NEAREST
+ : GL_NEAREST)));
+ MBGL_CHECK_ERROR(glTexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ binding.filter == gfx::TextureFilterType::Linear ? GL_LINEAR : GL_NEAREST));
+ resource.filter = binding.filter;
+ resource.mipmap = binding.mipmap;
+ }
+ if (binding.wrapX != resource.wrapX) {
+
+ MBGL_CHECK_ERROR(glTexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ binding.wrapX == gfx::TextureWrapType::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT));
+ resource.wrapX = binding.wrapX;
+ }
+ if (binding.wrapY != resource.wrapY) {
+ MBGL_CHECK_ERROR(glTexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+ binding.wrapY == gfx::TextureWrapType::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT));
+ resource.wrapY = binding.wrapY;
+ }
+ } else if (context.texture[unit] != resource.texture) {
+ // We are checking first to avoid setting the active texture without a subsequent
+ // texture bind.
+ context.activeTextureUnit = unit;
+ context.texture[unit] = resource.texture;
+ }
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/texture.hpp b/platform/gfx/gl/src/mbgl/gl/texture.hpp
new file mode 100644
index 0000000000..74a314d2a7
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/texture.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mbgl/gfx/texture.hpp>
+#include <mbgl/gl/uniform.hpp>
+#include <mbgl/util/literal.hpp>
+#include <mbgl/util/ignore.hpp>
+
+#include <vector>
+#include <string>
+
+namespace mbgl {
+namespace gl {
+
+class Context;
+
+void bindTexture(gl::Context&, uint8_t unit, const gfx::TextureBinding&);
+
+template <class>
+class TextureStates;
+
+template <class... Ts>
+class TextureStates<TypeList<Ts...>> {
+private:
+ using State =
+ IndexedTuple<TypeList<Ts...>, TypeList<ExpandToType<Ts, gl::UniformState<uint8_t>>...>>;
+
+ State state;
+
+public:
+ void queryLocations(const ProgramID& id) {
+ state = State{ gl::uniformLocation(id,
+ concat_literals<&string_literal<'u', '_'>::value, &Ts::name>::value())... };
+ }
+
+ NamedUniformLocations getNamedLocations() const {
+ return NamedUniformLocations{ { concat_literals<&string_literal<'u', '_'>::value, &Ts::name>::value(),
+ state.template get<Ts>().location }... };
+ }
+
+ void bind(gl::Context& context, const gfx::TextureBindings<TypeList<Ts...>>& bindings) {
+ util::ignore(
+ { (state.template get<Ts>() = TypeIndex<Ts, Ts...>::value,
+ gl::bindTexture(context, TypeIndex<Ts, Ts...>::value, bindings.template get<Ts>()),
+ 0)... });
+ }
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/texture_resource.hpp b/platform/gfx/gl/src/mbgl/gl/texture_resource.hpp
new file mode 100644
index 0000000000..ed742e75b7
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/texture_resource.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <mbgl/gfx/texture.hpp>
+#include <mbgl/gl/object.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class TextureResource : public gfx::TextureResource {
+public:
+ TextureResource(UniqueTexture&& texture_) : texture(std::move(texture_)) {
+ }
+
+ UniqueTexture texture;
+ gfx::TextureFilterType filter = gfx::TextureFilterType::Nearest;
+ gfx::TextureMipMapType mipmap = gfx::TextureMipMapType::No;
+ gfx::TextureWrapType wrapX = gfx::TextureWrapType::Clamp;
+ gfx::TextureWrapType wrapY = gfx::TextureWrapType::Clamp;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/types.hpp b/platform/gfx/gl/src/mbgl/gl/types.hpp
new file mode 100644
index 0000000000..e679d7646b
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/types.hpp
@@ -0,0 +1,62 @@
+#pragma once
+
+#include <cstdint>
+#include <type_traits>
+
+namespace mbgl {
+namespace gl {
+
+// Mapping based on https://www.opengl.org/wiki/OpenGL_Type
+using ProgramID = uint32_t;
+using ShaderID = uint32_t;
+using BufferID = uint32_t;
+using TextureID = uint32_t;
+using VertexArrayID = uint32_t;
+using FramebufferID = uint32_t;
+using RenderbufferID = uint32_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;
+
+enum class ShaderType : uint32_t {
+ Vertex = 0x8B31,
+ Fragment = 0x8B30
+};
+
+struct PixelStorageType {
+ int32_t alignment;
+};
+
+constexpr bool operator!=(const PixelStorageType& a, const PixelStorageType& b) {
+ return a.alignment != b.alignment;
+}
+
+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,
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/uniform.cpp b/platform/gfx/gl/src/mbgl/gl/uniform.cpp
new file mode 100644
index 0000000000..8d99d4b467
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/uniform.cpp
@@ -0,0 +1,202 @@
+#include <mbgl/gl/uniform.hpp>
+#include <mbgl/gl/defines.hpp>
+#include <mbgl/platform/gl_functions.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/util/size.hpp>
+#include <mbgl/util/convert.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace gl {
+
+using namespace platform;
+
+UniformLocation uniformLocation(ProgramID id, const char* name) {
+ return MBGL_CHECK_ERROR(glGetUniformLocation(id, name));
+}
+
+template <>
+void bindUniform<float>(UniformLocation location, const float& t) {
+ MBGL_CHECK_ERROR(glUniform1f(location, t));
+}
+
+template <>
+void bindUniform<int32_t>(UniformLocation location, const int32_t& t) {
+ MBGL_CHECK_ERROR(glUniform1i(location, t));
+}
+
+template <>
+void bindUniform<std::array<float, 2>>(UniformLocation location, const std::array<float, 2>& t) {
+ MBGL_CHECK_ERROR(glUniform2fv(location, 1, t.data()));
+}
+
+template <>
+void bindUniform<std::array<float, 3>>(UniformLocation location, const std::array<float, 3>& t) {
+ MBGL_CHECK_ERROR(glUniform3fv(location, 1, t.data()));
+}
+
+template <>
+void bindUniform<std::array<float, 4>>(UniformLocation location, const std::array<float, 4>& t) {
+ MBGL_CHECK_ERROR(glUniform4fv(location, 1, t.data()));
+}
+
+template <>
+void bindUniform<std::array<double, 4>>(UniformLocation location, const std::array<double, 4>& t) {
+ MBGL_CHECK_ERROR(glUniformMatrix2fv(location, 1, GL_FALSE, util::convert<float>(t).data()));
+}
+
+template <>
+void bindUniform<std::array<double, 9>>(UniformLocation location, const std::array<double, 9>& t) {
+ MBGL_CHECK_ERROR(glUniformMatrix3fv(location, 1, GL_FALSE, util::convert<float>(t).data()));
+}
+
+template <>
+void bindUniform<std::array<double, 16>>(UniformLocation location, const std::array<double, 16>& t) {
+ MBGL_CHECK_ERROR(glUniformMatrix4fv(location, 1, GL_FALSE, util::convert<float>(t).data()));
+}
+
+
+template <>
+void bindUniform<bool>(UniformLocation location, const bool& t) {
+ return bindUniform(location, int32_t(t));
+}
+
+template <>
+void bindUniform<uint32_t>(UniformLocation location, const uint32_t& t) {
+ bindUniform(location, int32_t(t));
+}
+
+template <>
+void bindUniform<uint8_t>(UniformLocation location, const uint8_t& t) {
+ bindUniform(location, int32_t(t));
+}
+
+template <>
+void bindUniform<Color>(UniformLocation location, const Color& t) {
+ bindUniform(location, std::array<float, 4> {{ t.r, t.g, t.b, t.a }});
+}
+
+template <>
+void bindUniform<Size>(UniformLocation location, const Size& t) {
+ bindUniform(location, util::convert<float>(std::array<uint32_t, 2> {{ t.width, t.height }}));
+}
+
+template <>
+void bindUniform<std::array<uint16_t, 2>>(UniformLocation location, const std::array<uint16_t, 2>& t) {
+ bindUniform(location, util::convert<float>(t));
+}
+
+template <>
+void bindUniform<std::array<uint16_t, 4>>(UniformLocation location, const std::array<uint16_t, 4>& t) {
+ bindUniform(location, util::convert<float>(t));
+}
+
+// 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<float, 4>>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec4);
+ 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<uint32_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;
+}
+
+template <>
+bool verifyUniform<std::array<uint16_t, 4>>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 &&
+ (uniform.type == UniformDataType::IntVec4 ||
+ uniform.type == UniformDataType::FloatVec4));
+ return true;
+}
+
+// Add more as needed.
+
+#endif
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/uniform.hpp b/platform/gfx/gl/src/mbgl/gl/uniform.hpp
new file mode 100644
index 0000000000..10501036cb
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/uniform.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+#include <mbgl/gfx/uniform.hpp>
+#include <mbgl/gl/types.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/literal.hpp>
+#include <mbgl/util/ignore.hpp>
+#include <mbgl/util/indexed_tuple.hpp>
+
+#include <array>
+#include <vector>
+#include <map>
+#include <functional>
+
+namespace mbgl {
+namespace gl {
+
+template <class T>
+void bindUniform(UniformLocation, const 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 Value>
+class UniformState {
+public:
+ UniformState(UniformLocation location_ = -1) : location(std::move(location_)) {
+ }
+
+ void operator=(const Value& value) {
+ if (location >= 0 && (!current || *current != value)) {
+ current = value;
+ bindUniform(location, value);
+ }
+ }
+
+ UniformLocation location;
+ optional<Value> current = {};
+};
+
+UniformLocation uniformLocation(ProgramID, const char* name);
+
+using NamedUniformLocations = std::vector<std::pair<const std::string, UniformLocation>>;
+
+template <class>
+class UniformStates;
+
+template <class... Us>
+class UniformStates<TypeList<Us...>> final {
+private:
+ using State = IndexedTuple<TypeList<Us...>, TypeList<UniformState<typename Us::Value>...>>;
+
+ State state;
+
+public:
+ void queryLocations(const ProgramID& id) {
+#ifndef NDEBUG
+ // Verify active uniform types match the enum
+ const auto active = gl::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(concat_literals<&string_literal<'u', '_'>::value, &Us::name>::value()) != active.end()
+ ? verifyUniform<typename Us::Value>(active.at(concat_literals<&string_literal<'u', '_'>::value, &Us::name>::value()))
+ : false)... });
+#endif
+
+ state = State{ gl::uniformLocation(id, concat_literals<&string_literal<'u', '_'>::value, &Us::name>::value())... };
+ }
+
+ NamedUniformLocations getNamedLocations() const {
+ return NamedUniformLocations{ { concat_literals<&string_literal<'u', '_'>::value, &Us::name>::value(), state.template get<Us>().location }... };
+ }
+
+ void bind(const gfx::UniformValues<TypeList<Us...>>& values) {
+ util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... });
+ }
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/upload_pass.cpp b/platform/gfx/gl/src/mbgl/gl/upload_pass.cpp
new file mode 100644
index 0000000000..358f1a7203
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/upload_pass.cpp
@@ -0,0 +1,117 @@
+#include <mbgl/gl/upload_pass.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/enum.hpp>
+#include <mbgl/gl/defines.hpp>
+#include <mbgl/gl/command_encoder.hpp>
+#include <mbgl/gl/vertex_buffer_resource.hpp>
+#include <mbgl/gl/index_buffer_resource.hpp>
+#include <mbgl/gl/texture_resource.hpp>
+
+namespace mbgl {
+namespace gl {
+
+using namespace platform;
+
+UploadPass::UploadPass(gl::CommandEncoder& commandEncoder_, const char* name)
+ : commandEncoder(commandEncoder_), debugGroup(commandEncoder.createDebugGroup(name)) {
+}
+
+std::unique_ptr<gfx::VertexBufferResource> UploadPass::createVertexBufferResource(
+ const void* data, std::size_t size, const gfx::BufferUsageType usage) {
+ BufferID id = 0;
+ MBGL_CHECK_ERROR(glGenBuffers(1, &id));
+ UniqueBuffer result{ std::move(id), { commandEncoder.context } };
+ commandEncoder.context.vertexBuffer = result;
+ MBGL_CHECK_ERROR(
+ glBufferData(GL_ARRAY_BUFFER, size, data, Enum<gfx::BufferUsageType>::to(usage)));
+ return std::make_unique<gl::VertexBufferResource>(std::move(result));
+}
+
+void UploadPass::updateVertexBufferResource(gfx::VertexBufferResource& resource,
+ const void* data,
+ std::size_t size) {
+ commandEncoder.context.vertexBuffer = static_cast<gl::VertexBufferResource&>(resource).buffer;
+ MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data));
+}
+
+std::unique_ptr<gfx::IndexBufferResource> UploadPass::createIndexBufferResource(
+ const void* data, std::size_t size, const gfx::BufferUsageType usage) {
+ BufferID id = 0;
+ MBGL_CHECK_ERROR(glGenBuffers(1, &id));
+ UniqueBuffer result{ std::move(id), { commandEncoder.context } };
+ commandEncoder.context.bindVertexArray = 0;
+ commandEncoder.context.globalVertexArrayState.indexBuffer = result;
+ MBGL_CHECK_ERROR(
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, Enum<gfx::BufferUsageType>::to(usage)));
+ return std::make_unique<gl::IndexBufferResource>(std::move(result));
+}
+
+void UploadPass::updateIndexBufferResource(gfx::IndexBufferResource& resource,
+ const void* data,
+ std::size_t size) {
+ // Be sure to unbind any existing vertex array object before binding the index buffer
+ // so that we don't mess up another VAO
+ commandEncoder.context.bindVertexArray = 0;
+ commandEncoder.context.globalVertexArrayState.indexBuffer =
+ static_cast<gl::IndexBufferResource&>(resource).buffer;
+ MBGL_CHECK_ERROR(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data));
+}
+
+std::unique_ptr<gfx::TextureResource>
+UploadPass::createTextureResource(const Size size,
+ const void* data,
+ gfx::TexturePixelType format,
+ gfx::TextureChannelDataType type) {
+ auto obj = commandEncoder.context.createUniqueTexture();
+ std::unique_ptr<gfx::TextureResource> resource =
+ std::make_unique<gl::TextureResource>(std::move(obj));
+ commandEncoder.context.pixelStoreUnpack = { 1 };
+ updateTextureResource(*resource, size, data, format, type);
+ // 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.
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+ return resource;
+}
+
+void UploadPass::updateTextureResource(gfx::TextureResource& resource,
+ const Size size,
+ const void* data,
+ gfx::TexturePixelType format,
+ gfx::TextureChannelDataType type) {
+ // Always use texture unit 0 for manipulating it.
+ commandEncoder.context.activeTextureUnit = 0;
+ commandEncoder.context.texture[0] = static_cast<gl::TextureResource&>(resource).texture;
+ MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, Enum<gfx::TexturePixelType>::to(format),
+ size.width, size.height, 0,
+ Enum<gfx::TexturePixelType>::to(format),
+ Enum<gfx::TextureChannelDataType>::to(type), data));
+}
+
+void UploadPass::updateTextureResourceSub(gfx::TextureResource& resource,
+ const uint16_t xOffset,
+ const uint16_t yOffset,
+ const Size size,
+ const void* data,
+ gfx::TexturePixelType format,
+ gfx::TextureChannelDataType type) {
+ // Always use texture unit 0 for manipulating it.
+ commandEncoder.context.activeTextureUnit = 0;
+ commandEncoder.context.texture[0] = static_cast<const gl::TextureResource&>(resource).texture;
+ MBGL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, size.width, size.height,
+ Enum<gfx::TexturePixelType>::to(format),
+ Enum<gfx::TextureChannelDataType>::to(type), data));
+}
+
+void UploadPass::pushDebugGroup(const char* name) {
+ commandEncoder.pushDebugGroup(name);
+}
+
+void UploadPass::popDebugGroup() {
+ commandEncoder.popDebugGroup();
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/upload_pass.hpp b/platform/gfx/gl/src/mbgl/gl/upload_pass.hpp
new file mode 100644
index 0000000000..8b4f71b88d
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/upload_pass.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <mbgl/gfx/upload_pass.hpp>
+
+namespace mbgl {
+namespace gfx {
+
+class CommandEncoder;
+
+} // namespace gfx
+
+namespace gl {
+
+class CommandEncoder;
+class Context;
+
+class UploadPass final : public gfx::UploadPass {
+public:
+ UploadPass(gl::CommandEncoder&, const char* name);
+
+private:
+ void pushDebugGroup(const char* name) override;
+ void popDebugGroup() override;
+
+public:
+ std::unique_ptr<gfx::VertexBufferResource> createVertexBufferResource(const void* data, std::size_t size, const gfx::BufferUsageType) override;
+ void updateVertexBufferResource(gfx::VertexBufferResource&, const void* data, std::size_t size) override;
+ std::unique_ptr<gfx::IndexBufferResource> createIndexBufferResource(const void* data, std::size_t size, const gfx::BufferUsageType) override;
+ void updateIndexBufferResource(gfx::IndexBufferResource&, const void* data, std::size_t size) override;
+
+public:
+ std::unique_ptr<gfx::TextureResource> createTextureResource(Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override;
+ void updateTextureResource(gfx::TextureResource&, Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override;
+ void updateTextureResourceSub(gfx::TextureResource&, const uint16_t xOffset, const uint16_t yOffset, Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override;
+
+private:
+ gl::CommandEncoder& commandEncoder;
+ const gfx::DebugGroup<gfx::CommandEncoder> debugGroup;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/value.cpp b/platform/gfx/gl/src/mbgl/gl/value.cpp
new file mode 100644
index 0000000000..bd08ac48fc
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/value.cpp
@@ -0,0 +1,602 @@
+#include <mbgl/gl/value.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/vertex_buffer_resource.hpp>
+#include <mbgl/gl/vertex_array_extension.hpp>
+#include <mbgl/gl/enum.hpp>
+
+namespace mbgl {
+namespace gl {
+namespace value {
+
+using namespace platform;
+
+const constexpr ClearDepth::Type ClearDepth::Default;
+
+void ClearDepth::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearDepthf(value));
+}
+
+ClearDepth::Type ClearDepth::Get() {
+ GLfloat clearDepth;
+ MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth));
+ return clearDepth;
+}
+
+const ClearColor::Type ClearColor::Default { 0, 0, 0, 0 };
+
+void ClearColor::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearColor(value.r, value.g, value.b, value.a));
+}
+
+ClearColor::Type ClearColor::Get() {
+ GLfloat clearColor[4];
+ MBGL_CHECK_ERROR(glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor));
+ return { clearColor[0], clearColor[1], clearColor[2], clearColor[3] };
+}
+
+const constexpr ClearStencil::Type ClearStencil::Default;
+
+void ClearStencil::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearStencil(value));
+}
+
+ClearStencil::Type ClearStencil::Get() {
+ GLint clearStencil;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &clearStencil));
+ return clearStencil;
+}
+
+const constexpr StencilMask::Type StencilMask::Default;
+
+void StencilMask::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glStencilMask(value));
+}
+
+StencilMask::Type StencilMask::Get() {
+ GLint stencilMask;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_WRITEMASK, &stencilMask));
+ return stencilMask;
+}
+
+const constexpr DepthMask::Type DepthMask::Default;
+
+void DepthMask::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glDepthMask(Enum<gfx::DepthMaskType>::to(value)));
+}
+
+DepthMask::Type DepthMask::Get() {
+ GLboolean depthMask;
+ MBGL_CHECK_ERROR(glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask));
+ return Enum<gfx::DepthMaskType>::from(depthMask);
+}
+
+const constexpr ColorMask::Type ColorMask::Default;
+
+void ColorMask::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glColorMask(value.r, value.g, value.b, value.a));
+}
+
+ColorMask::Type ColorMask::Get() {
+ GLboolean bools[4];
+ MBGL_CHECK_ERROR(glGetBooleanv(GL_COLOR_WRITEMASK, bools));
+ return { static_cast<bool>(bools[0]), static_cast<bool>(bools[1]), static_cast<bool>(bools[2]),
+ static_cast<bool>(bools[3]) };
+}
+
+const constexpr StencilFunc::Type StencilFunc::Default;
+
+void StencilFunc::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glStencilFunc(Enum<gfx::StencilFunctionType>::to(value.func), value.ref, value.mask));
+}
+
+StencilFunc::Type StencilFunc::Get() {
+ GLint func, ref, mask;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_FUNC, &func));
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_REF, &ref));
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_VALUE_MASK, &mask));
+ return { Enum<gfx::StencilFunctionType>::from(func), ref, static_cast<uint32_t>(mask) };
+}
+
+const constexpr StencilTest::Type StencilTest::Default;
+
+void StencilTest::Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_STENCIL_TEST) : glDisable(GL_STENCIL_TEST));
+}
+
+StencilTest::Type StencilTest::Get() {
+ Type stencilTest;
+ MBGL_CHECK_ERROR(stencilTest = glIsEnabled(GL_STENCIL_TEST));
+ return stencilTest;
+}
+
+const constexpr StencilOp::Type StencilOp::Default;
+
+void StencilOp::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glStencilOp(Enum<gfx::StencilOpType>::to(value.sfail),
+ Enum<gfx::StencilOpType>::to(value.dpfail),
+ Enum<gfx::StencilOpType>::to(value.dppass)));
+}
+
+StencilOp::Type StencilOp::Get() {
+ GLint sfail, dpfail, dppass;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_FAIL, &sfail));
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &dpfail));
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &dppass));
+ return { Enum<gfx::StencilOpType>::from(sfail),
+ Enum<gfx::StencilOpType>::from(dpfail),
+ Enum<gfx::StencilOpType>::from(dppass) };
+}
+
+const constexpr DepthRange::Type DepthRange::Default;
+
+void DepthRange::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glDepthRangef(value.min, value.max));
+}
+
+DepthRange::Type DepthRange::Get() {
+ GLfloat floats[2];
+ MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_RANGE, floats));
+ return { floats[0], floats[1] };
+}
+
+const constexpr DepthTest::Type DepthTest::Default;
+
+void DepthTest::Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST));
+}
+
+DepthTest::Type DepthTest::Get() {
+ Type depthTest;
+ MBGL_CHECK_ERROR(depthTest = glIsEnabled(GL_DEPTH_TEST));
+ return depthTest;
+}
+
+const constexpr DepthFunc::Type DepthFunc::Default;
+
+void DepthFunc::Set(const DepthFunc::Type& value) {
+ MBGL_CHECK_ERROR(glDepthFunc(Enum<gfx::DepthFunctionType>::to(value)));
+}
+
+DepthFunc::Type DepthFunc::Get() {
+ GLint depthFunc;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_DEPTH_FUNC, &depthFunc));
+ return Enum<gfx::DepthFunctionType>::from(depthFunc);
+}
+
+const constexpr Blend::Type Blend::Default;
+
+void Blend::Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_BLEND) : glDisable(GL_BLEND));
+}
+
+Blend::Type Blend::Get() {
+ Type blend;
+ MBGL_CHECK_ERROR(blend = glIsEnabled(GL_BLEND));
+ return blend;
+}
+
+const constexpr BlendEquation::Type BlendEquation::Default;
+
+void BlendEquation::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBlendEquation(Enum<gfx::ColorBlendEquationType>::to(value)));
+}
+
+BlendEquation::Type BlendEquation::Get() {
+ GLint blend;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_BLEND_EQUATION_RGB, &blend));
+ return Enum<gfx::ColorBlendEquationType>::from(blend);
+}
+
+const constexpr BlendFunc::Type BlendFunc::Default;
+
+void BlendFunc::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBlendFunc(Enum<gfx::ColorBlendFactorType>::to(value.sfactor),
+ Enum<gfx::ColorBlendFactorType>::to(value.dfactor)));
+}
+
+BlendFunc::Type BlendFunc::Get() {
+ GLint sfactor, dfactor;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_BLEND_SRC_ALPHA, &sfactor));
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_BLEND_DST_ALPHA, &dfactor));
+ return { Enum<gfx::ColorBlendFactorType>::from(sfactor),
+ Enum<gfx::ColorBlendFactorType>::from(dfactor) };
+}
+
+const BlendColor::Type BlendColor::Default { 0, 0, 0, 0 };
+
+void BlendColor::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBlendColor(value.r, value.g, value.b, value.a));
+}
+
+BlendColor::Type BlendColor::Get() {
+ GLfloat floats[4];
+ MBGL_CHECK_ERROR(glGetFloatv(GL_BLEND_COLOR, floats));
+ return { floats[0], floats[1], floats[2], floats[3] };
+}
+
+const constexpr Program::Type Program::Default;
+
+void Program::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glUseProgram(value));
+}
+
+Program::Type Program::Get() {
+ GLint program;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_CURRENT_PROGRAM, &program));
+ return program;
+}
+
+const constexpr LineWidth::Type LineWidth::Default;
+
+void LineWidth::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glLineWidth(value));
+}
+
+LineWidth::Type LineWidth::Get() {
+ GLfloat lineWidth;
+ MBGL_CHECK_ERROR(glGetFloatv(GL_LINE_WIDTH, &lineWidth));
+ return lineWidth;
+}
+
+const constexpr ActiveTextureUnit::Type ActiveTextureUnit::Default;
+
+void ActiveTextureUnit::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value));
+}
+
+ActiveTextureUnit::Type ActiveTextureUnit::Get() {
+ GLint activeTexture;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture));
+ return static_cast<Type>(activeTexture - GL_TEXTURE0);
+}
+
+const constexpr Viewport::Type Viewport::Default;
+
+void Viewport::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glViewport(value.x, value.y, value.size.width, value.size.height));
+}
+
+Viewport::Type Viewport::Get() {
+ GLint viewport[4];
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_VIEWPORT, viewport));
+ return { static_cast<int32_t>(viewport[0]), static_cast<int32_t>(viewport[1]),
+ { 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) {
+ MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, value));
+}
+
+BindFramebuffer::Type BindFramebuffer::Get() {
+ GLint binding;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &binding));
+ return binding;
+}
+
+const constexpr BindRenderbuffer::Type BindRenderbuffer::Default;
+
+void BindRenderbuffer::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, value));
+}
+
+BindRenderbuffer::Type BindRenderbuffer::Get() {
+ GLint binding;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_RENDERBUFFER_BINDING, &binding));
+ return binding;
+}
+
+const constexpr CullFace::Type CullFace::Default;
+
+void CullFace::Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE));
+}
+
+CullFace::Type CullFace::Get() {
+ GLboolean cullFace;
+ MBGL_CHECK_ERROR(cullFace = glIsEnabled(GL_CULL_FACE));
+ return cullFace;
+}
+
+const constexpr CullFaceSide::Type CullFaceSide::Default;
+
+void CullFaceSide::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glCullFace(Enum<gfx::CullFaceSideType>::to(value)));
+}
+
+CullFaceSide::Type CullFaceSide::Get() {
+ GLint cullFaceMode;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_CULL_FACE_MODE, &cullFaceMode));
+ return Enum<gfx::CullFaceSideType>::from(cullFaceMode);
+}
+
+const constexpr CullFaceWinding::Type CullFaceWinding::Default;
+
+void CullFaceWinding::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glFrontFace(Enum<gfx::CullFaceWindingType>::to(value)));
+}
+
+CullFaceWinding::Type CullFaceWinding::Get() {
+ GLint frontFace;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_FRONT_FACE, &frontFace));
+ return Enum<gfx::CullFaceWindingType>::from(frontFace);
+}
+
+const constexpr BindTexture::Type BindTexture::Default;
+
+void BindTexture::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, value));
+}
+
+BindTexture::Type BindTexture::Get() {
+ GLint binding;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_TEXTURE_BINDING_2D, &binding));
+ return binding;
+}
+
+const constexpr BindVertexBuffer::Type BindVertexBuffer::Default;
+
+void BindVertexBuffer::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, value));
+}
+
+BindVertexBuffer::Type BindVertexBuffer::Get() {
+ GLint binding;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &binding));
+ return binding;
+}
+
+const constexpr BindElementBuffer::Type BindElementBuffer::Default;
+
+void BindElementBuffer::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, value));
+}
+
+BindElementBuffer::Type BindElementBuffer::Get() {
+ GLint binding;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &binding));
+ return binding;
+}
+
+const constexpr BindVertexArray::Type BindVertexArray::Default;
+
+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(const Context& context) {
+ GLint binding = 0;
+ if (context.getVertexArrayExtension()) {
+#ifdef GL_VERTEX_ARRAY_BINDING
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &binding));
+#elif GL_VERTEX_ARRAY_BINDING_OES
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_VERTEX_ARRAY_BINDING_OES, &binding));
+#elif GL_VERTEX_ARRAY_BINDING_ARB
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_VERTEX_ARRAY_BINDING_ARB, &binding));
+#elif GLVERTEX_ARRAY_BINDING_APPLE
+ MBGL_CHECK_ERROR(glGetIntegerv(GLVERTEX_ARRAY_BINDING_APPLE, &binding));
+#endif
+ }
+ return binding;
+}
+
+const VertexAttribute::Type VertexAttribute::Default {};
+
+namespace {
+
+GLenum vertexType(const gfx::AttributeDataType type) {
+ switch (type) {
+ case gfx::AttributeDataType::Byte:
+ case gfx::AttributeDataType::Byte2:
+ case gfx::AttributeDataType::Byte3:
+ case gfx::AttributeDataType::Byte4:
+ return GL_BYTE;
+ case gfx::AttributeDataType::UByte:
+ case gfx::AttributeDataType::UByte2:
+ case gfx::AttributeDataType::UByte3:
+ case gfx::AttributeDataType::UByte4:
+ return GL_UNSIGNED_BYTE;
+ case gfx::AttributeDataType::Short:
+ case gfx::AttributeDataType::Short2:
+ case gfx::AttributeDataType::Short3:
+ case gfx::AttributeDataType::Short4:
+ return GL_SHORT;
+ case gfx::AttributeDataType::UShort:
+ case gfx::AttributeDataType::UShort2:
+ case gfx::AttributeDataType::UShort3:
+ case gfx::AttributeDataType::UShort4:
+ return GL_UNSIGNED_SHORT;
+ case gfx::AttributeDataType::Int:
+ case gfx::AttributeDataType::Int2:
+ case gfx::AttributeDataType::Int3:
+ case gfx::AttributeDataType::Int4:
+ return GL_INT;
+ case gfx::AttributeDataType::UInt:
+ case gfx::AttributeDataType::UInt2:
+ case gfx::AttributeDataType::UInt3:
+ case gfx::AttributeDataType::UInt4:
+ return GL_UNSIGNED_INT;
+ case gfx::AttributeDataType::Float:
+ case gfx::AttributeDataType::Float2:
+ case gfx::AttributeDataType::Float3:
+ case gfx::AttributeDataType::Float4:
+ return GL_FLOAT;
+ default:
+ return GL_FLOAT;
+ }
+}
+
+GLint components(const gfx::AttributeDataType type) {
+ switch (type) {
+ case gfx::AttributeDataType::Byte:
+ case gfx::AttributeDataType::UByte:
+ case gfx::AttributeDataType::Short:
+ case gfx::AttributeDataType::UShort:
+ case gfx::AttributeDataType::Int:
+ case gfx::AttributeDataType::UInt:
+ case gfx::AttributeDataType::Float:
+ return 1;
+ case gfx::AttributeDataType::Byte2:
+ case gfx::AttributeDataType::UByte2:
+ case gfx::AttributeDataType::Short2:
+ case gfx::AttributeDataType::UShort2:
+ case gfx::AttributeDataType::Int2:
+ case gfx::AttributeDataType::UInt2:
+ case gfx::AttributeDataType::Float2:
+ return 2;
+ case gfx::AttributeDataType::Byte3:
+ case gfx::AttributeDataType::UByte3:
+ case gfx::AttributeDataType::Short3:
+ case gfx::AttributeDataType::UShort3:
+ case gfx::AttributeDataType::Int3:
+ case gfx::AttributeDataType::UInt3:
+ case gfx::AttributeDataType::Float3:
+ return 3;
+ case gfx::AttributeDataType::Byte4:
+ case gfx::AttributeDataType::UByte4:
+ case gfx::AttributeDataType::Short4:
+ case gfx::AttributeDataType::UShort4:
+ case gfx::AttributeDataType::Int4:
+ case gfx::AttributeDataType::UInt4:
+ case gfx::AttributeDataType::Float4:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+} // namespace
+
+void VertexAttribute::Set(const Type& binding, Context& context, AttributeLocation location) {
+ if (binding) {
+ context.vertexBuffer = reinterpret_cast<const gl::VertexBufferResource&>(*binding->vertexBufferResource).buffer;
+ MBGL_CHECK_ERROR(glEnableVertexAttribArray(location));
+ MBGL_CHECK_ERROR(glVertexAttribPointer(
+ location,
+ components(binding->attribute.dataType),
+ vertexType(binding->attribute.dataType),
+ static_cast<GLboolean>(false),
+ static_cast<GLsizei>(binding->vertexStride),
+ reinterpret_cast<GLvoid*>(binding->attribute.offset + (binding->vertexStride * 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;
+
+void PointSize::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glPointSize(value));
+}
+
+PointSize::Type PointSize::Get() {
+ GLfloat pointSize;
+ MBGL_CHECK_ERROR(glGetFloatv(GL_POINT_SIZE, &pointSize));
+ return pointSize;
+}
+
+const constexpr PixelZoom::Type PixelZoom::Default;
+
+void PixelZoom::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glPixelZoom(value.xfactor, value.yfactor));
+}
+
+PixelZoom::Type PixelZoom::Get() {
+ GLfloat xfactor, yfactor;
+ MBGL_CHECK_ERROR(glGetFloatv(GL_ZOOM_X, &xfactor));
+ MBGL_CHECK_ERROR(glGetFloatv(GL_ZOOM_Y, &yfactor));
+ return { xfactor, yfactor };
+}
+
+const constexpr RasterPos::Type RasterPos::Default;
+
+void RasterPos::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glRasterPos4d(value.x, value.y, value.z, value.w));
+}
+
+RasterPos::Type RasterPos::Get() {
+ GLdouble pos[4];
+ MBGL_CHECK_ERROR(glGetDoublev(GL_CURRENT_RASTER_POSITION, pos));
+ return { pos[0], pos[1], pos[2], pos[3] };
+}
+
+const constexpr PixelTransferDepth::Type PixelTransferDepth::Default;
+
+void PixelTransferDepth::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glPixelTransferf(GL_DEPTH_SCALE, value.scale));
+ MBGL_CHECK_ERROR(glPixelTransferf(GL_DEPTH_BIAS, value.bias));
+}
+
+PixelTransferDepth::Type PixelTransferDepth::Get() {
+ Type value;
+ MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_SCALE, &value.scale));
+ MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_BIAS, &value.bias));
+ return value;
+}
+
+const constexpr PixelTransferStencil::Type PixelTransferStencil::Default;
+
+void PixelTransferStencil::Set(const Type& value) {
+ MBGL_CHECK_ERROR(glPixelTransferf(GL_INDEX_SHIFT, value.shift));
+ MBGL_CHECK_ERROR(glPixelTransferf(GL_INDEX_OFFSET, value.offset));
+}
+
+PixelTransferStencil::Type PixelTransferStencil::Get() {
+ Type value;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_INDEX_SHIFT, &value.shift));
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_INDEX_OFFSET, &value.offset));
+ return value;
+}
+
+#endif // MBGL_USE_GLES2
+
+} // namespace value
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/value.hpp b/platform/gfx/gl/src/mbgl/gl/value.hpp
new file mode 100644
index 0000000000..5bd2132ecf
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/value.hpp
@@ -0,0 +1,357 @@
+#pragma once
+
+#include <mbgl/gl/types.hpp>
+#include <mbgl/gfx/depth_mode.hpp>
+#include <mbgl/gfx/stencil_mode.hpp>
+#include <mbgl/gfx/color_mode.hpp>
+#include <mbgl/gfx/cull_face_mode.hpp>
+#include <mbgl/gl/attribute.hpp>
+#include <mbgl/platform/gl_functions.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 {
+ using Type = float;
+ static const constexpr Type Default = 1;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct ClearColor {
+ using Type = Color;
+ static const Type Default;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct ClearStencil {
+ using Type = int32_t;
+ static const constexpr Type Default = 0;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct StencilMask {
+ using Type = uint32_t;
+ static const constexpr Type Default = ~0u;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct DepthMask {
+ using Type = gfx::DepthMaskType;
+ static const constexpr Type Default = gfx::DepthMaskType::ReadWrite;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct ColorMask {
+ using Type = gfx::ColorMode::Mask;
+ static const constexpr Type Default = { true, true, true, true };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct StencilFunc {
+ struct Type {
+ gfx::StencilFunctionType func;
+ int32_t ref;
+ uint32_t mask;
+ };
+ static const constexpr Type Default = { gfx::StencilMode::Always::func, 0, ~0u };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+constexpr bool operator!=(const StencilFunc::Type& a, const StencilFunc::Type& b) {
+ return a.func != b.func || a.ref != b.ref || a.mask != b.mask;
+}
+
+struct StencilTest {
+ using Type = bool;
+ static const constexpr Type Default = false;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct StencilOp {
+ struct Type {
+ gfx::StencilOpType sfail;
+ gfx::StencilOpType dpfail;
+ gfx::StencilOpType dppass;
+ };
+ static const constexpr Type Default = { gfx::StencilOpType::Keep, gfx::StencilOpType::Keep, gfx::StencilOpType::Keep };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+constexpr bool operator!=(const StencilOp::Type& a, const StencilOp::Type& b) {
+ return a.sfail != b.sfail || a.dpfail != b.dpfail || a.dppass != b.dppass;
+}
+
+struct DepthRange {
+ using Type = Range<float>;
+ static const constexpr Type Default = { 0, 1 };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct DepthTest {
+ using Type = bool;
+ static const constexpr Type Default = false;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct DepthFunc {
+ using Type = gfx::DepthFunctionType;
+ static const constexpr Type Default = gfx::DepthFunctionType::Less;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct Blend {
+ using Type = bool;
+ static const constexpr Type Default = true;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct BlendEquation {
+ using Type = gfx::ColorBlendEquationType;
+ static const constexpr Type Default = gfx::ColorBlendEquationType::Add;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct BlendFunc {
+ struct Type {
+ gfx::ColorBlendFactorType sfactor;
+ gfx::ColorBlendFactorType dfactor;
+ };
+ static const constexpr Type Default = { gfx::ColorBlendFactorType::One, gfx::ColorBlendFactorType::Zero };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+constexpr bool operator!=(const BlendFunc::Type& a, const BlendFunc::Type& b) {
+ return a.sfactor != b.sfactor || a.dfactor != b.dfactor;
+}
+
+struct BlendColor {
+ using Type = Color;
+ static const Type Default;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct Program {
+ using Type = gl::ProgramID;
+ static const constexpr Type Default = 0;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct LineWidth {
+ using Type = float;
+ static const constexpr Type Default = 1;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct ActiveTextureUnit {
+ using Type = uint8_t;
+ static const constexpr Type Default = 0;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct Viewport {
+ struct Type {
+ int32_t x;
+ int32_t y;
+ Size size;
+ };
+ static const constexpr Type Default = { 0, 0, { 0, 0 } };
+ static void Set(const Type&);
+ 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;
+}
+
+constexpr bool operator==(const Viewport::Type& a, const Viewport::Type& b) {
+ return !(a != b);
+}
+
+struct BindFramebuffer {
+ using Type = FramebufferID;
+ static const constexpr Type Default = 0;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct BindRenderbuffer {
+ using Type = RenderbufferID;
+ static const constexpr Type Default = 0;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct CullFace {
+ using Type = bool;
+ static const constexpr Type Default = false;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct CullFaceSide {
+ using Type = gfx::CullFaceSideType;
+ static const constexpr Type Default = gfx::CullFaceSideType::Back;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct CullFaceWinding {
+ using Type = gfx::CullFaceWindingType;
+ static const constexpr Type Default = gfx::CullFaceWindingType::CounterClockwise;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct BindTexture {
+ using Type = gl::TextureID;
+ static const constexpr Type Default = 0;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct BindVertexBuffer {
+ using Type = gl::BufferID;
+ static const constexpr Type Default = 0;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct BindElementBuffer {
+ using Type = gl::BufferID;
+ static const constexpr Type Default = 0;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+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<gfx::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();
+};
+
+#if not MBGL_USE_GLES2
+
+struct PointSize {
+ using Type = float;
+ static const constexpr Type Default = 1;
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct PixelZoom {
+ struct Type {
+ float xfactor;
+ float yfactor;
+ };
+ static const constexpr Type Default = { 1, 1 };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+constexpr bool operator!=(const PixelZoom::Type& a, const PixelZoom::Type& b) {
+ return a.xfactor != b.xfactor || a.yfactor != b.yfactor;
+}
+
+struct RasterPos {
+ struct Type {
+ double x;
+ double y;
+ double z;
+ double w;
+ };
+ static const constexpr Type Default = { 0, 0, 0, 1 };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+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 PixelTransferDepth {
+ struct Type {
+ float scale;
+ float bias;
+ };
+ static const constexpr Type Default = { 1, 0 };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+constexpr bool operator!=(const PixelTransferDepth::Type& a, const PixelTransferDepth::Type& b) {
+ return a.scale != b.scale || a.bias != b.bias;
+}
+
+struct PixelTransferStencil {
+ struct Type {
+ int32_t shift;
+ int32_t offset;
+ };
+ static const constexpr Type Default = { 0, 0 };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+constexpr bool operator!=(const PixelTransferStencil::Type& a, const PixelTransferStencil::Type& b) {
+ return a.shift != b.shift || a.offset != b.offset;
+}
+
+#endif // MBGL_USE_GLES2
+
+} // namespace value
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/vertex_array.cpp b/platform/gfx/gl/src/mbgl/gl/vertex_array.cpp
new file mode 100644
index 0000000000..831b118fce
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/vertex_array.cpp
@@ -0,0 +1,24 @@
+#include <mbgl/gl/vertex_array.hpp>
+#include <mbgl/gl/index_buffer_resource.hpp>
+#include <mbgl/gl/context.hpp>
+
+namespace mbgl {
+namespace gl {
+
+void VertexArray::bind(Context& context,
+ const gfx::IndexBuffer& indexBuffer,
+ const AttributeBindingArray& bindings) {
+ context.bindVertexArray = state->vertexArray;
+ state->indexBuffer = indexBuffer.getResource<gl::IndexBufferResource>().buffer;
+
+ state->bindings.reserve(bindings.size());
+ for (AttributeLocation location = 0; location < bindings.size(); ++location) {
+ if (state->bindings.size() <= location) {
+ state->bindings.emplace_back(context, AttributeLocation(location));
+ }
+ state->bindings[location] = bindings[location];
+ }
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/vertex_array.hpp b/platform/gfx/gl/src/mbgl/gl/vertex_array.hpp
new file mode 100644
index 0000000000..70413050b2
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/vertex_array.hpp
@@ -0,0 +1,71 @@
+#pragma once
+
+#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 gfx {
+class IndexBuffer;
+} // namespace gfx
+
+namespace gl {
+
+class Context;
+
+class VertexArrayState {
+public:
+ VertexArrayState(UniqueVertexArray vertexArray_)
+ : vertexArray(std::move(vertexArray_)) {
+ }
+
+ void setDirty() {
+ indexBuffer.setDirty();
+ for (auto& binding : bindings) {
+ binding.setDirty();
+ }
+ }
+
+ UniqueVertexArray vertexArray;
+ State<value::BindElementBuffer> indexBuffer;
+
+ using AttributeState = State<value::VertexAttribute, Context&, AttributeLocation>;
+ std::vector<AttributeState> bindings;
+};
+
+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&, const gfx::IndexBuffer&, const AttributeBindingArray&);
+
+private:
+ UniqueVertexArrayState state;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/vertex_array_extension.hpp b/platform/gfx/gl/src/mbgl/gl/vertex_array_extension.hpp
new file mode 100644
index 0000000000..967d99af32
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/vertex_array_extension.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <mbgl/gl/extension.hpp>
+#include <mbgl/gl/defines.hpp>
+#include <mbgl/platform/gl_functions.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(platform::GLuint array)> bindVertexArray;
+
+ const ExtensionFunction<void(platform::GLsizei n, const platform::GLuint* arrays)> deleteVertexArrays;
+
+ const ExtensionFunction<void(platform::GLsizei n, platform::GLuint* arrays)> genVertexArrays;
+};
+
+} // namespace extension
+} // namespace gl
+} // namespace mbgl
diff --git a/platform/gfx/gl/src/mbgl/gl/vertex_buffer_resource.hpp b/platform/gfx/gl/src/mbgl/gl/vertex_buffer_resource.hpp
new file mode 100644
index 0000000000..95e5e75d45
--- /dev/null
+++ b/platform/gfx/gl/src/mbgl/gl/vertex_buffer_resource.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mbgl/gfx/vertex_buffer.hpp>
+#include <mbgl/gl/object.hpp>
+
+namespace mbgl {
+namespace gl {
+
+class VertexBufferResource : public gfx::VertexBufferResource {
+public:
+ VertexBufferResource(UniqueBuffer&& buffer_) : buffer(std::move(buffer_)) {
+ }
+
+ UniqueBuffer buffer;
+};
+
+} // namespace gl
+} // namespace mbgl