15
Makefile
15
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
|
||||
|
||||
115
bin/render.cpp
Normal file
115
bin/render.cpp
Normal file
@@ -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);
|
||||
}
|
||||
53
bin/render.gyp
Normal file
53
bin/render.gyp
Normal file
@@ -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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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,89 +47,40 @@ 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the old error handler.
|
||||
XSetErrorHandler(previousErrorHandler);
|
||||
return ctx;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void HeadlessView::createContext() {
|
||||
#if MBGL_USE_CGL
|
||||
CGLError error = CGLCreateContext(display_->pixelFormat, NULL, &glContext);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user