summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--common/curl_request.cpp383
-rw-r--r--common/glfw_view.cpp9
-rw-r--r--common/glfw_view.hpp1
-rw-r--r--common/glx.h2
-rw-r--r--common/headless_display.cpp69
-rw-r--r--common/headless_display.hpp25
-rw-r--r--common/headless_view.cpp139
-rw-r--r--common/headless_view.hpp26
-rw-r--r--include/mbgl/map/map.hpp11
-rw-r--r--include/mbgl/map/view.hpp4
-rw-r--r--include/mbgl/renderer/painter.hpp2
-rw-r--r--include/mbgl/style/class_dictionary.hpp13
-rw-r--r--include/mbgl/util/uv_detail.hpp13
m---------ios/mapbox-gl-cocoa0
-rwxr-xr-xsetup-libraries.sh25
-rw-r--r--src/map/map.cpp22
-rw-r--r--src/renderer/painter.cpp20
-rw-r--r--src/style/class_dictionary.cpp34
-rw-r--r--src/style/style_layer.cpp2
-rw-r--r--src/style/style_parser.cpp2
-rw-r--r--src/util/uv.cpp2
-rw-r--r--test/fixtures/fixture_request.cpp1
-rw-r--r--test/headless.cpp10
-rw-r--r--test/test.gyp2
25 files changed, 698 insertions, 120 deletions
diff --git a/.travis.yml b/.travis.yml
index 42cca837c6..a2bf164c62 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,6 +22,7 @@ env:
- secure: "bG4YYWMfl9API0MSRgmOaJrlGLv06tRg9KJNawBieZvBJbITPpxVGJZT3/l/SEJ+Rl15e2dRex4k+MGQlmT2SqPQxTEYWv1qxNigKPPcla7IWeNmWWqW8uVvFjdglojgBOK2k/xErVQtA4zDfi3mwSXH4DKwquXWsoEKmX2SV7M="
- secure: "Cbvap9ubVKgjPe3hUhI6JGeDZzBXHpOG9RaYKh+SdoIPhKnlJiNOYm1egomi+e4uqJInlFKuVHTw7Ng9Cun6Zm0jIxpkSchv1GpsR7hmB3UGnGed19Dw8121FwuUaktN+4YnbVlsyd+u8EHD3+h58t4eELrLrZolM4rS7DL6caA="
- secure: "RiBIBfVhhaMjU5ksuwJO3shdvG9FpinBjdSv4co9jg9171SR8edNriedHjVKSIeBhSGNmZmX+twS3dJS/By6tl/LKh9sTynA+ZAYYljkE7jn881B/gMrlYvdAA6og5KvkhV1/0iJWlhuZrMTkhpDR200iLgg3EWBhWjltzmDW/I="
+ - AWS_S3_BUCKET=mapbox-gl-testing
before_install:
- source ./scripts/flags.sh
diff --git a/common/curl_request.cpp b/common/curl_request.cpp
new file mode 100644
index 0000000000..3370c0a859
--- /dev/null
+++ b/common/curl_request.cpp
@@ -0,0 +1,383 @@
+
+#include <mbgl/platform/platform.hpp>
+#include <mbgl/platform/request.hpp>
+#include <mbgl/util/uv_detail.hpp>
+#include <mbgl/util/std.hpp>
+
+#include <queue>
+#include <boost/lockfree/queue.hpp>
+
+#include <curl/curl.h>
+
+// This file contains code from http://curl.haxx.se/libcurl/c/multi-uv.html:
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* Example application code using the multi socket interface to download
+ multiple files at once, but instead of using curl_multi_perform and
+ curl_multi_wait, which uses select(), we use libuv.
+ It supports epoll, kqueue, etc. on unixes and fast IO completion ports on
+ Windows, which means, it should be very fast on all platforms..
+
+ Written by Clemens Gruber, based on an outdated example from uvbook and
+ some tests from libuv.
+
+ Requires libuv and (of course) libcurl.
+
+ See http://nikhilm.github.com/uvbook/ for more information on libuv.
+*/
+
+namespace mbgl {
+namespace platform {
+namespace request {
+
+struct curl_context {
+ uv_poll_t poll_handle;
+ curl_socket_t sockfd;
+};
+
+// Handles the request thread + messaging to the thread.
+static uv_thread_t thread;
+static uv::once init_thread_once;
+static uv_loop_t *loop = nullptr;
+static uv_async_t async_add;
+static uv_async_t async_cancel;
+
+// Stores pointers (!) to shared_ptrs. We use shared_ptrs so that request objects don't get
+// auto-destructed while they're in progress. The TileData object retains a weak_ptr to this
+// request, so we have to use a shared_ptr here to ensure that this object stays alive.
+static boost::lockfree::queue<std::shared_ptr<mbgl::platform::Request> *> add_queue(8);
+static boost::lockfree::queue<std::shared_ptr<mbgl::platform::Request> *> cancel_queue(8);
+
+// Used as the CURL timer function to periodically check for socket updates.
+static uv_timer_t timeout;
+
+// CURL multi handle that we use to request multiple URLs at the same time, without having to block
+// and spawn threads.
+static CURLM *curl_multi = nullptr;
+
+// CURL share handles are used for sharing session state (e.g.)
+static uv::mutex curl_share_mutex;
+static CURLSH *curl_share = nullptr;
+
+// A queue that we use for storing resuable CURL easy handles to avoid creating and destroying them
+// all the time.
+static std::queue<CURL *> curl_handle_cache;
+
+
+class CURLRequest : public mbgl::platform::Request {
+public:
+ CURLRequest(const std::string &url,
+ std::function<void(mbgl::platform::Response *)> callback,
+ std::shared_ptr<uv::loop> loop)
+ : Request(url, callback, loop) {}
+
+ CURL *curl = nullptr;
+};
+
+
+// Implementation starts here.
+
+// Locks the CURL share handle
+void curl_share_lock(CURL *, curl_lock_data, curl_lock_access, void *) { curl_share_mutex.lock(); }
+
+// Unlocks the CURL share handle
+void curl_share_unlock(CURL *, curl_lock_data, void *) { curl_share_mutex.unlock(); }
+
+curl_context *create_curl_context(curl_socket_t sockfd) {
+ curl_context *context = new curl_context;
+ context->sockfd = sockfd;
+
+ uv_poll_init_socket(loop, &context->poll_handle, sockfd);
+ context->poll_handle.data = context;
+
+ return context;
+}
+
+void curl_close_cb(uv_handle_t *handle) {
+ curl_context *context = (curl_context *)handle->data;
+ free(context);
+}
+
+void destroy_curl_context(curl_context *context) {
+ uv_close((uv_handle_t *)&context->poll_handle, curl_close_cb);
+}
+
+void remove_curl_handle(CURL *handle) {
+ CURLMcode error = curl_multi_remove_handle(curl_multi, handle);
+ if (error != CURLM_OK) {
+ throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error));
+ }
+
+ curl_easy_reset(handle);
+ curl_handle_cache.push(handle);
+}
+
+void curl_perform(uv_poll_t *req, int /*status*/, int events) {
+ int running_handles;
+ int flags = 0;
+ curl_context *context;
+ CURLMsg *message;
+ int pending;
+
+ uv_timer_stop(&timeout);
+
+ if (events & UV_READABLE)
+ flags |= CURL_CSELECT_IN;
+ if (events & UV_WRITABLE)
+ flags |= CURL_CSELECT_OUT;
+
+ context = (curl_context *)req;
+
+ curl_multi_socket_action(curl_multi, context->sockfd, flags, &running_handles);
+
+ while ((message = curl_multi_info_read(curl_multi, &pending))) {
+ switch (message->msg) {
+ case CURLMSG_DONE: {
+ std::shared_ptr<Request> *req = nullptr;
+ curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&req);
+
+ // Add human-readable error code
+ if (message->data.result != CURLE_OK) {
+ (*req)->res->error_message = curl_easy_strerror(message->data.result);
+ (*req)->res->code = -1;
+ } else {
+ curl_easy_getinfo(message->easy_handle, CURLINFO_RESPONSE_CODE, &(*req)->res->code);
+ }
+
+ // We're currently in the CURL request thread. We're going to schedule a uv_work request
+ // that executes the background function in a threadpool, and tell it to call the
+ // after callback back in the main uv loop.
+ (*req)->complete();
+
+ CURL *handle = message->easy_handle;
+ remove_curl_handle(handle);
+
+ // We're setting this to NULL because there might still be shared_ptrs around that could
+ // be cancelled.
+ ((CURLRequest *)req->get())->curl = nullptr;
+
+ // Delete the shared_ptr pointer we created earlier.
+ delete req;
+ break;
+ }
+
+ default:
+ // This should never happen, because there are no other message types.
+ throw std::runtime_error("CURLMSG returned unknown message type");
+ }
+ }
+}
+
+int handle_socket(CURL * /*easy*/, curl_socket_t s, int action, void * /*userp*/, void *socketp) {
+ curl_context *context = nullptr;
+
+ if (socketp) {
+ context = (curl_context *)socketp;
+ } else if (action != CURL_POLL_REMOVE) {
+ context = create_curl_context(s);
+ }
+
+ if (context) {
+ curl_multi_assign(curl_multi, s, (void *)context);
+ if (action == CURL_POLL_IN || action == CURL_POLL_INOUT) {
+ uv_poll_start(&context->poll_handle, UV_READABLE, curl_perform);
+ }
+ if (action == CURL_POLL_OUT || action == CURL_POLL_INOUT) {
+ uv_poll_start(&context->poll_handle, UV_WRITABLE, curl_perform);
+ }
+ if (action == CURL_POLL_REMOVE && socketp) {
+ uv_poll_stop(&context->poll_handle);
+ destroy_curl_context(context);
+ curl_multi_assign(curl_multi, s, NULL);
+ }
+ }
+
+ return 0;
+}
+
+void on_timeout(uv_timer_t *, int status /*req*/) {
+ int running_handles;
+ CURLMcode error =
+ curl_multi_socket_action(curl_multi, CURL_SOCKET_TIMEOUT, 0, &running_handles);
+ if (error != CURLM_OK) {
+ throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error));
+ }
+}
+
+void start_timeout(CURLM * /*multi*/, long timeout_ms, void * /*userp*/) {
+ if (timeout_ms <= 0) {
+ on_timeout(&timeout, -1);
+ } else {
+ uv_timer_start(&timeout, &on_timeout, timeout_ms, 0);
+ }
+}
+
+// This function is the first function called in the request thread. It sets up the CURL share/multi
+// handles and runs the thread loop.
+void thread_init(void * /*ptr*/) {
+ uv_timer_init(loop, &timeout);
+
+ CURLSHcode share_error;
+ curl_share = curl_share_init();
+
+ share_error = curl_share_setopt(curl_share, CURLSHOPT_LOCKFUNC, curl_share_lock);
+ if (share_error != CURLSHE_OK) {
+ throw std::runtime_error(std::string("CURL share error: ") + curl_share_strerror(share_error));
+ }
+
+ share_error = curl_share_setopt(curl_share, CURLSHOPT_UNLOCKFUNC, curl_share_unlock);
+ if (share_error != CURLSHE_OK) {
+ throw std::runtime_error(std::string("CURL share error: ") + curl_share_strerror(share_error));
+ }
+
+ CURLMcode multi_error;
+ curl_multi = curl_multi_init();
+
+ multi_error = curl_multi_setopt(curl_multi, CURLMOPT_SOCKETFUNCTION, handle_socket);
+ if (multi_error != CURLM_OK) {
+ throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(multi_error));
+ }
+ multi_error = curl_multi_setopt(curl_multi, CURLMOPT_TIMERFUNCTION, start_timeout);
+ if (multi_error != CURLM_OK) {
+ throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(multi_error));
+
+ }
+
+ // Main event loop. This will not return until the request loop is terminated.
+ uv_run(loop, UV_RUN_DEFAULT);
+
+ curl_multi_cleanup(curl_multi);
+ curl_multi = nullptr;
+ curl_share_cleanup(curl_share);
+ curl_share = nullptr;
+
+ // Clean up all the CURL easy handles that we kept around for potential future reuse.
+ while (!curl_handle_cache.empty()) {
+ curl_easy_cleanup(curl_handle_cache.front());
+ curl_handle_cache.pop();
+ }
+}
+
+// This function is called when we have new data for a request. We just append it to the string
+// containing the previous data.
+size_t curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp) {
+ ((std::string *)userp)->append((char *)contents, size * nmemb);
+ return size * nmemb;
+}
+
+// This callback is called in the request event loop (on the request thread).
+// It initializes newly queued up download requests and adds them to the CURL
+// multi handle.
+void async_add_cb(uv_async_t *, int status /*async*/) {
+ std::shared_ptr<Request> *req = nullptr;
+ while (add_queue.pop(req)) {
+ // Make sure that we're not starting requests that have been cancelled
+ // already by async_cancel_cb.
+ if ((*req)->cancelled) {
+ delete req;
+ continue;
+ }
+
+ // Obtain a curl handle (and try to reuse existing handles before creating new ones).
+ CURL *handle = nullptr;
+ if (!curl_handle_cache.empty()) {
+ handle = curl_handle_cache.front();
+ curl_handle_cache.pop();
+ } else {
+ handle = curl_easy_init();
+ }
+
+ ((CURLRequest *)req->get())->curl = handle;
+
+ curl_easy_setopt(handle, CURLOPT_PRIVATE, req);
+ curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt");
+ curl_easy_setopt(handle, CURLOPT_URL, (*req)->url.c_str());
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, &(*req)->res->body);
+ curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate");
+ curl_easy_setopt(handle, CURLOPT_SHARE, curl_share);
+ curl_multi_add_handle(curl_multi, handle);
+ }
+}
+
+void async_cancel_cb(uv_async_t *, int status /*async*/) {
+ std::shared_ptr<Request> *req = nullptr;
+ while (cancel_queue.pop(req)) {
+ // It is possible that the request has not yet been started, but that it already has been
+ // added to the queue for scheduling new requests. In this case, the CURL handle is invalid
+ // and we manually mark the Request as cancelled.
+ CURL *handle = ((CURLRequest *)req->get())->curl;
+ if (handle && !(*req)->cancelled) {
+ remove_curl_handle(handle);
+ ((CURLRequest *)req->get())->curl = nullptr;
+ }
+ (*req)->cancelled = true;
+
+ delete req;
+ req = nullptr;
+ }
+}
+
+void thread_init_cb() {
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ loop = uv_loop_new();
+ uv_async_init(loop, &async_add, &async_add_cb);
+ uv_async_init(loop, &async_cancel, &async_cancel_cb);
+ uv_thread_create(&thread, thread_init, nullptr);
+}
+} // end namespace request
+} // end namespace platform
+
+
+std::shared_ptr<platform::Request>
+platform::request_http(const std::string &url,
+ std::function<void(Response *)> callback,
+ std::shared_ptr<uv::loop> loop) {
+ using namespace request;
+ init_thread_once(thread_init_cb);
+ std::shared_ptr<CURLRequest> req = std::make_shared<CURLRequest>(url, callback, loop);
+
+ // Note that we are creating a new shared_ptr pointer(!) because the lockless queue can't store
+ // objects with nontrivial destructors. We have to make absolutely sure that we manually delete
+ // the shared_ptr when we pop it from the queue.
+ add_queue.push(new std::shared_ptr<Request>(req));
+ uv_async_send(&async_add);
+
+ return req;
+}
+
+// Cancels an HTTP request.
+void platform::cancel_request_http(const std::shared_ptr<Request> &req) {
+ if (req) {
+ using namespace request;
+
+ // Note that we are creating a new shared_ptr pointer(!) because the lockless queue can't
+ // store objects with nontrivial destructors. We have to make absolutely shure that we
+ // manually delete the shared_ptr when we pop it from the queue.
+ cancel_queue.push(new std::shared_ptr<Request>(req));
+ uv_async_send(&async_cancel);
+ }
+}
+} // end namespace mbgl
diff --git a/common/glfw_view.cpp b/common/glfw_view.cpp
index edfb3a6a6f..120faf4df1 100644
--- a/common/glfw_view.cpp
+++ b/common/glfw_view.cpp
@@ -8,7 +8,10 @@ GLFWView::GLFWView(bool fullscreen) : fullscreen(fullscreen) {
#endif
}
-GLFWView::~GLFWView() { glfwTerminate(); }
+GLFWView::~GLFWView() {
+ map->terminate();
+ glfwTerminate();
+}
void GLFWView::initialize(mbgl::Map *map) {
View::initialize(map);
@@ -194,6 +197,10 @@ void GLFWView::make_active() {
glfwMakeContextCurrent(window);
}
+void GLFWView::make_inactive() {
+ glfwMakeContextCurrent(nullptr);
+}
+
void GLFWView::notify() {
glfwPostEmptyEvent();
}
diff --git a/common/glfw_view.hpp b/common/glfw_view.hpp
index 481b1598bb..6e91c1125e 100644
--- a/common/glfw_view.hpp
+++ b/common/glfw_view.hpp
@@ -17,6 +17,7 @@ public:
void initialize(mbgl::Map *map);
void swap();
void make_active();
+ void make_inactive();
void notify();
void notify_map_change(mbgl::MapChange change, mbgl::timestamp delay = 0);
diff --git a/common/glx.h b/common/glx.h
new file mode 100644
index 0000000000..6b7d9a3df9
--- /dev/null
+++ b/common/glx.h
@@ -0,0 +1,2 @@
+#include <GL/glx.h>
+#undef None
diff --git a/common/headless_display.cpp b/common/headless_display.cpp
new file mode 100644
index 0000000000..bbb1c10f51
--- /dev/null
+++ b/common/headless_display.cpp
@@ -0,0 +1,69 @@
+#include "headless_display.hpp"
+
+#include <stdexcept>
+
+namespace mbgl {
+
+HeadlessDisplay::HeadlessDisplay() {
+#if MBGL_USE_CGL
+ // TODO: test if OpenGL 4.1 with GL_ARB_ES2_compatibility is supported
+ // If it is, use kCGLOGLPVersion_3_2_Core and enable that extension.
+ CGLPixelFormatAttribute attributes[] = {
+ kCGLPFAOpenGLProfile,
+ (CGLPixelFormatAttribute) kCGLOGLPVersion_Legacy,
+ kCGLPFAAccelerated,
+ (CGLPixelFormatAttribute) 0
+ };
+
+ GLint num;
+ CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num);
+ if (error) {
+ fprintf(stderr, "Error pixel format: %s\n", CGLErrorString(error));
+ return;
+ }
+#endif
+
+#if MBGL_USE_GLX
+ if (!XInitThreads()) {
+ throw std::runtime_error("Failed to XInitThreads");
+ }
+
+ x_display = XOpenDisplay(0);
+
+ if (x_display == nullptr) {
+ throw std::runtime_error("Failed to open X display");
+ }
+
+ static int pixelFormat[] = {
+ GLX_RGBA,
+ GLX_DOUBLEBUFFER,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_DEPTH_SIZE, 24,
+ GLX_STENCIL_SIZE, 8,
+ 0
+ };
+
+ x_info = glXChooseVisual(x_display, DefaultScreen(x_display), pixelFormat);
+
+ if (x_info == nullptr) {
+ throw std::runtime_error("Error pixel format");
+ }
+#endif
+}
+
+HeadlessDisplay::~HeadlessDisplay() {
+#if MBGL_USE_CGL
+ CGLDestroyPixelFormat(pixelFormat);
+#endif
+
+#if MBGL_USE_GLX
+ XFree(x_info);
+ XCloseDisplay(x_display);
+#endif
+}
+
+}
+
diff --git a/common/headless_display.hpp b/common/headless_display.hpp
new file mode 100644
index 0000000000..0eb41911ee
--- /dev/null
+++ b/common/headless_display.hpp
@@ -0,0 +1,25 @@
+#ifndef MBGL_COMMON_HEADLESS_DISPLAY
+#define MBGL_COMMON_HEADLESS_DISPLAY
+
+#include "headless_view.hpp"
+
+namespace mbgl {
+
+class HeadlessDisplay {
+public:
+ HeadlessDisplay();
+ ~HeadlessDisplay();
+
+#if MBGL_USE_CGL
+ CGLPixelFormatObj pixelFormat;
+#endif
+
+#if MBGL_USE_GLX
+ Display *x_display = nullptr;
+ XVisualInfo *x_info = nullptr;
+#endif
+};
+
+}
+
+#endif
diff --git a/common/headless_view.cpp b/common/headless_view.cpp
index ace41d38c0..c2084ac90d 100644
--- a/common/headless_view.cpp
+++ b/common/headless_view.cpp
@@ -1,61 +1,38 @@
#include "headless_view.hpp"
-#include <mbgl/util/timer.hpp>
+#include "headless_display.hpp"
#include <stdexcept>
+#include <sstream>
+#include <string>
namespace mbgl {
-HeadlessView::HeadlessView() {
+HeadlessView::HeadlessView()
+ : display_(std::make_shared<HeadlessDisplay>()) {
+ createContext();
+}
+
+HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display)
+ : display_(display) {
+ createContext();
+}
+
+void HeadlessView::createContext() {
#if MBGL_USE_CGL
- // TODO: test if OpenGL 4.1 with GL_ARB_ES2_compatibility is supported
- // If it is, use kCGLOGLPVersion_3_2_Core and enable that extension.
- CGLPixelFormatAttribute attributes[] = {
- kCGLPFAOpenGLProfile,
- (CGLPixelFormatAttribute) kCGLOGLPVersion_Legacy,
- kCGLPFAAccelerated,
- (CGLPixelFormatAttribute) 0
- };
-
- CGLPixelFormatObj pixelFormat;
- GLint num;
- CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num);
+ CGLError error = CGLCreateContext(display_->pixelFormat, NULL, &gl_context);
if (error) {
- fprintf(stderr, "Error pixel format\n");
- return;
+ throw std::runtime_error("Error creating GL context object\n");
}
- error = CGLCreateContext(pixelFormat, NULL, &gl_context);
- CGLDestroyPixelFormat(pixelFormat);
- if (error) {
- fprintf(stderr, "Error creating GL context object\n");
- return;
+ error = CGLEnable(gl_context, kCGLCEMPEngine);
+ if (error != kCGLNoError ) {
+ throw std::runtime_error("Error enabling OpenGL multithreading\n");
}
#endif
#if MBGL_USE_GLX
- x_display = XOpenDisplay(0);
-
- if (x_display == nullptr) {
- throw std::runtime_error("Failed to open X display");
- }
-
- static int pixelFormat[] = {
- GLX_RGBA,
- GLX_DOUBLEBUFFER,
- GLX_RED_SIZE, 8,
- GLX_GREEN_SIZE, 8,
- GLX_BLUE_SIZE, 8,
- GLX_ALPHA_SIZE, 8,
- GLX_DEPTH_SIZE, 24,
- GLX_STENCIL_SIZE, 8,
- None
- };
-
- x_info = glXChooseVisual(x_display, DefaultScreen(x_display), pixelFormat);
-
- if (x_info == nullptr) {
- throw std::runtime_error("Error pixel format");
- }
+ x_display = display_->x_display;
+ x_info = display_->x_info;
gl_context = glXCreateContext(x_display, x_info, 0, GL_TRUE);
if (gl_context == nullptr) {
@@ -64,12 +41,15 @@ HeadlessView::HeadlessView() {
#endif
}
-
void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) {
clear_buffers();
- width *= pixelRatio;
- height *= pixelRatio;
+ width_ = width;
+ height_ = height;
+ pixelRatio_ = pixelRatio;
+
+ const unsigned int w = width_ * pixelRatio_;
+ const unsigned int h = height_ * pixelRatio_;
#if MBGL_USE_CGL
make_active();
@@ -77,12 +57,12 @@ void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) {
// Create depth/stencil buffer
glGenRenderbuffersEXT(1, &fbo_depth_stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo_depth_stencil);
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, width, height);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glGenRenderbuffersEXT(1, &fbo_color);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo_color);
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, width, height);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glGenFramebuffersEXT(1, &fbo);
@@ -94,29 +74,46 @@ void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) {
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
- fprintf(stderr, "Couldn't create framebuffer: ");
+ std::stringstream error("Couldn't create framebuffer: ");
switch (status) {
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: fprintf(stderr, "incomplete attachment\n"); break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: fprintf(stderr, "incomplete missing attachment\n"); break;
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: fprintf(stderr, "incomplete draw buffer\n"); break;
- case GL_FRAMEBUFFER_UNSUPPORTED: fprintf(stderr, "unsupported\n"); break;
- default: fprintf(stderr, "other\n"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error << "incomplete attachment\n"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error << "incomplete missing attachment\n"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error << "incomplete dimensions\n"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error << "incomplete formats\n"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error << "incomplete draw buffer\n"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error << "incomplete read buffer\n"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: error << "unsupported\n"; break;
+ default: error << "other\n"; break;
}
- return;
+ throw std::runtime_error(error.str());
}
+
+ make_inactive();
#endif
#if MBGL_USE_GLX
- x_pixmap = XCreatePixmap(x_display, DefaultRootWindow(x_display), width, height, 32);
+ x_pixmap = XCreatePixmap(x_display, DefaultRootWindow(x_display), w, h, 32);
glx_pixmap = glXCreateGLXPixmap(x_display, x_info, x_pixmap);
+#endif
+}
+
+const std::unique_ptr<uint32_t[]> HeadlessView::readPixels() {
+ const unsigned int w = width_ * pixelRatio_;
+ const unsigned int h = height_ * pixelRatio_;
+
+ std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]);
make_active();
-#endif
+ glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
+ make_inactive();
+ return pixels;
}
void HeadlessView::clear_buffers() {
#if MBGL_USE_CGL
+ make_active();
+
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
if (fbo) {
@@ -133,6 +130,8 @@ void HeadlessView::clear_buffers() {
glDeleteRenderbuffersEXT(1, &fbo_depth_stencil);
fbo_depth_stencil = 0;
}
+
+ make_inactive();
#endif
#if MBGL_USE_GLX
@@ -145,6 +144,8 @@ void HeadlessView::clear_buffers() {
XFreePixmap(x_display, x_pixmap);
x_pixmap = 0;
}
+
+ make_inactive();
#endif
}
@@ -156,10 +157,7 @@ HeadlessView::~HeadlessView() {
#endif
#if MBGL_USE_GLX
- glXMakeCurrent(x_display, None, NULL);
glXDestroyContext(x_display, gl_context);
- XFree(x_info);
- XCloseDisplay(x_display);
#endif
}
@@ -175,13 +173,28 @@ void HeadlessView::make_active() {
#if MBGL_USE_CGL
CGLError error = CGLSetCurrentContext(gl_context);
if (error) {
- fprintf(stderr, "Switching OpenGL context failed\n");
+ throw std::runtime_error("Switching OpenGL context failed\n");
}
#endif
#if MBGL_USE_GLX
if (!glXMakeCurrent(x_display, glx_pixmap, gl_context)) {
- fprintf(stderr, "Switching OpenGL context failed\n");
+ throw std::runtime_error("Switching OpenGL context failed\n");
+ }
+#endif
+}
+
+void HeadlessView::make_inactive() {
+#if MBGL_USE_CGL
+ CGLError error = CGLSetCurrentContext(nullptr);
+ if (error) {
+ throw std::runtime_error("Removing OpenGL context failed\n");
+ }
+#endif
+
+#if MBGL_USE_GLX
+ if (!glXMakeCurrent(x_display, 0, NULL)) {
+ throw std::runtime_error("Removing OpenGL context failed\n");
}
#endif
}
diff --git a/common/headless_view.hpp b/common/headless_view.hpp
index 42f9c46da2..d2fe75382a 100644
--- a/common/headless_view.hpp
+++ b/common/headless_view.hpp
@@ -1,37 +1,49 @@
-#ifndef MBGL_COMMON_HEADLESS_CGL
-#define MBGL_COMMON_HEADLESS_CGL
+#ifndef MBGL_COMMON_HEADLESS_VIEW
+#define MBGL_COMMON_HEADLESS_VIEW
#ifdef __APPLE__
#define MBGL_USE_CGL 1
#else
-#include <GL/glx.h>
+#include "glx.h"
#define MBGL_USE_GLX 1
#endif
#include <mbgl/map/view.hpp>
#include <mbgl/platform/gl.hpp>
-#include <mbgl/util/time.hpp>
+
+#include <memory>
namespace mbgl {
+class HeadlessDisplay;
+
class HeadlessView : public View {
public:
HeadlessView();
+ HeadlessView(std::shared_ptr<HeadlessDisplay> display);
~HeadlessView();
+ void createContext();
+
void resize(uint16_t width, uint16_t height, float pixelRatio);
+ const std::unique_ptr<uint32_t[]> readPixels();
void notify();
void notify_map_change(MapChange change, timestamp delay = 0);
void make_active();
+ void make_inactive();
void swap();
unsigned int root_fbo();
private:
void clear_buffers();
-
private:
+ std::shared_ptr<HeadlessDisplay> display_;
+ uint16_t width_;
+ uint16_t height_;
+ float pixelRatio_;
+
#if MBGL_USE_CGL
CGLContextObj gl_context;
GLuint fbo = 0;
@@ -40,9 +52,9 @@ private:
#endif
#if MBGL_USE_GLX
- GLXContext gl_context = nullptr;
- XVisualInfo *x_info = nullptr;
Display *x_display = nullptr;
+ XVisualInfo *x_info = nullptr;
+ GLXContext gl_context = nullptr;
Pixmap x_pixmap = 0;
GLXPixmap glx_pixmap = 0;
#endif
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index a69943aa74..7e4687ea6f 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -63,6 +63,9 @@ public:
// Triggers a cleanup that releases resources.
void cleanup();
+ // Releases resources immediately
+ void terminate();
+
// Controls buffer swapping.
bool needsSwap();
void swapped();
@@ -143,10 +146,10 @@ public:
private:
// uv async callbacks
- static void render(uv_async_t *async);
- static void terminate(uv_async_t *async);
- static void cleanup(uv_async_t *async);
- static void delete_async(uv_handle_t *handle);
+ static void render(uv_async_t *async, int status);
+ static void terminate(uv_async_t *async, int status);
+ static void cleanup(uv_async_t *async, int status);
+ static void delete_async(uv_handle_t *handle, int status);
// Setup
void setup();
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
index bbdcd97c79..395a05d435 100644
--- a/include/mbgl/map/view.hpp
+++ b/include/mbgl/map/view.hpp
@@ -35,6 +35,10 @@ public:
// renderer setup since the render thread doesn't switch the contexts.
virtual void make_active() = 0;
+ // Called from the render thread. Makes the GL context inactive in the current
+ // thread. This is called once just before the rendering thread terminates.
+ virtual void make_inactive() = 0;
+
// Returns the base framebuffer object, if any, and 0 if using the system
// provided framebuffer.
virtual unsigned int root_fbo() {
diff --git a/include/mbgl/renderer/painter.hpp b/include/mbgl/renderer/painter.hpp
index 13c2050bd0..0f9bd79173 100644
--- a/include/mbgl/renderer/painter.hpp
+++ b/include/mbgl/renderer/painter.hpp
@@ -64,6 +64,7 @@ public:
// lazy initialization) in case rendering continues.
void cleanup();
+ void terminate();
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
// tiles whatsoever.
@@ -124,6 +125,7 @@ public:
private:
void setupShaders();
+ void deleteShaders();
mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchorType anchor);
void prepareTile(const Tile& tile);
diff --git a/include/mbgl/style/class_dictionary.hpp b/include/mbgl/style/class_dictionary.hpp
index c7f9c6a284..ecf80be3e3 100644
--- a/include/mbgl/style/class_dictionary.hpp
+++ b/include/mbgl/style/class_dictionary.hpp
@@ -14,17 +14,22 @@ enum class ClassID : uint32_t {
};
class ClassDictionary {
+private:
+ ClassDictionary();
+
public:
+ static ClassDictionary &Get();
+
// Returns an ID for a class name. If the class name does not yet have an ID, one is
// auto-generated and stored for future reference.
- static ClassID Lookup(const std::string &class_name);
+ ClassID lookup(const std::string &class_name);
// Returns either Fallback, Default or Named, depending on the type of the class id.
- static ClassID Normalize(ClassID id);
+ ClassID normalize(ClassID id);
private:
- static std::unordered_map<std::string, ClassID> store;
- static uint32_t offset;
+ std::unordered_map<std::string, ClassID> store = { { "", ClassID::Default } };
+ uint32_t offset = 0;
};
}
diff --git a/include/mbgl/util/uv_detail.hpp b/include/mbgl/util/uv_detail.hpp
index e0a57ce65d..a80423f822 100644
--- a/include/mbgl/util/uv_detail.hpp
+++ b/include/mbgl/util/uv_detail.hpp
@@ -23,18 +23,13 @@ private:
class loop {
public:
- inline loop() {
- if (uv_loop_init(&l) != 0) {
- throw std::runtime_error("failed to initialize loop");
- }
- }
-
- inline ~loop() { uv_loop_close(&l); }
+ inline loop() : l(uv_loop_new()) {}
+ inline ~loop() { uv_loop_delete(l); }
- inline uv_loop_t *operator*() { return &l; }
+ inline uv_loop_t *operator*() { return l; }
private:
- uv_loop_t l;
+ uv_loop_t *l;
};
class mutex {
diff --git a/ios/mapbox-gl-cocoa b/ios/mapbox-gl-cocoa
-Subproject bc2c5fe6974fa99ea4816e9e793757cc1706659
+Subproject 1aa3db196b210c6880df6b2aff5e0d924aa7876
diff --git a/setup-libraries.sh b/setup-libraries.sh
index e752d055c1..dc1b1643fa 100755
--- a/setup-libraries.sh
+++ b/setup-libraries.sh
@@ -51,7 +51,7 @@ set -u
NODE=$(which node)
NPM=$(which npm)
-MP_HASH="eb8c6d7e6bd6adb42c232ed806b889f3e6825bb5"
+MP_HASH="e82e6c00ea5a0cda147e4c121a3c7751cae69ff8"
DIR_HASH=$(echo `pwd` | git hash-object --stdin)
if [ ! -d 'mapnik-packaging/' ]; then
@@ -68,8 +68,8 @@ export CXX11=true
if [ ${UNAME} = 'Darwin' ]; then
if [[ $TRAVIS ]]; then
- if aws s3 cp s3://mapbox-gl-testing/dependencies/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
- if aws s3 cp s3://mapbox-gl-testing/dependencies/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
+ if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
+ if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
rm -rf out/build-cpp11-libcpp-x86_64-macosx
rm -rf out/build-cpp11-libcpp-universal
tar -xzf out/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz
@@ -81,6 +81,7 @@ fi
if test -z "${TRAVIS:-}" || ! test -d out/build-cpp11-libcpp-universal; then
source iPhoneOS.sh
+export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
@@ -88,6 +89,7 @@ source iPhoneOS.sh
echo ' ...done'
source iPhoneOSs.sh
+export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
@@ -95,6 +97,7 @@ source iPhoneOSs.sh
echo ' ...done'
source iPhoneOS64.sh
+export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
@@ -102,6 +105,7 @@ source iPhoneOS64.sh
echo ' ...done'
source iPhoneSimulator.sh
+export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
@@ -109,6 +113,7 @@ source iPhoneSimulator.sh
echo ' ...done'
source iPhoneSimulator64.sh
+export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
@@ -116,6 +121,7 @@ source iPhoneSimulator64.sh
echo ' ...done'
source MacOSX.sh
+export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libglfw3.a ] ; then ./scripts/build_glfw.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
@@ -132,9 +138,9 @@ echo "NOTE: One patch FAILURE is expected. The other should have been applied or
if [[ $TRAVIS && $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY ]] ; then
tar -zcf out/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libcpp-universal
- aws s3 cp --acl public-read out/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz s3://mapbox-gl-testing/dependencies/
+ aws s3 cp --acl public-read out/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz s3://${AWS_S3_BUCKET}/dependencies/
tar -zcf out/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libcpp-x86_64-macosx
- aws s3 cp --acl public-read out/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz s3://mapbox-gl-testing/dependencies/
+ aws s3 cp --acl public-read out/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz s3://${AWS_S3_BUCKET}/dependencies/
fi
fi
@@ -147,13 +153,14 @@ cd ../../
elif [ ${UNAME} = 'Linux' ]; then
if [[ $TRAVIS ]]; then
- if aws s3 cp s3://mapbox-gl-testing/dependencies/build-cpp11-libstdcpp-gcc-x86_64-linux.tar.gz ./out/ ; then
+ if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
rm -rf out/build-cpp11-libstdcpp-gcc-x86_64-linux
- tar -xzf out/build-cpp11-libstdcpp-gcc-x86_64-linux.tar.gz
+ tar -xzf out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz
fi
fi
source Linux.sh
+export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libglfw3.a ] ; then ./scripts/build_glfw.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
@@ -164,8 +171,8 @@ source Linux.sh
if [[ $TRAVIS && $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY ]] ; then
if ! tar --compare -zf out/build-cpp11-libstdcpp-gcc-x86_64-linux.tar.gz ; then
- tar -zcf out/build-cpp11-libstdcpp-gcc-x86_64-linux.tar.gz out/build-cpp11-libstdcpp-gcc-x86_64-linux
- aws s3 cp --acl public-read out/build-cpp11-libstdcpp-gcc-x86_64-linux.tar.gz s3://mapbox-gl-testing/dependencies/
+ tar -zcf out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libstdcpp-gcc-x86_64-linux
+ aws s3 cp --acl public-read out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz s3://${AWS_S3_BUCKET}/dependencies/
fi
fi
diff --git a/src/map/map.cpp b/src/map/map.cpp
index a1543dbdae..9034c6d5ac 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -145,7 +145,7 @@ void Map::stop(stop_callback cb, void *data) {
async = false;
}
-void Map::delete_async(uv_handle_t *handle) {
+void Map::delete_async(uv_handle_t *handle, int status) {
delete (uv_async_t *)handle;
}
@@ -167,7 +167,6 @@ void Map::run() {
// If the map rendering wasn't started asynchronously, we perform one render
// *after* all events have been processed.
if (!async) {
- prepare();
render();
#ifndef NDEBUG
map_thread = -1;
@@ -203,13 +202,16 @@ void Map::cleanup() {
}
}
-void Map::cleanup(uv_async_t *async) {
+void Map::cleanup(uv_async_t *async, int status) {
Map *map = static_cast<Map *>(async->data);
- map->view.make_active();
map->painter.cleanup();
}
+void Map::terminate() {
+ painter.terminate();
+}
+
void Map::setReachability(bool reachable) {
// Note: This function may be called from *any* thread.
if (reachable) {
@@ -221,7 +223,7 @@ void Map::setReachability(bool reachable) {
}
}
-void Map::render(uv_async_t *async) {
+void Map::render(uv_async_t *async, int status) {
Map *map = static_cast<Map *>(async->data);
assert(uv_thread_self() == map->map_thread);
@@ -241,7 +243,7 @@ void Map::render(uv_async_t *async) {
}
}
-void Map::terminate(uv_async_t *async) {
+void Map::terminate(uv_async_t *async, int status) {
// Closes all open handles on the loop. This means that the loop will automatically terminate.
Map *map = static_cast<Map *>(async->data);
assert(uv_thread_self() == map->map_thread);
@@ -263,8 +265,8 @@ void Map::terminate(uv_async_t *async) {
void Map::setup() {
assert(uv_thread_self() == map_thread);
view.make_active();
-
painter.setup();
+ view.make_inactive();
}
void Map::setStyleURL(const std::string &url) {
@@ -597,8 +599,6 @@ void Map::updateRenderState() {
}
void Map::prepare() {
- view.make_active();
-
if (!fileSource) {
fileSource = std::make_shared<FileSource>(**loop, platform::defaultCacheDatabase());
glyphStore = std::make_shared<GlyphStore>(fileSource);
@@ -643,6 +643,8 @@ void Map::prepare() {
}
void Map::render() {
+ view.make_active();
+
#if defined(DEBUG)
std::vector<std::string> debug;
#endif
@@ -677,6 +679,8 @@ void Map::render() {
}
glFlush();
+
+ view.make_inactive();
}
void Map::renderLayers(util::ptr<StyleLayerGroup> group) {
diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp
index 9643fb1561..8988112585 100644
--- a/src/renderer/painter.cpp
+++ b/src/renderer/painter.cpp
@@ -82,9 +82,29 @@ void Painter::setupShaders() {
if (!gaussianShader) gaussianShader = std::make_unique<GaussianShader>();
}
+void Painter::deleteShaders() {
+ plainShader = nullptr;
+ outlineShader = nullptr;
+ lineShader = nullptr;
+ linejoinShader = nullptr;
+ linepatternShader = nullptr;
+ patternShader = nullptr;
+ iconShader = nullptr;
+ rasterShader = nullptr;
+ sdfGlyphShader = nullptr;
+ sdfIconShader = nullptr;
+ dotShader = nullptr;
+ gaussianShader = nullptr;
+}
+
void Painter::cleanup() {
}
+void Painter::terminate() {
+ cleanup();
+ deleteShaders();
+}
+
void Painter::resize() {
const TransformState &state = map.getState();
if (gl_viewport != state.getFramebufferDimensions()) {
diff --git a/src/style/class_dictionary.cpp b/src/style/class_dictionary.cpp
index 6e1eb5a879..ba7c0d55be 100644
--- a/src/style/class_dictionary.cpp
+++ b/src/style/class_dictionary.cpp
@@ -1,8 +1,34 @@
#include <mbgl/style/class_dictionary.hpp>
+#include <uv.h>
+
namespace mbgl {
-ClassID ClassDictionary::Lookup(const std::string &class_name) {
+ClassDictionary::ClassDictionary() {}
+
+ClassDictionary &ClassDictionary::Get() {
+ // Note: We should eventually switch to uv_key_* functions, but libuv 0.10 doesn't have these
+ // yet. Instead, we're using the pthread functions directly for now.
+ static pthread_once_t store_once = PTHREAD_ONCE_INIT;
+ static pthread_key_t store_key;
+
+ // Create the key.
+ pthread_once(&store_once, []() {
+ pthread_key_create(&store_key, [](void *ptr) {
+ delete reinterpret_cast<ClassDictionary *>(ptr);
+ });
+ });
+
+ ClassDictionary *ptr = reinterpret_cast<ClassDictionary *>(pthread_getspecific(store_key));
+ if (ptr == nullptr) {
+ ptr = new ClassDictionary();
+ pthread_setspecific(store_key, ptr);
+ }
+
+ return *ptr;
+}
+
+ClassID ClassDictionary::lookup(const std::string &class_name) {
auto it = store.find(class_name);
if (it == store.end()) {
// Insert the class name into the store.
@@ -14,7 +40,7 @@ ClassID ClassDictionary::Lookup(const std::string &class_name) {
}
}
-ClassID ClassDictionary::Normalize(ClassID id) {
+ClassID ClassDictionary::normalize(ClassID id) {
if (id >= ClassID::Named) {
return ClassID::Named;
} else {
@@ -22,8 +48,4 @@ ClassID ClassDictionary::Normalize(ClassID id) {
}
}
-
-std::unordered_map<std::string, ClassID> ClassDictionary::store = { { "", ClassID::Default } };
-uint32_t ClassDictionary::offset = 0;
-
}
diff --git a/src/style/style_layer.cpp b/src/style/style_layer.cpp
index 4f758fe723..b1b878cc8d 100644
--- a/src/style/style_layer.cpp
+++ b/src/style/style_layer.cpp
@@ -23,7 +23,7 @@ void StyleLayer::setClasses(const std::vector<std::string> &class_names, const t
for (auto it = class_names.rbegin(); it != class_names.rend(); it++) {
const std::string &class_name = *it;
// From here on, we're only dealing with IDs to avoid comparing strings all the time.
- const ClassID class_id = ClassDictionary::Lookup(class_name);
+ const ClassID class_id = ClassDictionary::Get().lookup(class_name);
applyClassProperties(class_id, already_applied, now, defaultTransition);
}
diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp
index c2247d51b2..2a64ab38f8 100644
--- a/src/style/style_parser.cpp
+++ b/src/style/style_parser.cpp
@@ -554,7 +554,7 @@ void StyleParser::parseStyles(JSVal value, std::map<ClassID, ClassProperties> &s
if (name == "style") {
parseStyle(replaceConstant(itr->value), styles[ClassID::Default]);
} else if (name.compare(0, 6, "style.") == 0 && name.length() > 6) {
- const ClassID class_id = ClassDictionary::Lookup(name.substr(6));
+ const ClassID class_id = ClassDictionary::Get().lookup(name.substr(6));
parseStyle(replaceConstant(itr->value), styles[class_id]);
}
}
diff --git a/src/util/uv.cpp b/src/util/uv.cpp
index b97074f054..6e15ac4537 100644
--- a/src/util/uv.cpp
+++ b/src/util/uv.cpp
@@ -10,7 +10,7 @@ std::string cwd() {
do {
max += 256;
dir.resize(max);
- uv_cwd(const_cast<char *>(dir.data()), &max);
+ uv_cwd(const_cast<char *>(dir.data()), max);
} while (max == dir.size());
dir.resize(max - 1);
return dir;
diff --git a/test/fixtures/fixture_request.cpp b/test/fixtures/fixture_request.cpp
index 3f72b890db..b37cd92bee 100644
--- a/test/fixtures/fixture_request.cpp
+++ b/test/fixtures/fixture_request.cpp
@@ -4,6 +4,7 @@
#include <mbgl/util/url.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/platform/log.hpp>
+#include <iostream>
#include <uv.h>
diff --git a/test/headless.cpp b/test/headless.cpp
index 3cc2607e47..91533bb36b 100644
--- a/test/headless.cpp
+++ b/test/headless.cpp
@@ -10,6 +10,7 @@
#include <rapidjson/stringbuffer.h>
#include "../common/headless_view.hpp"
+#include "../common/headless_display.hpp"
#include "./fixtures/fixture_log.hpp"
@@ -22,6 +23,8 @@ const std::string base_directory = []{
return fn + "/node_modules/mapbox-gl-test-suite/";
}();
+auto display_ = std::make_shared<mbgl::HeadlessDisplay>();
+
class HeadlessTest : public ::testing::TestWithParam<std::string> {};
TEST_P(HeadlessTest, render) {
@@ -78,7 +81,7 @@ TEST_P(HeadlessTest, render) {
}
}
- HeadlessView view;
+ HeadlessView view(display_);
Map map(view);
map.setStyleJSON(style, base_directory);
@@ -95,10 +98,7 @@ TEST_P(HeadlessTest, render) {
const unsigned int w = width * pixelRatio;
const unsigned int h = height * pixelRatio;
- const std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]);
- glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
-
- const std::string image = util::compress_png(w, h, pixels.get(), true);
+ const std::string image = util::compress_png(w, h, view.readPixels().get(), true);
util::write_file(actual_image, image);
}
}
diff --git a/test/test.gyp b/test/test.gyp
index fd7725bb03..1ebaf6cc11 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -201,6 +201,8 @@
"./headless.cpp",
"../common/headless_view.hpp",
"../common/headless_view.cpp",
+ "../common/headless_display.hpp",
+ "../common/headless_display.cpp",
"../common/platform_default.cpp",
"./fixtures/fixture_request.cpp",
"./fixtures/fixture_log.hpp",