summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile15
-rw-r--r--bin/render.cpp115
-rw-r--r--bin/render.gyp53
-rw-r--r--include/mbgl/map/map.hpp9
-rw-r--r--platform/default/headless_view.cpp120
-rw-r--r--src/mbgl/map/map.cpp29
6 files changed, 236 insertions, 105 deletions
diff --git a/Makefile b/Makefile
index 539ab12bcf..d429903db9 100644
--- a/Makefile
+++ b/Makefile
@@ -53,6 +53,10 @@ build/linux/Makefile: linux/mapboxgl-app.gyp config.gypi
build/macosx/Makefile: macosx/mapboxgl-app.gyp config.gypi
deps/run_gyp macosx/mapboxgl-app.gyp -Iconfig.gypi -Dplatform=osx --depth=. -Goutput_dir=.. --generator-output=./build/macosx -f make
+.PHONY: build/render/Makefile
+build/render/Makefile: bin/render.gyp config.gypi
+ deps/run_gyp bin/render.gyp -Iconfig.gypi -Dplatform=$(PLATFORM) --depth=. -Goutput_dir=.. --generator-output=./build/render -f make
+
.PHONY: build/test/test.xcodeproj
build/test/test.xcodeproj: test/test.gyp config.gypi
deps/run_gyp test/test.gyp -Iconfig.gypi -Dplatform=$(PLATFORM) --depth=. -Goutput_dir=.. --generator-output=./build -f xcode
@@ -69,6 +73,10 @@ build/ios/mapbox-gl-cocoa/app/mapboxgl-app.xcodeproj: ios/mapbox-gl-cocoa/app/ma
build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi
deps/run_gyp linux/mapboxgl-app.gyp -Iconfig.gypi -Dplatform=linux --depth=. --generator-output=./build -f xcode
+.PHONY: build/bin/render.xcodeproj
+build/bin/render.xcodeproj: bin/render.gyp config.gypi
+ deps/run_gyp bin/render.gyp -Iconfig.gypi -Dplatform=$(PLATFORM) --depth=. --generator-output=./build -f xcode
+
##### Test cases ###############################################################
test: build/test/Makefile
@@ -101,6 +109,10 @@ osx: build/macosx/Makefile
run-osx: osx
build/$(BUILDTYPE)/Mapbox\ GL.app/Contents/MacOS/MAPBOX\ GL
+# Builds the CLI render app
+render: build/render/Makefile
+ $(MAKE) -C build/render BUILDTYPE=$(BUILDTYPE) V=$(V) mbgl-render
+
##### Xcode projects ###########################################################
clear_xcode_cache:
@@ -120,6 +132,9 @@ xproj: build/macosx/mapboxgl-app.xcodeproj
iproj: build/ios/mapbox-gl-cocoa/app/mapboxgl-app.xcodeproj
open ./build/ios/mapbox-gl-cocoa/app/mapboxgl-app.xcodeproj
+rproj: build/bin/render.xcodeproj
+ open ./build/bin/render.xcodeproj
+
# build Linux project for Xcode (Runs on Mac OS X too, but without platform-specific code)
lproj: build/linux/mapboxgl-app.xcodeproj
open ./build/linux/mapboxgl-app.xcodeproj
diff --git a/bin/render.cpp b/bin/render.cpp
new file mode 100644
index 0000000000..feeb4fe3f4
--- /dev/null
+++ b/bin/render.cpp
@@ -0,0 +1,115 @@
+#include <mbgl/map/map.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/io.hpp>
+
+#include <rapidjson/document.h>
+#include <rapidjson/writer.h>
+#include <rapidjson/stringbuffer.h>
+
+#include <mbgl/platform/default/headless_view.hpp>
+#include <mbgl/platform/default/headless_display.hpp>
+#include <mbgl/storage/caching_http_file_source.hpp>
+
+#if __APPLE__
+#include <mbgl/platform/darwin/log_nslog.hpp>
+#else
+#include <mbgl/platform/default/log_stderr.hpp>
+#endif
+
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char *argv[]) {
+
+ std::string style_path;
+ double lat = 0, lon = 0;
+ double zoom = 0;
+ double bearing = 0;
+
+ int width = 256;
+ int height = 256;
+ double pixelRatio = 1.0;
+ std::string output = "out.png";
+ std::string cache = "cache.sqlite";
+ std::vector<std::string> classes;
+ std::string token;
+
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("style,s", po::value(&style_path)->required()->value_name("json"), "Map stylesheet")
+ ("lon,x", po::value(&lon)->value_name("degrees")->default_value(lon), "Longitude")
+ ("lat,y", po::value(&lat)->value_name("degrees")->default_value(lat), "Latitude in degrees")
+ ("zoom,z", po::value(&zoom)->value_name("number")->default_value(zoom), "Zoom level")
+ ("bearing,b", po::value(&bearing)->value_name("degrees")->default_value(bearing), "Bearing")
+ ("width,w", po::value(&width)->value_name("pixels")->default_value(width), "Image width")
+ ("height,h", po::value(&height)->value_name("pixels")->default_value(height), "Image height")
+ ("class,c", po::value(&classes)->value_name("name"), "Class name")
+ ("token,t", po::value(&token)->value_name("key")->default_value(token), "Mapbox access token")
+ ("output,o", po::value(&output)->value_name("file")->default_value(output), "Output file name")
+ ("cache,d", po::value(&cache)->value_name("file")->default_value(cache), "Cache database file name")
+ ;
+
+ try {
+ po::variables_map vm;
+ po::store(po::parse_command_line(argc, argv, desc), vm);
+ po::notify(vm);
+ } catch(std::exception& e) {
+ std::cout << "Error: " << e.what() << std::endl << desc;
+ exit(1);
+ }
+
+ std::string style = mbgl::util::read_file(style_path);
+
+ using namespace mbgl;
+
+
+#if __APPLE__
+ Log::Set<NSLogBackend>();
+#else
+ Log::Set<StderrLogBackend>();
+#endif
+
+ CachingHTTPFileSource fileSource(cache);
+
+ // Try to load the token from the environment.
+ if (!token.size()) {
+ const char *token_ptr = getenv("MAPBOX_ACCESS_TOKEN");
+ if (token_ptr) {
+ token = token_ptr;
+ }
+ }
+
+ // Set access token if present
+ if (token.size()) {
+ fileSource.setAccessToken(std::string(token));
+ }
+
+ HeadlessView view;
+ Map map(view, fileSource);
+
+ map.setStyleJSON(style, ".");
+ map.setAppliedClasses(classes);
+
+ view.resize(width, height, pixelRatio);
+ map.resize(width, height, pixelRatio);
+ map.setLonLatZoom(lon, lat, zoom);
+ map.setBearing(bearing);
+
+ std::unique_ptr<uint32_t[]> pixels;
+
+ // Run the loop. It will terminate when we don't have any further listeners.
+ map.run();
+
+ // Get the data from the GPU.
+ pixels = view.readPixels();
+
+ const unsigned int w = width * pixelRatio;
+ const unsigned int h = height * pixelRatio;
+ const std::string image = util::compress_png(w, h, pixels.get());
+ util::write_file(output, image);
+}
diff --git a/bin/render.gyp b/bin/render.gyp
new file mode 100644
index 0000000000..e9a2594236
--- /dev/null
+++ b/bin/render.gyp
@@ -0,0 +1,53 @@
+{
+ 'includes': [
+ '../gyp/common.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'mbgl-render',
+ 'product_name': 'mbgl-render',
+ 'type': 'executable',
+ 'sources': [
+ './render.cpp',
+ ],
+ 'variables' : {
+ 'cflags': [
+ '<@(uv_cflags)',
+ '<@(png_cflags)',
+ '-I<(boost_root)/include',
+ ],
+ 'ldflags': [
+ '<@(glfw3_ldflags)',
+ '<@(uv_ldflags)',
+ '<@(sqlite3_ldflags)',
+ '<@(curl_ldflags)',
+ '<@(png_ldflags)',
+ '<@(uv_static_libs)',
+ '-L<(boost_root)/lib',
+ '-lboost_program_options'
+ ],
+ },
+ 'conditions': [
+ # add libuv include path and OpenGL libs
+ ['OS == "mac"',
+ {
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS': ['<@(cflags)'],
+ 'OTHER_LDFLAGS': ['<@(ldflags)'],
+ },
+ },
+ {
+ 'cflags': ['<@(cflags)'],
+ 'libraries': ['<@(ldflags)'],
+ }],
+ ],
+ 'include_dirs': [ '../src' ],
+ 'dependencies': [
+ '../mapboxgl.gyp:mbgl-standalone',
+ '../mapboxgl.gyp:mbgl-headless',
+ '../mapboxgl.gyp:mbgl-<(platform)',
+ '../mapboxgl.gyp:copy_certificate_bundle',
+ ],
+ },
+ ],
+}
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 245aaf9ea7..45846170c2 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -136,7 +136,14 @@ private:
// Unconditionally performs a render with the current map state.
void render();
- bool async = false;
+ enum class Mode : uint8_t {
+ None, // we're not doing any processing
+ Continuous, // continually updating map
+ Static, // a once-off static image.
+ };
+
+ Mode mode = Mode::None;
+
std::unique_ptr<uv::loop> loop;
std::unique_ptr<uv::worker> workers;
std::thread thread;
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index b0fa60fc12..4053af226d 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -1,5 +1,6 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/util/std.hpp>
@@ -9,12 +10,6 @@
#include <cstring>
#include <cassert>
-#if MBGL_USE_GLX
-#ifdef GLX_ARB_create_context
-static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = nullptr;
-#endif
-#endif
-
#ifdef MBGL_USE_CGL
#include <CoreFoundation/CoreFoundation.h>
@@ -52,88 +47,39 @@ HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display)
void HeadlessView::loadExtensions() {
make_active();
- const std::string extensions = (char *)MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS));
+ const char *extension_ptr = (char *)MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS));
+
+ if (extension_ptr) {
+ const std::string extensions = extension_ptr;
#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");
- assert(gl::BindVertexArray != nullptr);
- assert(gl::DeleteVertexArrays != nullptr);
- assert(gl::GenVertexArrays != nullptr);
- assert(gl::IsVertexArray != nullptr);
- }
+ 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");
+ assert(gl::BindVertexArray != nullptr);
+ assert(gl::DeleteVertexArrays != nullptr);
+ assert(gl::GenVertexArrays != nullptr);
+ assert(gl::IsVertexArray != nullptr);
+ }
#endif
#ifdef MBGL_USE_GLX
- if (extensions.find("GL_ARB_vertex_array_object") != std::string::npos) {
- gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)glXGetProcAddress((const GLubyte *)"glBindVertexArray");
- gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)glXGetProcAddress((const GLubyte *)"glDeleteVertexArrays");
- gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)glXGetProcAddress((const GLubyte *)"glGenVertexArrays");
- gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)glXGetProcAddress((const GLubyte *)"glIsVertexArray");
- assert(gl::BindVertexArray != nullptr);
- assert(gl::DeleteVertexArrays != nullptr);
- assert(gl::GenVertexArrays != nullptr);
- assert(gl::IsVertexArray != nullptr);
- }
-#endif
-
- make_inactive();
-}
-
-
-#if MBGL_USE_GLX
-#ifdef GLX_ARB_create_context
-
-// These are all of the OpenGL Core profile version that we know about.
-struct core_profile_version { int major, minor; };
-static const core_profile_version coreProfileVersions[] = {
- {4, 5},
- {4, 4},
- {4, 3},
- {4, 2},
- {4, 1},
- {4, 0},
- {3, 3},
- {3, 2},
- {3, 1},
- {3, 0},
- {0, 0},
-};
-
-GLXContext createCoreProfile(Display *dpy, GLXFBConfig fbconfig) {
- static bool contextCreationFailed = false;
- GLXContext ctx = 0;
-
- // Set the Error Handler to avoid crashing the program when the context creation fails.
- // It is expected that some context creation attempts fail, e.g. because the OpenGL
- // implementation does not support the version we're requesting.
- int (*previousErrorHandler)(Display *, XErrorEvent *) = XSetErrorHandler([](Display *, XErrorEvent *) {
- contextCreationFailed = true;
- return 0;
- });
-
- // Try to create core profiles from the highest known version on down.
- for (int i = 0; !ctx && coreProfileVersions[i].major; i++) {
- contextCreationFailed = false;
- const int contextFlags[] = {
- GLX_CONTEXT_MAJOR_VERSION_ARB, coreProfileVersions[i].major,
- GLX_CONTEXT_MINOR_VERSION_ARB, coreProfileVersions[i].minor,
- 0
- };
- ctx = glXCreateContextAttribsARB(dpy, fbconfig, 0, True, contextFlags);
- if (contextCreationFailed) {
- ctx = 0;
+ if (extensions.find("GL_ARB_vertex_array_object") != std::string::npos) {
+ gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)glXGetProcAddress((const GLubyte *)"glBindVertexArray");
+ gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)glXGetProcAddress((const GLubyte *)"glDeleteVertexArrays");
+ gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)glXGetProcAddress((const GLubyte *)"glGenVertexArrays");
+ gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)glXGetProcAddress((const GLubyte *)"glIsVertexArray");
+ assert(gl::BindVertexArray != nullptr);
+ assert(gl::DeleteVertexArrays != nullptr);
+ assert(gl::GenVertexArrays != nullptr);
+ assert(gl::IsVertexArray != nullptr);
}
+#endif
}
- // Restore the old error handler.
- XSetErrorHandler(previousErrorHandler);
- return ctx;
+ make_inactive();
}
-#endif
-#endif
void HeadlessView::createContext() {
#if MBGL_USE_CGL
@@ -149,27 +95,15 @@ void HeadlessView::createContext() {
#endif
#if MBGL_USE_GLX
-#ifdef GLX_ARB_create_context
- if (glXCreateContextAttribsARB == nullptr) {
- glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB");
- }
-#endif
-
xDisplay = display_->xDisplay;
fbConfigs = display_->fbConfigs;
-#ifdef GLX_ARB_create_context
- if (glXCreateContextAttribsARB) {
- // Try to create a core profile context.
- glContext = createCoreProfile(xDisplay, fbConfigs[0]);
- }
-#endif
-
if (!glContext) {
// Try to create a legacy context
glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, 0, True);
if (glContext) {
if (!glXIsDirect(xDisplay, glContext)) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "Failed to create direct OpenGL Legacy context");
glXDestroyContext(xDisplay, glContext);
glContext = 0;
}
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index f46e0f558b..ea4115f05c 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -91,6 +91,7 @@ Map::Map(View& view_, FileSource& fileSource_)
view(view_),
#ifndef NDEBUG
mainThread(std::this_thread::get_id()),
+ mapThread(mainThread),
#endif
transform(view_),
fileSource(fileSource_),
@@ -108,7 +109,7 @@ Map::Map(View& view_, FileSource& fileSource_)
}
Map::~Map() {
- if (async) {
+ if (mode == Mode::Continuous) {
stop();
}
@@ -132,11 +133,11 @@ uv::worker &Map::getWorker() {
void Map::start() {
assert(std::this_thread::get_id() == mainThread);
- assert(!async);
+ assert(mode == Mode::None);
// When starting map rendering in another thread, we perform async/continuously
// updated rendering. Only in these cases, we attach the async handlers.
- async = true;
+ mode = Mode::Continuous;
// Reset the flag.
isStopped = false;
@@ -200,7 +201,7 @@ void Map::start() {
void Map::stop(std::function<void ()> callback) {
assert(std::this_thread::get_id() == mainThread);
assert(mainThread != mapThread);
- assert(async);
+ assert(mode == Mode::Continuous);
asyncTerminate->send();
@@ -220,15 +221,16 @@ void Map::stop(std::function<void ()> callback) {
// already finished executing.
thread.join();
- async = false;
+ mode = Mode::None;
}
void Map::run() {
+ if (mode == Mode::None) {
#ifndef NDEBUG
- if (!async) {
mapThread = mainThread;
- }
#endif
+ mode = Mode::Static;
+ }
assert(std::this_thread::get_id() == mapThread);
setup();
@@ -240,18 +242,23 @@ void Map::run() {
// If the map rendering wasn't started asynchronously, we perform one render
// *after* all events have been processed.
- if (!async) {
+ if (mode == Mode::Static) {
render();
#ifndef NDEBUG
mapThread = std::thread::id();
#endif
+ mode = Mode::None;
+ fileSource.clearLoop();
}
}
void Map::rerender() {
- // We only send render events if we want to continuously update the map
- // (== async rendering).
- if (async) {
+ if (mode == Mode::Static) {
+ prepare();
+ } else if (mode == Mode::Continuous) {
+ // We only send render events if we want to continuously update the map
+ // (== async rendering).
+ assert(asyncRender);
asyncRender->send();
}
}