diff --git a/Makefile b/Makefile index 70e9dd815..1d03340c8 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ build/macosx/Makefile: macosx/mapboxgl-app.gyp config.gypi .PHONY: build/test/test.xcodeproj build/test/test.xcodeproj: test/test.gyp config.gypi - deps/run_gyp test/test.gyp -Iconfig.gypi -Dplatform=osx --depth=. -Goutput_dir=.. --generator-output=./build -f xcode + deps/run_gyp test/test.gyp -Iconfig.gypi -Dplatform=$(PLATFORM) --depth=. -Goutput_dir=.. --generator-output=./build -f xcode .PHONY: build/macosx/mapboxgl-app.xcodeproj build/macosx/mapboxgl-app.xcodeproj: macosx/mapboxgl-app.gyp config.gypi diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp index 6c6dc72e3..ac8937fb3 100644 --- a/android/mapboxgl-app.gyp +++ b/android/mapboxgl-app.gyp @@ -24,7 +24,12 @@ ], 'variables': { 'ldflags': [ - '-llog -landroid -lEGL -lGLESv2 -lstdc++ -latomic ' + '-llog', + '-landroid', + '-lEGL', + '-lGLESv2', + '-lstdc++', + '-latomic', '<@(png_ldflags)', '<@(sqlite3_ldflags)', '<@(openssl_ldflags)', diff --git a/gyp/common.gypi b/gyp/common.gypi index 97b474b4b..d219ba2f9 100644 --- a/gyp/common.gypi +++ b/gyp/common.gypi @@ -14,7 +14,16 @@ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0', 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'GCC_ENABLE_CPP_RTTI':'YES', - 'OTHER_CPLUSPLUSFLAGS': [ '-Wall', '-Wextra', '-Wshadow', '-Wno-variadic-macros', '-frtti', '-fexceptions' ], + 'OTHER_CPLUSPLUSFLAGS': [ + '-Werror', + '-Wall', + '-Wextra', + '-Wshadow', + '-Wno-variadic-macros', + '-frtti', + '-fexceptions', + '-Wno-error=unused-parameter', + ], 'GCC_WARN_PEDANTIC': 'YES', 'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE', }, @@ -23,7 +32,8 @@ 'cflags_cc': [ '-Wno-unknown-pragmas', # We are using '#pragma mark', but it is only available on Darwin. '-Wno-literal-suffix', # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61653 - '-Wno-unknown-warning-option' + '-Wno-unknown-warning-option', + '-Wno-error=unused-parameter', ], }], ], diff --git a/gyp/mbgl-linux.gypi b/gyp/mbgl-linux.gypi index a22bd8f7c..823ba91c3 100644 --- a/gyp/mbgl-linux.gypi +++ b/gyp/mbgl-linux.gypi @@ -11,6 +11,7 @@ '<@(uv_cflags)', '<@(curl_cflags)', '<@(nu_cflags)', + '-I<(boost_root)/include', ], 'cflags': [ '<@(uv_cflags)', diff --git a/include/mbgl/geometry/vao.hpp b/include/mbgl/geometry/vao.hpp index e6e199b69..2ecba731f 100644 --- a/include/mbgl/geometry/vao.hpp +++ b/include/mbgl/geometry/vao.hpp @@ -9,7 +9,6 @@ namespace mbgl { -#if GL_ARB_vertex_array_object class VertexArrayObject : public util::noncopyable { public: inline VertexArrayObject() {}; @@ -28,7 +27,9 @@ public: if (bound_shader == 0) { vertexBuffer.bind(); shader.bind(offset); - storeBinding(shader, vertexBuffer.getID(), 0, offset); + if (vao) { + storeBinding(shader, vertexBuffer.getID(), 0, offset); + } } else { verifyBinding(shader, vertexBuffer.getID(), 0, offset); } @@ -41,7 +42,9 @@ public: vertexBuffer.bind(); elementsBuffer.bind(); shader.bind(offset); - storeBinding(shader, vertexBuffer.getID(), elementsBuffer.getID(), offset); + if (vao) { + storeBinding(shader, vertexBuffer.getID(), elementsBuffer.getID(), offset); + } } else { verifyBinding(shader, vertexBuffer.getID(), elementsBuffer.getID(), offset); } @@ -65,30 +68,6 @@ private: char *bound_offset = 0; }; -#else - -class VertexArrayObject : public util::noncopyable { -public: - inline VertexArrayObject() {}; - - inline VertexArrayObject(VertexArrayObject &&) noexcept {}; - - template - inline void bind(Shader& shader, VertexBuffer &vertexBuffer, char *offset) { - vertexBuffer.bind(); - shader.bind(offset); - } - - template - inline void bind(Shader& shader, VertexBuffer &vertexBuffer, ElementsBuffer &elementsBuffer, char *offset) { - vertexBuffer.bind(); - elementsBuffer.bind(); - shader.bind(offset); - } -}; - -#endif - } -#endif \ No newline at end of file +#endif diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp index f14033834..f599dde27 100644 --- a/include/mbgl/platform/default/headless_view.hpp +++ b/include/mbgl/platform/default/headless_view.hpp @@ -25,6 +25,7 @@ public: ~HeadlessView(); void createContext(); + void loadExtensions(); void resize(uint16_t width, uint16_t height, float pixelRatio); std::unique_ptr readPixels(); diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp index 3361e55ce..1b3d55eb1 100644 --- a/include/mbgl/platform/event.hpp +++ b/include/mbgl/platform/event.hpp @@ -33,6 +33,7 @@ enum class Event : uint8_t { HttpRequest, Sprite, Image, + OpenGL, }; MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { @@ -46,6 +47,7 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { { Event::HttpRequest, "HttpRequest" }, { Event::Sprite, "Sprite" }, { Event::Image, "Image" }, + { Event::OpenGL, "OpenGL" }, { Event(-1), "Unknown" }, }); diff --git a/include/mbgl/platform/gl.hpp b/include/mbgl/platform/gl.hpp index 5706fa0ab..bbbcf6e7e 100644 --- a/include/mbgl/platform/gl.hpp +++ b/include/mbgl/platform/gl.hpp @@ -3,35 +3,22 @@ #include -#ifdef NVIDIA - #include - #include - - extern PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT; - - #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES -#elif __APPLE__ +#if __APPLE__ #include "TargetConditionals.h" #if TARGET_OS_IPHONE #include #include - #define glGenVertexArrays glGenVertexArraysOES - #define glBindVertexArray glBindVertexArrayOES - #define glDeleteVertexArrays glDeleteVertexArraysOES - #define GL_ARB_vertex_array_object 1 - #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES + // #define glGenVertexArrays glGenVertexArraysOES + // #define glBindVertexArray glBindVertexArrayOES + // #define glDeleteVertexArrays glDeleteVertexArraysOES + // #define GL_ARB_vertex_array_object 1 + // #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES #elif TARGET_IPHONE_SIMULATOR #include #include #elif TARGET_OS_MAC #include #include - #if GL_APPLE_vertex_array_object - #define GL_ARB_vertex_array_object 1 - #define glGenVertexArrays glGenVertexArraysAPPLE - #define glBindVertexArray glBindVertexArrayAPPLE - #define glDeleteVertexArrays glDeleteVertexArraysAPPLE - #endif #else #error Unsupported Apple platform #endif @@ -39,7 +26,6 @@ #define GL_GLEXT_PROTOTYPES #include #include - #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES #else #define GL_GLEXT_PROTOTYPES #include @@ -48,8 +34,19 @@ namespace mbgl { namespace gl { + +typedef void (* PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (* PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint* arrays); +typedef void (* PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint* arrays); +typedef GLboolean (* PFNGLISVERTEXARRAYPROC) (GLuint array); +extern PFNGLBINDVERTEXARRAYPROC BindVertexArray; +extern PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays; +extern PFNGLGENVERTEXARRAYSPROC GenVertexArrays; +extern PFNGLISVERTEXARRAYPROC IsVertexArray; + + // Debug group markers, useful for debuggin on iOS -#if defined(__APPLE__) && defined(DEBUG) && defined(GL_EXT_debug_marker) +#if __APPLE__ && defined(DEBUG) && defined(GL_EXT_debug_marker) // static int indent = 0; inline void start_group(const std::string &str) { glPushGroupMarkerEXT(0, str.c_str()); diff --git a/include/mbgl/text/collision.hpp b/include/mbgl/text/collision.hpp index dfefab689..3bf37a6a1 100644 --- a/include/mbgl/text/collision.hpp +++ b/include/mbgl/text/collision.hpp @@ -27,7 +27,7 @@ namespace bgi = bg::index; typedef bgm::point Point; typedef bgm::box Box; typedef std::pair PlacementValue; -typedef bgi::rtree> Tree; +typedef bgi::rtree> Tree; class Collision { diff --git a/platform/darwin/image.mm b/platform/darwin/image.mm index 50c871533..b8df3c403 100644 --- a/platform/darwin/image.mm +++ b/platform/darwin/image.mm @@ -93,8 +93,8 @@ Image::Image(const std::string &source_data) { return; } - width = CGImageGetWidth(image); - height = CGImageGetHeight(image); + width = uint32_t(CGImageGetWidth(image)); + height = uint32_t(CGImageGetHeight(image)); CGRect rect = {{ 0, 0 }, { static_cast(width), static_cast(height) }}; img = ::std::unique_ptr(new char[width * height * 4]()); diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index baacfbfd9..34b9ddbef 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -26,6 +27,10 @@ void GLFWView::initialize(mbgl::Map *map_) { monitor = glfwGetPrimaryMonitor(); } +#ifdef DEBUG + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); +#endif + #ifdef GL_ES_VERSION_2_0 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); @@ -63,6 +68,25 @@ void GLFWView::initialize(mbgl::Map *map_) { glfwSetScrollCallback(window, scroll); glfwSetKeyCallback(window, key); + + const std::string extensions = (char *)glGetString(GL_EXTENSIONS); + { + using namespace mbgl; + + + if (extensions.find("GL_ARB_vertex_array_object") != std::string::npos) { + gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)glfwGetProcAddress("glBindVertexArrayARB"); + gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)glfwGetProcAddress("glDeleteVertexArraysARB"); + gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)glfwGetProcAddress("glGenVertexArraysARB"); + gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)glfwGetProcAddress("glIsVertexArrayARB"); + } else if (extensions.find("GL_APPLE_vertex_array_object") != std::string::npos) { + gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)glfwGetProcAddress("glBindVertexArrayAPPLE"); + gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)glfwGetProcAddress("glDeleteVertexArraysAPPLE"); + gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)glfwGetProcAddress("glGenVertexArraysAPPLE"); + gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)glfwGetProcAddress("glIsVertexArrayAPPLE"); + } + } + glfwMakeContextCurrent(nullptr); } diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index 71096beba..687f3ecf7 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -11,16 +11,62 @@ static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = nullptr; #endif #endif +#ifdef MBGL_USE_CGL +#include + +typedef void (* CGLProc)(void); +CGLProc CGLGetProcAddress(const char *proc) { + static CFBundleRef framework = nullptr; + if (!framework) { + framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + if (!framework) { + throw std::runtime_error("Failed to load OpenGL.framework"); + } + } + + CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, proc, kCFStringEncodingASCII); + CGLProc symbol = (CGLProc)CFBundleGetFunctionPointerForName(framework, name); + CFRelease(name); + return symbol; +} +#endif + namespace mbgl { + HeadlessView::HeadlessView() : display_(std::make_shared()) { createContext(); + loadExtensions(); } HeadlessView::HeadlessView(std::shared_ptr display) : display_(display) { createContext(); + loadExtensions(); +} + +void HeadlessView::loadExtensions() { + make_active(); + const std::string extensions = (char *)glGetString(GL_EXTENSIONS); + +#ifdef MBGL_USE_CGL + if (extensions.find("GL_APPLE_vertex_array_object") != std::string::npos) { + gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)CGLGetProcAddress("glBindVertexArrayAPPLE"); + gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)CGLGetProcAddress("glDeleteVertexArraysAPPLE"); + gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)CGLGetProcAddress("glGenVertexArraysAPPLE"); + gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)CGLGetProcAddress("glIsVertexArrayAPPLE"); + } +#endif +#ifdef MBGL_USE_GLX + if (extensions.find("GL_ARB_vertex_array_object") != std::string::npos) { + gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)glXGetProcAddress((const GLubyte *)"glBindVertexArrayARB"); + gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)glXGetProcAddress((const GLubyte *)"glDeleteVertexArraysARB"); + gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)glXGetProcAddress((const GLubyte *)"glGenVertexArraysARB"); + gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)glXGetProcAddress((const GLubyte *)"glIsVertexArrayARB"); + } +#endif + make_inactive(); } diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 5549abea1..db057bc1b 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -1,7 +1,5 @@ #include #include -#include -#include #include #ifdef __ANDROID__ #include "../../android/cpp/NativeMapView.hpp" @@ -23,7 +21,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -77,24 +75,81 @@ static std::queue handles; namespace mbgl { -struct CURLContext { - util::ptr baton; - uv_poll_t *poll_handle = nullptr; - curl_socket_t sockfd = 0; +struct Context { + const util::ptr baton; + CURL *handle = nullptr; curl_slist *headers = nullptr; - CURLContext(const util::ptr &baton_) : baton(baton_) { + Context(const util::ptr &baton_) : baton(baton_) { + assert(baton); + baton->ptr = this; + + if (!handles.empty()) { + handle = handles.front(); + handles.pop(); + } else { + handle = curl_easy_init(); + } } - ~CURLContext() { - // We are destructing the poll handle in a CURL callback already. - assert(!poll_handle); + ~Context() { + baton->ptr = nullptr; - // Once the CURLContext gets destroyed, CURL doesn't need any headers anymore. if (headers) { curl_slist_free_all(headers); headers = nullptr; } + + CURLMcode error = curl_multi_remove_handle(multi, handle); + if (error != CURLM_OK) { + baton->response = std::unique_ptr(new Response()); + baton->response->code = -1; + baton->response->message = curl_multi_strerror(error); + } + + curl_easy_setopt(handle, CURLOPT_PRIVATE, nullptr); + curl_easy_reset(handle); + handles.push(handle); + handle = nullptr; + + if (baton->async) { + uv_async_send(baton->async); + baton->async = nullptr; + } + } +}; + +struct Socket { +private: + uv_poll_t poll_handle; + +public: + const curl_socket_t sockfd = 0; + +public: + Socket(curl_socket_t sockfd_) : sockfd(sockfd_) { + uv_poll_init_socket(loop, &poll_handle, sockfd); + poll_handle.data = this; + } + + void start(int events, uv_poll_cb cb) { + uv_poll_start(&poll_handle, events, cb); + } + + void stop() { + assert(poll_handle.data); + poll_handle.data = nullptr; + uv_poll_stop(&poll_handle); + uv_close((uv_handle_t *)&poll_handle, [](uv_handle_t *handle) { + delete (Socket *)handle->data; + }); + } + +private: + // Make the destructor private to ensure that we can only close the Socket + // with stop(), and disallow manual deletion. + ~Socket() { + assert(!poll_handle.data); } }; @@ -108,59 +163,22 @@ void curl_share_unlock(CURL *, curl_lock_data, void *) { uv_mutex_unlock(&share_mutex); } -// This function must run in the CURL thread. -// It is either called when the request is completed, or when we try to cancel the request. -void finish_request(const util::ptr &baton) { - assert(uv_thread_self() == thread_id); - if (baton->ptr) { - CURL *handle = (CURL *)baton->ptr; - CURLMcode error = curl_multi_remove_handle(multi, handle); - if (error != CURLM_OK) { - baton->response = std::make_unique(); - baton->response->code = -1; - baton->response->message = curl_multi_strerror(error); - } - - // Destroy the shared pointer. We still have one pointing to it - CURLContext *context = nullptr; - curl_easy_getinfo(handle, CURLINFO_PRIVATE, (char *)&context); - curl_easy_setopt(handle, CURLOPT_PRIVATE, nullptr); - delete context; - - // TODO: delete the headers object again. - - curl_easy_reset(handle); - handles.push(handle); - baton->ptr = nullptr; - } -} - -void curl_perform(uv_poll_t *req, int, int events) { - int running_handles; - int flags = 0; - CURLContext *context = (CURLContext *)req->data; - CURLMsg *message; - int pending; - - uv_timer_stop(&timeout); - - if (events & UV_READABLE) { - flags |= CURL_CSELECT_IN; - } - if (events & UV_WRITABLE) { - flags |= CURL_CSELECT_OUT; - } - - curl_multi_socket_action(multi, context->sockfd, flags, &running_handles); +void check_multi_info() { + CURLMsg *message = nullptr; + int pending = 0; while ((message = curl_multi_info_read(multi, &pending))) { switch (message->msg) { case CURLMSG_DONE: { - CURLContext *ctx = nullptr; - curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&ctx); + Context *context = nullptr; + curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&context); + assert(context); - // Make a copy so that the Baton stays around even after we are calling finish_request - util::ptr baton = ctx->baton; + auto baton = context->baton; + + // This request is complete. We are removing the pointer to the CURL easy handle again + // to prevent this request from getting canceled. + context->baton->ptr = nullptr; // Add human-readable error code if (message->data.result != CURLE_OK) { @@ -202,16 +220,8 @@ void curl_perform(uv_poll_t *req, int, int events) { } } - // We're currently in the CURL request thread. - finish_request(baton); - - if (baton->async) { - uv_async_send(baton->async); - baton->async = nullptr; - } - - break; - } + delete context; + } break; default: // This should never happen, because there are no other message types. @@ -220,63 +230,71 @@ void curl_perform(uv_poll_t *req, int, int events) { } } -int handle_socket(CURL *handle, curl_socket_t sockfd, int action, void *, void *socketp) { - CURLContext *context = nullptr; - curl_easy_getinfo(handle, CURLINFO_PRIVATE, (char *)&context); +void curl_perform(uv_poll_t *req, int /* status */, int events) { + int flags = 0; - if (!socketp && action != CURL_POLL_REMOVE) { - // We haven't initialized the socket yet, so we need to do this now. - assert(context->sockfd == 0); - context->sockfd = sockfd; - assert(!context->poll_handle); - context->poll_handle = new uv_poll_t; - uv_poll_init_socket(loop, context->poll_handle, sockfd); - context->poll_handle->data = context; - curl_multi_assign(multi, sockfd, context); + uv_timer_stop(&timeout); + + if (events & UV_READABLE) { + flags |= CURL_CSELECT_IN; + } + if (events & UV_WRITABLE) { + flags |= CURL_CSELECT_OUT; } - if (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); - uv_close((uv_handle_t *)context->poll_handle, [](uv_handle_t *poll_handle) { - delete (uv_poll_t *)poll_handle; - }); - context->poll_handle = nullptr; - curl_multi_assign(multi, sockfd, NULL); + Socket *context = (Socket *)req->data; + int running_handles = 0; + curl_multi_socket_action(multi, context->sockfd, flags, &running_handles); + + check_multi_info(); +} + +int handle_socket(CURL * /* handle */, curl_socket_t s, int action, void * /* userp */, void *socketp) { + Socket *socket = (Socket *)socketp; + if (!socket && action != CURL_POLL_REMOVE) { + socket = new Socket(s); + curl_multi_assign(multi, s, (void *)socket); + } + + switch (action) { + case CURL_POLL_IN: + socket->start(UV_READABLE, curl_perform); + break; + case CURL_POLL_OUT: + socket->start(UV_WRITABLE, curl_perform); + break; + case CURL_POLL_REMOVE: + if (socket) { + socket->stop(); + curl_multi_assign(multi, s, NULL); } + break; + default: + abort(); } return 0; } #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -void on_timeout(uv_timer_t *, int = 0) { +void on_timeout(uv_timer_t * /* req */, int /* status */) { #else -void on_timeout(uv_timer_t *) { +void on_timeout(uv_timer_t * /* req */) { #endif int running_handles; CURLMcode error = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running_handles); if (error != CURLM_OK) { throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error)); } + + check_multi_info(); } -void start_timeout(CURLM *, long timeout_ms, void *) { +void start_timeout(CURLM * /* multi */, long timeout_ms, void * /* userp */) { if (timeout_ms <= 0) { -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - on_timeout(&timeout, 0); -#else - on_timeout(&timeout); -#endif - } else { - uv_timer_start(&timeout, on_timeout, timeout_ms, 0); + timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */ } + uv_timer_start(&timeout, on_timeout, timeout_ms, 0); } void thread_init(void *) { @@ -285,6 +303,10 @@ void thread_init(void *) { #endif thread_id = uv_thread_self(); + if (curl_global_init(CURL_GLOBAL_ALL)) { + throw std::runtime_error("Could not init cURL"); + } + uv_timer_init(loop, &timeout); CURLSHcode share_error; @@ -300,7 +322,6 @@ void thread_init(void *) { throw std::runtime_error(std::string("CURL share error: ") + curl_share_strerror(share_error)); } - CURLMcode multi_error; multi = curl_multi_init(); @@ -311,7 +332,6 @@ void thread_init(void *) { multi_error = curl_multi_setopt(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. @@ -329,7 +349,9 @@ void thread_init(void *) { // 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 *const contents, const size_t size, const size_t nmemb, void *const userp) { - ((std::string *)userp)->append((char *)contents, size * nmemb); + auto &response = *(std::unique_ptr *)userp; + assert(response); + response->data.append((char *)contents, size * nmemb); return size * nmemb; } @@ -350,7 +372,8 @@ size_t header_matches(const char *const header, const char *const buffer, const size_t curl_header_cb(char * const buffer, const size_t size, const size_t nmemb, void *const userp) { const size_t length = size * nmemb; - Response *response = static_cast(userp); + auto &response = *(std::unique_ptr *)userp; + assert(response); size_t begin = std::string::npos; if ((begin = header_matches("last-modified: ", buffer, length)) != std::string::npos) { @@ -371,40 +394,30 @@ size_t curl_header_cb(char * const buffer, const size_t size, const size_t nmemb // This function must run in the CURL thread. void start_request(void *const ptr) { assert(uv_thread_self() == thread_id); - std::unique_ptr> baton_guard { (util::ptr *)ptr }; - util::ptr &baton = *baton_guard.get(); - assert(baton); - CURL *handle = nullptr; - if (!handles.empty()) { - handle = handles.front(); - handles.pop(); - } else { - handle = curl_easy_init(); - } + // The Context object stores information that we need to retain throughout the request, such + // as the actual CURL easy handle, the baton, and the list of headers. The Context itself is + // stored in both the CURL easy handle's PRIVATE field, and the baton's `ptr` field. + auto context = new Context(*(util::ptr *)ptr); + delete (util::ptr *)ptr; - baton->ptr = handle; - - // Wrap this in a unique_ptr for now so that it destructs until we assign it the the CURL handle. - std::unique_ptr context = std::make_unique(baton); - - if (baton->response) { - if (!baton->response->etag.empty()) { - const std::string header = std::string("If-None-Match: ") + baton->response->etag; + if (context->baton->response) { + if (!context->baton->response->etag.empty()) { + const std::string header = std::string("If-None-Match: ") + context->baton->response->etag; context->headers = curl_slist_append(context->headers, header.c_str()); - } else if (baton->response->modified) { - const std::string time = std::string("If-Modified-Since: ") + - util::rfc1123(baton->response->modified); + } else if (context->baton->response->modified) { + const std::string time = + std::string("If-Modified-Since: ") + util::rfc1123(context->baton->response->modified); context->headers = curl_slist_append(context->headers, time.c_str()); } } if (context->headers) { - curl_easy_setopt(handle, CURLOPT_HTTPHEADER, context->headers); + curl_easy_setopt(context->handle, CURLOPT_HTTPHEADER, context->headers); } - if (!baton->response) { - baton->response = std::make_unique(); + if (!context->baton->response) { + context->baton->response = std::unique_ptr(new Response()); } #ifndef __ANDROID__ @@ -414,35 +427,35 @@ void start_request(void *const ptr) { #endif // Carry on the shared pointer in the private information of the CURL handle. - curl_easy_setopt(handle, CURLOPT_PRIVATE, context.release()); - curl_easy_setopt(handle, CURLOPT_CAINFO, ca_path.c_str()); - curl_easy_setopt(handle, CURLOPT_URL, baton->path.c_str()); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_cb); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, &baton->response->data); - curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, curl_header_cb); - curl_easy_setopt(handle, CURLOPT_HEADERDATA, baton->response.get()); - curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate"); - curl_easy_setopt(handle, CURLOPT_SHARE, share); + curl_easy_setopt(context->handle, CURLOPT_PRIVATE, context); + curl_easy_setopt(context->handle, CURLOPT_CAINFO, ca_path.c_str()); + curl_easy_setopt(context->handle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(context->handle, CURLOPT_URL, context->baton->path.c_str()); + curl_easy_setopt(context->handle, CURLOPT_WRITEFUNCTION, curl_write_cb); + curl_easy_setopt(context->handle, CURLOPT_WRITEDATA, &context->baton->response); + curl_easy_setopt(context->handle, CURLOPT_HEADERFUNCTION, curl_header_cb); + curl_easy_setopt(context->handle, CURLOPT_HEADERDATA, &context->baton->response); + curl_easy_setopt(context->handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate"); // Start requesting the information. - curl_multi_add_handle(multi, handle); + curl_multi_add_handle(multi, context->handle); } // This function must run in the CURL thread. void stop_request(void *const ptr) { assert(uv_thread_self() == thread_id); - std::unique_ptr> baton_guard { (util::ptr *)ptr }; - util::ptr &baton = *baton_guard.get(); + auto baton = *(util::ptr *)ptr; + delete (util::ptr *)ptr; assert(baton); if (baton->async) { baton->type = HTTPResponseType::Canceled; - // We can still stop the request because it is still in progress. - finish_request(baton); + assert(baton->ptr); - uv_async_send(baton->async); - baton->async = nullptr; + // We can still stop the request because it is still in progress. + delete (Context *)baton->ptr; + assert(!baton->ptr); } else { // If the async handle is gone, it means that the actual request has been completed before // we got a chance to cancel it. In this case, this is a no-op. It is likely that diff --git a/src/geometry/vao.cpp b/src/geometry/vao.cpp index 4c459c436..d2fd2727e 100644 --- a/src/geometry/vao.cpp +++ b/src/geometry/vao.cpp @@ -1,20 +1,30 @@ #include +#include namespace mbgl { -#if GL_ARB_vertex_array_object - VertexArrayObject::~VertexArrayObject() { + if (!gl::DeleteVertexArrays) return; + if (vao) { - glDeleteVertexArrays(1, &vao); + gl::DeleteVertexArrays(1, &vao); } } void VertexArrayObject::bindVertexArrayObject() { - if (!vao) { - glGenVertexArrays(1, &vao); + if (!gl::GenVertexArrays || !gl::BindVertexArray) { + static bool reported = false; + if (!reported) { + Log::Warning(Event::OpenGL, "Not using Vertex Array Objects"); + reported = true; + } + return; } - glBindVertexArray(vao); + + if (!vao) { + gl::GenVertexArrays(1, &vao); + } + gl::BindVertexArray(vao); } void VertexArrayObject::verifyBinding(Shader &shader, GLuint vertexBuffer, GLuint elementsBuffer, @@ -41,6 +51,4 @@ void VertexArrayObject::storeBinding(Shader &shader, GLuint vertexBuffer, GLuint bound_elements_buffer = elementsBuffer; } -#endif - } diff --git a/src/map/tile_data.cpp b/src/map/tile_data.cpp index aa48fd00f..44628e8b3 100644 --- a/src/map/tile_data.cpp +++ b/src/map/tile_data.cpp @@ -75,7 +75,10 @@ void TileData::request() { void TileData::cancel() { if (state != State::obsolete) { state = State::obsolete; - req.reset(); + if (req) { + req->cancel(); + req.reset(); + } } } diff --git a/src/platform/gl.cpp b/src/platform/gl.cpp index a1bf0ffc6..3133f128b 100644 --- a/src/platform/gl.cpp +++ b/src/platform/gl.cpp @@ -2,9 +2,17 @@ #include -#ifdef NVIDIA -PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT; -#endif + +namespace mbgl { +namespace gl { + +PFNGLBINDVERTEXARRAYPROC BindVertexArray = nullptr; +PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays = nullptr; +PFNGLGENVERTEXARRAYSPROC GenVertexArrays = nullptr; +PFNGLISVERTEXARRAYPROC IsVertexArray = nullptr; + +} +} void _CHECK_GL_ERROR(const char *cmd, const char *file, int line) { std::cout << cmd << ";" << std::endl; diff --git a/src/renderer/prerendered_texture.cpp b/src/renderer/prerendered_texture.cpp index 0b8b9a386..63f68c8ed 100644 --- a/src/renderer/prerendered_texture.cpp +++ b/src/renderer/prerendered_texture.cpp @@ -49,7 +49,11 @@ void PrerenderedTexture::bindFramebuffer() { // Create depth/stencil buffer glGenRenderbuffers(1, &fbo_depth_stencil); glBindRenderbuffer(GL_RENDERBUFFER, fbo_depth_stencil); +#ifdef GL_ES_VERSION_2_0 + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, properties.size, properties.size); +#else glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, properties.size, properties.size); +#endif glBindRenderbuffer(GL_RENDERBUFFER, 0); } diff --git a/src/storage/response.cpp b/src/storage/response.cpp index e3cc66e60..a08a6d31c 100644 --- a/src/storage/response.cpp +++ b/src/storage/response.cpp @@ -6,10 +6,10 @@ namespace mbgl { int64_t Response::parseCacheControl(const char *value) { if (value) { - uint64_t seconds = 0; + unsigned long long seconds = 0; // TODO: cache-control may contain other information as well: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 - if (std::sscanf(value, "max-age=%llu", reinterpret_cast(&seconds)) == 1) { + if (std::sscanf(value, "max-age=%llu", &seconds) == 1) { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count() + seconds; diff --git a/test/headless.cpp b/test/headless.cpp index 38c3decd6..680f51aeb 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -17,9 +17,21 @@ #include #include #include +#ifdef __linux__ +#include +#endif std::string base_directory; +namespace mbgl { +namespace platform { + +std::string defaultCacheDatabase() { + // Disables the cache. + return ""; +} +} +} class ServerEnvironment : public ::testing::Environment { public: @@ -28,8 +40,13 @@ public: if (pid < 0) { throw std::runtime_error("Cannot create web server"); } else if (pid == 0) { - char *arg[] = { nullptr }; - int ret = execv((base_directory + "bin/server.py").c_str(), arg); +#ifdef __linux__ + prctl(PR_SET_PDEATHSIG, SIGHUP); +#endif + const auto executable = base_directory + "bin/server.py"; + const char *port = "2900"; + char *arg[] = { const_cast(executable.c_str()), const_cast(port), nullptr }; + int ret = execv(executable.c_str(), arg); // This call should not return. In case execve failed, we exit anyway. if (ret < 0) { fprintf(stderr, "Failed to start server: %s\n", strerror(errno)); @@ -56,9 +73,14 @@ ServerEnvironment* env = nullptr; GTEST_API_ int main(int argc, char *argv[]) { // Note: glibc's dirname() **modifies** the argument and can't handle static strings. - std::string argv0 { argv[0] }; argv0 = dirname(const_cast(argv0.c_str())); std::string file { __FILE__ }; file = dirname(const_cast(file.c_str())); - base_directory = argv0 + "/" + file + "/suite/"; + if (file[0] == '/') { + // If __FILE__ is an absolute path, we don't have to guess from the argv 0. + base_directory = file + "/suite/"; + } else { + std::string argv0 { argv[0] }; argv0 = dirname(const_cast(argv0.c_str())); + base_directory = argv0 + "/" + file + "/suite/"; + } testing::InitGoogleTest(&argc, argv); env = new ServerEnvironment(); @@ -66,6 +88,15 @@ GTEST_API_ int main(int argc, char *argv[]) { return RUN_ALL_TESTS(); } +void rewriteLocalScheme(rapidjson::Value &value, rapidjson::Document::AllocatorType &allocator) { + ASSERT_TRUE(value.IsString()); + auto string = std::string { value.GetString(),value.GetStringLength() }; + if (string.compare(0, 8, "local://") == 0) { + string.replace(0, 8, "http://127.0.0.1:2900/"); + value.SetString(string.data(), string.size(), allocator); + } +} + class HeadlessTest : public ::testing::TestWithParam {}; TEST_P(HeadlessTest, render) { @@ -76,17 +107,40 @@ TEST_P(HeadlessTest, render) { std::string style = util::read_file(base_directory + "tests/" + base + "/style.json"); std::string info = util::read_file(base_directory + "tests/" + base + "/info.json"); - std::size_t pos = 0; - while ((pos = style.find("local://", pos)) != std::string::npos) { - style.replace(pos, 8, "http://localhost:2900/"); - } - // Parse style. rapidjson::Document styleDoc; styleDoc.Parse<0>((const char *const)style.c_str()); ASSERT_FALSE(styleDoc.HasParseError()); ASSERT_TRUE(styleDoc.IsObject()); + // Rewrite "local://" to "http://127.0.0.1:2900/". + if (styleDoc.HasMember("sprite")) { + rewriteLocalScheme(styleDoc["sprite"], styleDoc.GetAllocator()); + } + + if (styleDoc.HasMember("glyphs")) { + rewriteLocalScheme(styleDoc["glyphs"], styleDoc.GetAllocator()); + } + + if (styleDoc.HasMember("sources")) { + auto &sources = styleDoc["sources"]; + ASSERT_TRUE(sources.IsObject()); + for (auto source = sources.MemberBegin(), end = sources.MemberEnd(); source != end; source++) { + if (source->value.HasMember("tiles")) { + auto &tiles = source->value["tiles"]; + ASSERT_TRUE(tiles.IsArray()); + for (rapidjson::SizeType i = 0; i < tiles.Size(); i++) { + rewriteLocalScheme(tiles[i], styleDoc.GetAllocator()); + } + } + + if (source->value.HasMember("url")) { + rewriteLocalScheme(source->value["url"], styleDoc.GetAllocator()); + } + } + } + + // Convert the JSON object back into a stringified version. rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); styleDoc.Accept(writer); diff --git a/test/test.gyp b/test/test.gyp index 2c6f411cf..cd2bb0ce0 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -198,9 +198,19 @@ '../mapboxgl.gyp:mbgl-standalone', '<(platform_library)', ], + 'variables': { + 'cflags_cc': [ + '-I<(boost_root)/include', + ] + }, 'conditions': [ - ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)', '-framework Foundation' ] } + ['OS == "mac"', { + 'xcode_settings': { + 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ], + 'OTHER_LDFLAGS': [ '<@(ldflags)', '-framework Foundation' ] + }, }, { + 'cflags_cc': [ '<@(cflags_cc)' ], 'libraries': [ '<@(ldflags)'], }] ] diff --git a/utils/mbgl-config/mbgl-config.template.sh b/utils/mbgl-config/mbgl-config.template.sh index 16b67b1f0..84fce43c1 100644 --- a/utils/mbgl-config/mbgl-config.template.sh +++ b/utils/mbgl-config/mbgl-config.template.sh @@ -43,7 +43,7 @@ while test $# -gt 0; do ;; --libs) - echo -L${CONFIG_MBGL_PREFIX}/lib -lmbgl -lmbgl-${CONFIG_MBGL_PLATFORM} -lmbgl-headless ${CONFIG_MBGL_LDFLAGS} + echo -L${CONFIG_MBGL_PREFIX}/lib -lmbgl-core -lmbgl-${CONFIG_MBGL_PLATFORM} -lmbgl-headless ${CONFIG_MBGL_LDFLAGS} ;; --includedir)