summaryrefslogtreecommitdiff
path: root/src/mbgl/gl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/gl')
-rw-r--r--src/mbgl/gl/debugging.cpp2
-rw-r--r--src/mbgl/gl/gl.cpp236
-rw-r--r--src/mbgl/gl/gl_config.cpp24
-rw-r--r--src/mbgl/gl/gl_config.hpp100
-rw-r--r--src/mbgl/gl/gl_object_store.cpp120
-rw-r--r--src/mbgl/gl/gl_object_store.hpp141
-rw-r--r--src/mbgl/gl/texture_pool.cpp41
-rw-r--r--src/mbgl/gl/texture_pool.hpp41
8 files changed, 704 insertions, 1 deletions
diff --git a/src/mbgl/gl/debugging.cpp b/src/mbgl/gl/debugging.cpp
index fb9e037714..e61de42bc3 100644
--- a/src/mbgl/gl/debugging.cpp
+++ b/src/mbgl/gl/debugging.cpp
@@ -1,5 +1,5 @@
#include <mbgl/gl/debugging.hpp>
-#include <mbgl/platform/gl.hpp>
+#include <mbgl/gl/gl.hpp>
#include <mbgl/platform/event.hpp>
#include <mbgl/platform/log.hpp>
diff --git a/src/mbgl/gl/gl.cpp b/src/mbgl/gl/gl.cpp
new file mode 100644
index 0000000000..c57717aa87
--- /dev/null
+++ b/src/mbgl/gl/gl.cpp
@@ -0,0 +1,236 @@
+#include <mbgl/gl/gl.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/platform/log.hpp>
+
+#include <cassert>
+#include <iostream>
+#include <map>
+#include <mutex>
+
+namespace mbgl {
+namespace gl {
+
+std::vector<ExtensionFunctionBase*>& ExtensionFunctionBase::functions() {
+ static std::vector<ExtensionFunctionBase*> functions;
+ return functions;
+}
+
+static std::once_flag initializeExtensionsOnce;
+
+void InitializeExtensions(glProc (*getProcAddress)(const char *)) {
+ std::call_once(initializeExtensionsOnce, [getProcAddress] {
+ const char * extensionsPtr = reinterpret_cast<const char *>(
+ MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)));
+
+ if (!extensionsPtr)
+ return;
+
+ const std::string extensions = extensionsPtr;
+ for (auto fn : ExtensionFunctionBase::functions()) {
+ for (auto probe : fn->probes) {
+ if (extensions.find(probe.first) != std::string::npos) {
+#ifdef GL_TRACK
+ fn->foundName = probe.second;
+#endif
+ fn->ptr = getProcAddress(probe.second);
+ break;
+ }
+ }
+ }
+ });
+}
+
+void checkError(const char *cmd, const char *file, int line) {
+ const GLenum err = glGetError();
+ if (err != GL_NO_ERROR) {
+ const char *error = nullptr;
+ switch (err) {
+ case GL_INVALID_ENUM: error = "INVALID_ENUM"; break;
+ case GL_INVALID_VALUE: error = "INVALID_VALUE"; break;
+ case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break;
+ case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break;
+ case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break;
+#ifdef GL_STACK_UNDERFLOW
+ case GL_STACK_UNDERFLOW: error = "STACK_UNDERFLOW"; break;
+#endif
+#ifdef GL_STACK_OVERFLOW
+ case GL_STACK_OVERFLOW: error = "STACK_OVERFLOW"; break;
+#endif
+ default: error = "(unknown)"; break;
+ }
+
+ throw ::mbgl::gl::Error(err, std::string(cmd) + ": Error GL_" + error + " - " + file + ":" + util::toString(line));
+ }
+}
+} // namespace gl
+} // namespace mbgl
+
+#ifdef GL_TRACK
+#undef glBindTexture
+#undef glDeleteTextures
+#undef glTexImage2D
+#undef glClear
+#undef glShaderSource
+#undef glBufferData
+#undef glBindBuffer
+#undef glDeleteBuffers
+#undef glBufferData
+static unsigned int currentUsedBytes = 0;
+static GLint currentBoundTexture = 0;
+static std::map<GLint, unsigned int> bindingToSizeMap;
+
+static GLuint currentArrayBuffer = 0;
+static GLuint currentElementArrayBuffer = 0;
+static std::map<GLint, GLsizeiptr> bufferBindingToSizeMap;
+static unsigned int currentUsedBufferBytes = 0;
+static unsigned int largestAmountUsedSoFar = 0;
+
+static std::map<GLuint, GLuint> vertexArrayToArrayBufferMap;
+static GLuint currentVertexArray = 0;
+
+static std::mutex gDebugMutex;
+
+namespace mbgl {
+ namespace gl {
+ void mbx_trapExtension(const char *) { }
+ void mbx_trapExtension(const char *, GLint, const char *) { }
+ void mbx_trapExtension(const char *, GLsizei, GLuint *) { }
+ void mbx_trapExtension(const char *, GLsizei, const GLuint *) { }
+ void mbx_trapExtension(const char *, GLenum, GLenum, GLenum, GLsizei, const GLuint *, GLboolean) { }
+ void mbx_trapExtension(const char *, GLenum, GLuint, GLsizei, const GLchar *) { }
+ void mbx_trapExtension(const char *, GLDEBUGPROC, const void *) { }
+ void mbx_trapExtension(const char *, GLuint, GLuint, GLuint, GLuint, GLint, const char *, const void*) { }
+
+ void mbx_trapExtension(const char *name, GLuint array) {
+ if(strncasecmp(name, "glBindVertexArray", 17) == 0) {
+ currentVertexArray = array;
+ std::cout << name << ": " << array << std::endl;
+ }
+ }
+ }
+}
+
+void mbx_glBindBuffer(GLenum target,
+ GLuint buffer) {
+ std::unique_lock<std::mutex> lock(gDebugMutex);
+ if (target == GL_ARRAY_BUFFER) {
+ currentArrayBuffer = buffer;
+ if (currentVertexArray != 0) {
+ if (vertexArrayToArrayBufferMap.find(currentVertexArray) != vertexArrayToArrayBufferMap.end()) {
+ if (vertexArrayToArrayBufferMap[currentVertexArray] != currentArrayBuffer) {
+ std::cout << "glBindBuffer: ERROR: You are re-binding a VAO to point to a new array buffer. This is almost certainly unintended." << std::endl;
+ }
+ }
+ std::cout << "glBindBuffer: binding VAO " << currentVertexArray << " to array buffer " << currentArrayBuffer << std::endl;
+ vertexArrayToArrayBufferMap[currentVertexArray] = currentArrayBuffer;
+ }
+ } else if (target == GL_ELEMENT_ARRAY_BUFFER) {
+ currentElementArrayBuffer = buffer;
+ }
+ lock.unlock();
+ glBindBuffer(target, buffer);
+}
+
+void mbx_glDeleteBuffers(GLsizei n,
+ const GLuint * buffers) {
+ std::unique_lock<std::mutex> lock(gDebugMutex);
+ for (int i = 0; i < n; ++i) {
+ if (bufferBindingToSizeMap.find(buffers[i]) != bufferBindingToSizeMap.end()) {
+ currentUsedBufferBytes -= bufferBindingToSizeMap[buffers[i]];
+ std::cout << "GL glDeleteBuffers: " << buffers[i] << " freeing " << bufferBindingToSizeMap[buffers[i]] << " bytes current total " << currentUsedBufferBytes << "\n";
+ bufferBindingToSizeMap.erase(buffers[i]);
+ }
+ }
+ lock.unlock();
+ glDeleteBuffers(n, buffers);
+}
+
+void mbx_glBufferData(GLenum target,
+ GLsizeiptr size,
+ const GLvoid * data,
+ GLenum usage) {
+ std::unique_lock<std::mutex> lock(gDebugMutex);
+ GLuint currentBinding = 0;
+ if (target == GL_ARRAY_BUFFER) {
+ currentBinding = currentArrayBuffer;
+ } else if (target == GL_ELEMENT_ARRAY_BUFFER) {
+ currentBinding = currentElementArrayBuffer;
+ }
+ if (bufferBindingToSizeMap.find(currentBinding) != bufferBindingToSizeMap.end()) {
+ currentUsedBufferBytes -= bufferBindingToSizeMap[currentBinding];
+ std::cout << "GL glBufferData: " << currentBinding << " freeing " << bufferBindingToSizeMap[currentBinding] << " bytes current total " << currentUsedBufferBytes << "\n";
+ }
+ bufferBindingToSizeMap[currentBinding] = size;
+ currentUsedBufferBytes += size;
+ if (currentUsedBufferBytes > largestAmountUsedSoFar) {
+ largestAmountUsedSoFar = currentUsedBufferBytes;
+ }
+ std::cout << "GL glBufferData: " << currentBinding << " using " << bufferBindingToSizeMap[currentBinding] << " bytes current total " << currentUsedBufferBytes << " high water mark " << largestAmountUsedSoFar << "\n";
+ lock.unlock();
+
+ glBufferData(target, size, data, usage);
+}
+
+
+void mbx_glShaderSource(GLuint shader,
+ GLsizei count,
+ const GLchar * const *string,
+ const GLint *length) {
+ //std::cout << "Calling glShaderSource: " << *string << std::endl;
+ glShaderSource(shader, count, const_cast<const GLchar **>(string), length);
+}
+
+void mbx_glClear(GLbitfield mask) {
+ //std::cout << "Calling glClear" << std::endl;
+ glClear(mask);
+}
+
+void mbx_glBindTexture( GLenum target,
+ GLuint texture) {
+ std::unique_lock<std::mutex> lock(gDebugMutex);
+ if (target == GL_TEXTURE_2D) {
+ currentBoundTexture = texture;
+ }
+ lock.unlock();
+ glBindTexture(target, texture);
+}
+
+void mbx_glDeleteTextures(GLsizei n,
+ const GLuint * textures) {
+ std::unique_lock<std::mutex> lock(gDebugMutex);
+ for (int i = 0; i < n; ++i) {
+ if (bindingToSizeMap.find(textures[i]) != bindingToSizeMap.end()) {
+ std::cout << "GL deleteTexture:" << textures[i] << "freeing " << bindingToSizeMap[textures[i]] << " bytes current total " << currentUsedBytes << "\n";
+ currentUsedBytes -= bindingToSizeMap[textures[i]];
+ bindingToSizeMap.erase(textures[i]);
+ }
+ }
+ lock.unlock();
+ glDeleteTextures(n, textures);
+}
+
+void mbx_glTexImage2D(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const GLvoid * data) {
+ std::unique_lock<std::mutex> lock(gDebugMutex);
+ if (internalformat == GL_RGBA &&
+ type == GL_UNSIGNED_BYTE) {
+ if (bindingToSizeMap.find(currentBoundTexture) != bindingToSizeMap.end()) {
+ currentUsedBytes -= bindingToSizeMap[currentBoundTexture];
+ std::cout << "GL glTexImage2D: " << currentBoundTexture << " freeing " << bindingToSizeMap[currentBoundTexture] << " bytes current total " << currentUsedBytes << "\n";
+ }
+ bindingToSizeMap[currentBoundTexture] = width * height * 4;
+ currentUsedBytes += bindingToSizeMap[currentBoundTexture];
+ std::cout << "GL glTexImage2D: " << currentBoundTexture << " freeing " << bindingToSizeMap[currentBoundTexture] << " bytes current total " << currentUsedBytes << "\n";
+ }
+ lock.unlock();
+ glTexImage2D(target, level, internalformat, width, height, border, format, type, data);
+}
+#endif
+
diff --git a/src/mbgl/gl/gl_config.cpp b/src/mbgl/gl/gl_config.cpp
new file mode 100644
index 0000000000..4160ae100e
--- /dev/null
+++ b/src/mbgl/gl/gl_config.cpp
@@ -0,0 +1,24 @@
+#include "gl_config.hpp"
+
+namespace mbgl {
+namespace gl {
+
+const StencilFunc::Type StencilFunc::Default = { GL_ALWAYS, 0, ~0u };
+const StencilMask::Type StencilMask::Default = ~0u;
+const StencilTest::Type StencilTest::Default = GL_FALSE;
+const StencilOp::Type StencilOp::Default = { GL_KEEP, GL_KEEP, GL_REPLACE };
+const DepthRange::Type DepthRange::Default = { 0, 1 };
+const DepthMask::Type DepthMask::Default = GL_TRUE;
+const DepthTest::Type DepthTest::Default = GL_FALSE;
+const DepthFunc::Type DepthFunc::Default = GL_LEQUAL;
+const Blend::Type Blend::Default = GL_TRUE;
+const BlendFunc::Type BlendFunc::Default = { GL_ONE, GL_ONE_MINUS_SRC_ALPHA };
+const ColorMask::Type ColorMask::Default = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
+const ClearDepth::Type ClearDepth::Default = 1;
+const ClearColor::Type ClearColor::Default = { 0, 0, 0, 0 };
+const ClearStencil::Type ClearStencil::Default = 0;
+const Program::Type Program::Default = 0;
+const LineWidth::Type LineWidth::Default = 1;
+
+} // namespace gl
+} // namespace mbgl
diff --git a/src/mbgl/gl/gl_config.hpp b/src/mbgl/gl/gl_config.hpp
new file mode 100644
index 0000000000..af373fc3f8
--- /dev/null
+++ b/src/mbgl/gl/gl_config.hpp
@@ -0,0 +1,100 @@
+#ifndef MBGL_GL_GL_CONFIG
+#define MBGL_GL_GL_CONFIG
+
+#include <cstdint>
+#include <tuple>
+#include <array>
+
+#include <mbgl/gl/gl_values.hpp>
+
+namespace mbgl {
+namespace gl {
+
+template <typename T>
+class Value {
+public:
+ inline void operator=(const typename T::Type& value) {
+ if (dirty || current != value) {
+ dirty = false;
+ current = value;
+ T::Set(current);
+ }
+ }
+
+ inline void reset() {
+ dirty = true;
+ current = T::Default;
+ T::Set(current);
+ }
+
+ inline void setDirty() {
+ dirty = true;
+ }
+
+private:
+ typename T::Type current = T::Default;
+ bool dirty = false;
+};
+
+class Config {
+public:
+ void reset() {
+ stencilFunc.reset();
+ stencilMask.reset();
+ stencilTest.reset();
+ stencilOp.reset();
+ depthRange.reset();
+ depthMask.reset();
+ depthTest.reset();
+ depthFunc.reset();
+ blend.reset();
+ blendFunc.reset();
+ colorMask.reset();
+ clearDepth.reset();
+ clearColor.reset();
+ clearStencil.reset();
+ program.reset();
+ lineWidth.reset();
+ }
+
+ void setDirty() {
+ stencilFunc.setDirty();
+ stencilMask.setDirty();
+ stencilTest.setDirty();
+ stencilOp.setDirty();
+ depthRange.setDirty();
+ depthMask.setDirty();
+ depthTest.setDirty();
+ depthFunc.setDirty();
+ blend.setDirty();
+ blendFunc.setDirty();
+ colorMask.setDirty();
+ clearDepth.setDirty();
+ clearColor.setDirty();
+ clearStencil.setDirty();
+ program.setDirty();
+ lineWidth.setDirty();
+ }
+
+ Value<StencilFunc> stencilFunc;
+ Value<StencilMask> stencilMask;
+ Value<StencilTest> stencilTest;
+ Value<StencilOp> stencilOp;
+ Value<DepthRange> depthRange;
+ Value<DepthMask> depthMask;
+ Value<DepthTest> depthTest;
+ Value<DepthFunc> depthFunc;
+ Value<Blend> blend;
+ Value<BlendFunc> blendFunc;
+ Value<ColorMask> colorMask;
+ Value<ClearDepth> clearDepth;
+ Value<ClearColor> clearColor;
+ Value<ClearStencil> clearStencil;
+ Value<Program> program;
+ Value<LineWidth> lineWidth;
+};
+
+} // namespace gl
+} // namespace mbgl
+
+#endif // MBGL_RENDERER_GL_CONFIG
diff --git a/src/mbgl/gl/gl_object_store.cpp b/src/mbgl/gl/gl_object_store.cpp
new file mode 100644
index 0000000000..4948e20694
--- /dev/null
+++ b/src/mbgl/gl/gl_object_store.cpp
@@ -0,0 +1,120 @@
+#include <mbgl/gl/gl_object_store.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+namespace gl {
+
+void ProgramHolder::create(GLObjectStore& objectStore_) {
+ if (id) return;
+ objectStore = &objectStore_;
+ id = MBGL_CHECK_ERROR(glCreateProgram());
+}
+
+void ProgramHolder::reset() {
+ if (!id) return;
+ objectStore->abandonedPrograms.push_back(id);
+ id = 0;
+}
+
+void ShaderHolder::create(GLObjectStore& objectStore_) {
+ if (id) return;
+ objectStore = &objectStore_;
+ id = MBGL_CHECK_ERROR(glCreateShader(type));
+}
+
+void ShaderHolder::reset() {
+ if (!id) return;
+ objectStore->abandonedShaders.push_back(id);
+ id = 0;
+}
+
+void BufferHolder::create(GLObjectStore& objectStore_) {
+ if (id) return;
+ objectStore = &objectStore_;
+ MBGL_CHECK_ERROR(glGenBuffers(1, &id));
+}
+
+void BufferHolder::reset() {
+ if (!id) return;
+ objectStore->abandonedBuffers.push_back(id);
+ id = 0;
+}
+
+void TextureHolder::create(GLObjectStore& objectStore_) {
+ if (id) return;
+ objectStore = &objectStore_;
+ MBGL_CHECK_ERROR(glGenTextures(1, &id));
+}
+
+void TextureHolder::reset() {
+ if (!id) return;
+ objectStore->abandonedTextures.push_back(id);
+ id = 0;
+}
+
+void TexturePoolHolder::create(GLObjectStore& objectStore_) {
+ if (bool()) return;
+ objectStore = &objectStore_;
+ MBGL_CHECK_ERROR(glGenTextures(TextureMax, ids.data()));
+}
+
+void TexturePoolHolder::reset() {
+ if (!bool()) return;
+ for (GLuint id : ids) {
+ if (id) {
+ objectStore->abandonedTextures.push_back(id);
+ }
+ }
+ ids.fill(0);
+}
+
+void VAOHolder::create(GLObjectStore& objectStore_) {
+ if (id) return;
+ objectStore = &objectStore_;
+ MBGL_CHECK_ERROR(gl::GenVertexArrays(1, &id));
+}
+
+void VAOHolder::reset() {
+ if (!id) return;
+ objectStore->abandonedVAOs.push_back(id);
+ id = 0;
+}
+
+GLObjectStore::~GLObjectStore() {
+ assert(abandonedPrograms.empty());
+ assert(abandonedShaders.empty());
+ assert(abandonedBuffers.empty());
+ assert(abandonedTextures.empty());
+ assert(abandonedVAOs.empty());
+}
+
+void GLObjectStore::performCleanup() {
+ for (GLuint id : abandonedPrograms) {
+ MBGL_CHECK_ERROR(glDeleteProgram(id));
+ }
+ abandonedPrograms.clear();
+
+ for (GLuint id : abandonedShaders) {
+ MBGL_CHECK_ERROR(glDeleteShader(id));
+ }
+ abandonedShaders.clear();
+
+ if (!abandonedBuffers.empty()) {
+ MBGL_CHECK_ERROR(glDeleteBuffers(int(abandonedBuffers.size()), abandonedBuffers.data()));
+ abandonedBuffers.clear();
+ }
+
+ if (!abandonedTextures.empty()) {
+ MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data()));
+ abandonedTextures.clear();
+ }
+
+ if (!abandonedVAOs.empty()) {
+ MBGL_CHECK_ERROR(gl::DeleteVertexArrays(int(abandonedVAOs.size()), abandonedVAOs.data()));
+ abandonedVAOs.clear();
+ }
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/src/mbgl/gl/gl_object_store.hpp b/src/mbgl/gl/gl_object_store.hpp
new file mode 100644
index 0000000000..832f1d09b3
--- /dev/null
+++ b/src/mbgl/gl/gl_object_store.hpp
@@ -0,0 +1,141 @@
+#ifndef MBGL_MAP_UTIL_GL_OBJECT_STORE
+#define MBGL_MAP_UTIL_GL_OBJECT_STORE
+
+#include <mbgl/gl/gl.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <array>
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace gl {
+
+class GLObjectStore : private util::noncopyable {
+public:
+ ~GLObjectStore();
+
+ // 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();
+
+private:
+ friend class ProgramHolder;
+ friend class ShaderHolder;
+ friend class BufferHolder;
+ friend class TextureHolder;
+ friend class TexturePoolHolder;
+ friend class VAOHolder;
+
+ std::vector<GLuint> abandonedPrograms;
+ std::vector<GLuint> abandonedShaders;
+ std::vector<GLuint> abandonedBuffers;
+ std::vector<GLuint> abandonedTextures;
+ std::vector<GLuint> abandonedVAOs;
+};
+
+class GLHolder : private util::noncopyable {
+public:
+ GLHolder() {}
+
+ GLHolder(GLHolder&& o) noexcept : id(o.id) { o.id = 0; }
+ GLHolder& operator=(GLHolder&& o) noexcept { id = o.id; o.id = 0; return *this; }
+
+ explicit operator bool() const { return id; }
+ GLuint getID() const { return id; }
+
+protected:
+ GLuint id = 0;
+ GLObjectStore* objectStore = nullptr;
+};
+
+class ProgramHolder : public GLHolder {
+public:
+ ProgramHolder() = default;
+ ~ProgramHolder() { reset(); }
+
+ ProgramHolder(ProgramHolder&& o) noexcept : GLHolder(std::move(o)) {}
+ ProgramHolder& operator=(ProgramHolder&& o) noexcept { GLHolder::operator=(std::move(o)); return *this; }
+
+ void create(GLObjectStore&);
+ void reset();
+};
+
+class ShaderHolder : public GLHolder {
+public:
+ ShaderHolder(GLenum type_) : type(type_) {}
+ ~ShaderHolder() { reset(); }
+
+ ShaderHolder(ShaderHolder&& o) noexcept : GLHolder(std::move(o)), type(o.type) {}
+ ShaderHolder& operator=(ShaderHolder&& o) noexcept { GLHolder::operator=(std::move(o)); type = o.type; return *this; }
+
+ void create(GLObjectStore&);
+ void reset();
+
+private:
+ GLenum type = 0;
+};
+
+class BufferHolder : public GLHolder {
+public:
+ BufferHolder() = default;
+ ~BufferHolder() { reset(); }
+
+ BufferHolder(BufferHolder&& o) noexcept : GLHolder(std::move(o)) {}
+ BufferHolder& operator=(BufferHolder&& o) noexcept { GLHolder::operator=(std::move(o)); return *this; }
+
+ void create(GLObjectStore&);
+ void reset();
+};
+
+class TextureHolder : public GLHolder {
+public:
+ TextureHolder() = default;
+ ~TextureHolder() { reset(); }
+
+ TextureHolder(TextureHolder&& o) noexcept : GLHolder(std::move(o)) {}
+ TextureHolder& operator=(TextureHolder&& o) noexcept { GLHolder::operator=(std::move(o)); return *this; }
+
+ void create(GLObjectStore&);
+ void reset();
+};
+
+class TexturePoolHolder : private util::noncopyable {
+public:
+ static const GLsizei TextureMax = 64;
+
+ TexturePoolHolder() { ids.fill(0); }
+ ~TexturePoolHolder() { reset(); }
+
+ TexturePoolHolder(TexturePoolHolder&& o) noexcept : ids(std::move(o.ids)) {}
+ TexturePoolHolder& operator=(TexturePoolHolder&& o) noexcept { ids = std::move(o.ids); return *this; }
+
+ explicit operator bool() { return std::none_of(ids.begin(), ids.end(), [](int id) { return id == 0; }); }
+ const std::array<GLuint, TextureMax>& getIDs() const { return ids; }
+ const GLuint& operator[](size_t pos) { return ids[pos]; }
+
+ void create(GLObjectStore&);
+ void reset();
+
+private:
+ std::array<GLuint, TextureMax> ids;
+ GLObjectStore* objectStore = nullptr;
+};
+
+class VAOHolder : public GLHolder {
+public:
+ VAOHolder() = default;
+ ~VAOHolder() { reset(); }
+
+ VAOHolder(VAOHolder&& o) noexcept : GLHolder(std::move(o)) {}
+ VAOHolder& operator=(VAOHolder&& o) noexcept { GLHolder::operator=(std::move(o)); return *this; }
+
+ void create(GLObjectStore&);
+ void reset();
+};
+
+} // namespace gl
+} // namespace mbgl
+
+#endif
diff --git a/src/mbgl/gl/texture_pool.cpp b/src/mbgl/gl/texture_pool.cpp
new file mode 100644
index 0000000000..a875f4d579
--- /dev/null
+++ b/src/mbgl/gl/texture_pool.cpp
@@ -0,0 +1,41 @@
+#include <mbgl/gl/texture_pool.hpp>
+#include <mbgl/gl/gl_object_store.hpp>
+
+#include <vector>
+
+namespace mbgl {
+namespace gl {
+
+GLuint TexturePool::getTextureID(gl::GLObjectStore& glObjectStore) {
+ for (auto& impl : pools) {
+ if (impl.ids.empty()) continue;
+ auto it = impl.ids.begin();
+ GLuint id = *it;
+ impl.ids.erase(it);
+ return id;
+ }
+
+ // All texture IDs are in use.
+ pools.emplace_back(Impl(glObjectStore));
+ auto it = pools.back().ids.begin();
+ GLuint id = *it;
+ pools.back().ids.erase(it);
+ return id;
+}
+
+void TexturePool::releaseTextureID(GLuint id) {
+ for (auto it = pools.begin(); it != pools.end(); ++it) {
+ for (GLsizei i = 0; i < gl::TexturePoolHolder::TextureMax; ++i) {
+ if (it->pool[i] == id) {
+ it->ids.push_back(id);
+ if (GLsizei(it->ids.size()) == gl::TexturePoolHolder::TextureMax) {
+ pools.erase(it);
+ }
+ return;
+ }
+ }
+ }
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/src/mbgl/gl/texture_pool.hpp b/src/mbgl/gl/texture_pool.hpp
new file mode 100644
index 0000000000..10f63bfac9
--- /dev/null
+++ b/src/mbgl/gl/texture_pool.hpp
@@ -0,0 +1,41 @@
+#ifndef MBGL_UTIL_TEXTUREPOOL
+#define MBGL_UTIL_TEXTUREPOOL
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/gl/gl.hpp>
+#include <mbgl/gl/gl_object_store.hpp>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace gl {
+
+class TexturePool : private util::noncopyable {
+public:
+ GLuint getTextureID(gl::GLObjectStore&);
+ void releaseTextureID(GLuint);
+
+private:
+ class Impl : private util::noncopyable {
+ public:
+ Impl(gl::GLObjectStore& glObjectStore) : ids(gl::TexturePoolHolder::TextureMax) {
+ pool.create(glObjectStore);
+ std::copy(pool.getIDs().begin(), pool.getIDs().end(), ids.begin());
+ }
+
+ Impl(Impl&& o) : pool(std::move(o.pool)), ids(std::move(o.ids)) {}
+ Impl& operator=(Impl&& o) { pool = std::move(o.pool); ids = std::move(o.ids); return *this; }
+
+ gl::TexturePoolHolder pool;
+ std::vector<GLuint> ids;
+ };
+
+ std::vector<Impl> pools;
+};
+
+} // namespace gl
+} // namespace mbgl
+
+#endif