From dc5fcdbc6c1b65e5c35490473c30a6ce844a5c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Wed, 5 Nov 2014 11:41:49 +0100 Subject: [PATCH 01/17] rewrite the actual JSON file instead of a global search/replace --- test/headless.cpp | 48 ++++++++++++++++++++++++++++++++++++++++------- test/suite | 2 +- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/test/headless.cpp b/test/headless.cpp index 38c3decd6..92e0647a3 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -28,8 +28,10 @@ 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); + 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)); @@ -66,6 +68,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://localhost:2900/"); + value.SetString(string.data(), string.size(), allocator); + } +} + class HeadlessTest : public ::testing::TestWithParam {}; TEST_P(HeadlessTest, render) { @@ -76,17 +87,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://localhost: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/suite b/test/suite index b94ae79bb..5cc4f9cdb 160000 --- a/test/suite +++ b/test/suite @@ -1 +1 @@ -Subproject commit b94ae79bb1e802b3c2d41792401192631d2be545 +Subproject commit 5cc4f9cdbeada9511f81c88e87423e9fe365451d From 2f86a1735e2806047deebd8378dc60d35cd1993b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Wed, 5 Nov 2014 12:13:24 +0100 Subject: [PATCH 02/17] fix boost includes and allow any platform builds for xcode test project --- Makefile | 2 +- gyp/mbgl-linux.gypi | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 73e4c8799..539ab12bc 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/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)', From dfd9e9ce36c309cf6c952ccc4ef925d21ea5be1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Wed, 5 Nov 2014 15:49:52 +0100 Subject: [PATCH 03/17] don't use a cache for test cases --- test/headless.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/headless.cpp b/test/headless.cpp index 92e0647a3..c77aa1185 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -20,6 +20,15 @@ std::string base_directory; +namespace mbgl { +namespace platform { + +std::string defaultCacheDatabase() { + // Disables the cache. + return ""; +} +} +} class ServerEnvironment : public ::testing::Environment { public: From 44570c1d303ea169157f82034ef3af42f73d9e8d Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 5 Nov 2014 18:41:07 +0000 Subject: [PATCH 04/17] use linear<16,4> splitting algorithm (faster running tests) --- include/mbgl/text/collision.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 { From 3038cdbbf3bddc49f6a58dda710a8e080dcb9ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Wed, 5 Nov 2014 18:32:11 +0100 Subject: [PATCH 05/17] fix various issues in curl bindings related to tile cancelation --- platform/default/http_request_baton_curl.cpp | 310 ++++++++++--------- src/map/tile_data.cpp | 5 +- test/headless.cpp | 9 +- 3 files changed, 176 insertions(+), 148 deletions(-) diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 80eed2767..986191ef8 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 #include @@ -20,7 +18,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 @@ -74,24 +72,79 @@ 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() { + 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 ew can only close the Socket + // with stop(), and disallow manual deletion. + ~Socket() { + assert(!poll_handle.data); } }; @@ -105,59 +158,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) { @@ -199,16 +215,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. @@ -217,59 +225,75 @@ 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); + 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; + if (action == CURL_POLL_IN || action == CURL_POLL_OUT) { + if (socketp) { + socket = (Socket *)socketp; + } else { + socket = new Socket(s); } - 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); + 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 (socketp) { + ((Socket *)socketp)->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) { - on_timeout(&timeout, 0); - } 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 *) { @@ -278,6 +302,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; @@ -293,7 +321,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(); @@ -304,7 +331,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. @@ -322,7 +348,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; } @@ -343,7 +371,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) { @@ -364,72 +393,63 @@ 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()); } // 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-bundle.crt"); - 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-bundle.crt"); + 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"); + curl_easy_setopt(context->handle, CURLOPT_SHARE, share); // 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/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/test/headless.cpp b/test/headless.cpp index c77aa1185..d5a13031d 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -67,9 +67,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(); From 2e38b5964c4048072daa805a75ad564df5d9f9c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Wed, 5 Nov 2014 18:36:43 +0100 Subject: [PATCH 06/17] use IPv4 address instead of localhost to work around systems that don't resolve localhost to 127.0.0.1 --- test/headless.cpp | 4 ++-- test/suite | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/headless.cpp b/test/headless.cpp index d5a13031d..ef3e785cc 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -86,7 +86,7 @@ void rewriteLocalScheme(rapidjson::Value &value, rapidjson::Document::AllocatorT ASSERT_TRUE(value.IsString()); auto string = std::string { value.GetString(),value.GetStringLength() }; if (string.compare(0, 8, "local://") == 0) { - string.replace(0, 8, "http://localhost:2900/"); + string.replace(0, 8, "http://127.0.0.1:2900/"); value.SetString(string.data(), string.size(), allocator); } } @@ -107,7 +107,7 @@ TEST_P(HeadlessTest, render) { ASSERT_FALSE(styleDoc.HasParseError()); ASSERT_TRUE(styleDoc.IsObject()); - // Rewrite "local://" to "http://localhost:2900/". + // Rewrite "local://" to "http://127.0.0.1:2900/". if (styleDoc.HasMember("sprite")) { rewriteLocalScheme(styleDoc["sprite"], styleDoc.GetAllocator()); } diff --git a/test/suite b/test/suite index 5cc4f9cdb..2892e261e 160000 --- a/test/suite +++ b/test/suite @@ -1 +1 @@ -Subproject commit 5cc4f9cdbeada9511f81c88e87423e9fe365451d +Subproject commit 2892e261eb6dd8ce42f304e009ef7b2ff5e1a9c8 From 3303db79d925d7e712ed16378158d914f1c78293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 6 Nov 2014 02:58:14 -0800 Subject: [PATCH 07/17] fix undefined value [skip ci] --- platform/default/http_request_baton_curl.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 986191ef8..ecde8618f 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -245,13 +245,9 @@ void curl_perform(uv_poll_t *req, int /* status */, int events) { } int handle_socket(CURL * /* handle */, curl_socket_t s, int action, void * /* userp */, void *socketp) { - Socket *socket; - if (action == CURL_POLL_IN || action == CURL_POLL_OUT) { - if (socketp) { - socket = (Socket *)socketp; - } else { - socket = new Socket(s); - } + Socket *socket = (Socket *)socketp; + if (!socket && action != CURL_POLL_REMOVE) { + socket = new Socket(s); curl_multi_assign(multi, s, (void *)socket); } @@ -263,8 +259,8 @@ int handle_socket(CURL * /* handle */, curl_socket_t s, int action, void * /* us socket->start(UV_WRITABLE, curl_perform); break; case CURL_POLL_REMOVE: - if (socketp) { - ((Socket *)socketp)->stop(); + if (socket) { + socket->stop(); curl_multi_assign(multi, s, NULL); } break; From 0480c10f900065c40c3f06616310daba168b6dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 6 Nov 2014 02:58:38 -0800 Subject: [PATCH 08/17] [linux] let child process exit when parent hung up --- test/headless.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/headless.cpp b/test/headless.cpp index ef3e785cc..680f51aeb 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -17,6 +17,9 @@ #include #include #include +#ifdef __linux__ +#include +#endif std::string base_directory; @@ -37,6 +40,9 @@ public: if (pid < 0) { throw std::runtime_error("Cannot create web server"); } else if (pid == 0) { +#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 }; From 01d4cdbb02775df9dca92c98d96abd3c107b7713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Thu, 6 Nov 2014 13:21:46 +0100 Subject: [PATCH 09/17] clean up poll handle data in close() function so that assert() won't fail --- platform/default/http_request_baton_curl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index ecde8618f..b8b4bac5f 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -134,6 +134,8 @@ public: } 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; @@ -141,7 +143,7 @@ public: } private: - // Make the destructor private to ensure that ew can only close the Socket + // Make the destructor private to ensure that we can only close the Socket // with stop(), and disallow manual deletion. ~Socket() { assert(!poll_handle.data); From 618a4a5785e07b9663111d8d7f4b33c0f6be0c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Thu, 6 Nov 2014 14:18:52 +0100 Subject: [PATCH 10/17] add -Werror to os x as well --- gyp/common.gypi | 2 +- src/storage/response.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gyp/common.gypi b/gyp/common.gypi index b21e8b50d..df055b199 100644 --- a/gyp/common.gypi +++ b/gyp/common.gypi @@ -14,7 +14,7 @@ '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' ], 'GCC_WARN_PEDANTIC': 'YES', 'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE', }, diff --git a/src/storage/response.cpp b/src/storage/response.cpp index f1e1c3587..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=%lu", &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; From 37afa5af5b336a30d5ead6f6755d449a4b93a7a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Thu, 6 Nov 2014 15:40:16 +0100 Subject: [PATCH 11/17] explicitly cast to 32 bit integer --- platform/darwin/image.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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]()); From fb7f23884bedb0a2a825baa8dc14d6b84384a0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Thu, 6 Nov 2014 17:21:55 +0100 Subject: [PATCH 12/17] don't error on unused parameters --- gyp/common.gypi | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/gyp/common.gypi b/gyp/common.gypi index df055b199..6d5fb7690 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': [ '-Werror', '-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,6 +32,7 @@ '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-error=unused-parameter', ], }], ], From fbb43cb5eac69d6931cf8d0d24d7c41f0434e9a0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 6 Nov 2014 16:53:05 -0500 Subject: [PATCH 13/17] have mbgl-config report mbgl-core only (without libuv) --- utils/mbgl-config/mbgl-config.template.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 7e65c88b464b2faa35f8aa9c3ec3029d5c359393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Fri, 7 Nov 2014 17:25:50 +0100 Subject: [PATCH 14/17] bump cocoa bindings --- ios/mapbox-gl-cocoa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/mapbox-gl-cocoa b/ios/mapbox-gl-cocoa index 63d166d3d..eb8f0b81b 160000 --- a/ios/mapbox-gl-cocoa +++ b/ios/mapbox-gl-cocoa @@ -1 +1 @@ -Subproject commit 63d166d3dd33a460a5158159ea9656ea651aa179 +Subproject commit eb8f0b81bdec68be0e0cc7e511e62eb8061829f3 From 582d616deb5b1febbaa1c8dfb5e3fb405f8cad53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Fri, 7 Nov 2014 18:17:24 +0100 Subject: [PATCH 15/17] load VAO extension --- include/mbgl/geometry/vao.hpp | 24 +++-------- .../mbgl/platform/default/headless_view.hpp | 1 + include/mbgl/platform/event.hpp | 2 + include/mbgl/platform/gl.hpp | 42 +++++++++---------- ios/mapbox-gl-cocoa | 2 +- platform/default/glfw_view.cpp | 18 ++++++++ platform/default/headless_view.cpp | 38 +++++++++++++++++ src/geometry/vao.cpp | 24 +++++++---- src/platform/gl.cpp | 14 +++++-- src/renderer/prerendered_texture.cpp | 4 ++ 10 files changed, 118 insertions(+), 51 deletions(-) diff --git a/include/mbgl/geometry/vao.hpp b/include/mbgl/geometry/vao.hpp index bb1f571ff..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,21 +68,6 @@ private: char *bound_offset = 0; }; -#else - -class VertexArrayObject : public util::noncopyable { -public: - template - void bind(Shader& shader, Buffers& buffers, char *offset) { - for (auto &buffer : buffers) { - buffer.bind(); - } - shader.bind(offset); - } -}; - -#endif - } #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 2aa4aa0c9..df2d8b03e 100644 --- a/include/mbgl/platform/event.hpp +++ b/include/mbgl/platform/event.hpp @@ -32,6 +32,7 @@ enum class Event : uint8_t { Database, HttpRequest, Sprite, + OpenGL, }; MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { @@ -44,6 +45,7 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { { Event::Database, "Database" }, { Event::HttpRequest, "HttpRequest" }, { Event::Sprite, "Sprite" }, + { Event::OpenGL, "OpenGL" }, { Event(-1), "Unknown" }, }); diff --git a/include/mbgl/platform/gl.hpp b/include/mbgl/platform/gl.hpp index d5162f2c7..73753746a 100644 --- a/include/mbgl/platform/gl.hpp +++ b/include/mbgl/platform/gl.hpp @@ -3,48 +3,48 @@ #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 +#elif MBGL_USE_GLES2 + #include + #include #else - #define GL_GLEXT_PROTOTYPES #include #include #endif 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/ios/mapbox-gl-cocoa b/ios/mapbox-gl-cocoa index eb8f0b81b..f801f5b9f 160000 --- a/ios/mapbox-gl-cocoa +++ b/ios/mapbox-gl-cocoa @@ -1 +1 @@ -Subproject commit eb8f0b81bdec68be0e0cc7e511e62eb8061829f3 +Subproject commit f801f5b9fa9ca0e55c379f95295bc13338b19110 diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index baacfbfd9..7f8ca6994 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,19 @@ 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_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..8ce93030d 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -11,16 +11,54 @@ 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 + make_inactive(); } 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/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 adb7a5910..c9507d602 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); } From b5198374e766b7c67e3e87a1b9c729038e0b9b68 Mon Sep 17 00:00:00 2001 From: Mike Morris Date: Fri, 7 Nov 2014 12:20:40 -0500 Subject: [PATCH 16/17] add boost headers to cflags_cc for text_conversions test --- test/test.gyp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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)'], }] ] From 8fe8b2f739e997b76fcc768348c66f2007e0968b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Fri, 7 Nov 2014 09:38:07 -0800 Subject: [PATCH 17/17] add glx support --- include/mbgl/platform/gl.hpp | 1 + platform/default/glfw_view.cpp | 8 +++++++- platform/default/headless_view.cpp | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/mbgl/platform/gl.hpp b/include/mbgl/platform/gl.hpp index 73753746a..6685d1f37 100644 --- a/include/mbgl/platform/gl.hpp +++ b/include/mbgl/platform/gl.hpp @@ -26,6 +26,7 @@ #include #include #else + #define GL_GLEXT_PROTOTYPES #include #include #endif diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index 7f8ca6994..34b9ddbef 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -73,7 +73,13 @@ void GLFWView::initialize(mbgl::Map *map_) { { using namespace mbgl; - if (extensions.find("GL_APPLE_vertex_array_object") != std::string::npos) { + + 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"); diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index 8ce93030d..687f3ecf7 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -57,6 +57,14 @@ void HeadlessView::loadExtensions() { 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(); }