diff --git a/Makefile b/Makefile index 8cb88b327..e18118bde 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 + .PHONY: android android: ./scripts/local_mason.sh && \ @@ -126,6 +134,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: @@ -145,6 +157,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 000000000..feeb4fe3f --- /dev/null +++ b/bin/render.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#if __APPLE__ +#include +#else +#include +#endif + +#include +namespace po = boost::program_options; + +#include +#include +#include + +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 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(); +#else + Log::Set(); +#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 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 000000000..e9a259423 --- /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 750dbbe02..854d8c6a9 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -148,7 +148,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 loop; std::unique_ptr workers; std::thread thread; diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index cba1f9ba6..1b6c06a99 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -152,12 +152,16 @@ void GLFWView::initialize(mbgl::Map *map_) { } if (extensions.find("GL_ARB_get_program_binary") != std::string::npos) { - gl::GetProgramBinary = (gl::PFNGLGETPROGRAMBINARYPROC)glfwGetProcAddress("glGetProgramBinary"); - gl::ProgramBinary = (gl::PFNGLPROGRAMBINARYPROC)glfwGetProcAddress("glProgramBinary"); - gl::ProgramParameteri = (gl::PFNGLPROGRAMPARAMETERIPROC)glfwGetProcAddress("glProgramParameteri"); - assert(gl::GetProgramBinary != nullptr); - assert(gl::ProgramBinary != nullptr); - assert(gl::ProgramParameteri != nullptr); + GLint numBinaryFormats; + MBGL_CHECK_ERROR(glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numBinaryFormats)); + if (numBinaryFormats > 0) { + gl::GetProgramBinary = (gl::PFNGLGETPROGRAMBINARYPROC)glfwGetProcAddress("glGetProgramBinary"); + gl::ProgramBinary = (gl::PFNGLPROGRAMBINARYPROC)glfwGetProcAddress("glProgramBinary"); + gl::ProgramParameteri = (gl::PFNGLPROGRAMPARAMETERIPROC)glfwGetProcAddress("glProgramParameteri"); + assert(gl::GetProgramBinary != nullptr); + assert(gl::ProgramBinary != nullptr); + assert(gl::ProgramParameteri != nullptr); + } } // Require packed depth stencil diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index fcf93c0e5..5bc596690 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -1,5 +1,6 @@ #include #include +#include #include @@ -9,12 +10,6 @@ #include #include -#if MBGL_USE_GLX -#ifdef GLX_ARB_create_context -static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = nullptr; -#endif -#endif - #ifdef MBGL_USE_CGL #include @@ -53,47 +48,36 @@ HeadlessView::HeadlessView(std::shared_ptr display) void HeadlessView::loadExtensions() { make_active(); - std::string extensions; - if (usingGl3OrNewer) { -#ifdef GL_VERSION_3_0 - GLuint num_extensions = 0; - MBGL_CHECK_ERROR(glGetIntegerv(GL_NUM_EXTENSIONS, reinterpret_cast(&num_extensions))); - for (GLuint i = 0; i < num_extensions; i++) { - std::string extension = MBGL_CHECK_ERROR(reinterpret_cast(glGetStringi(GL_EXTENSIONS, i))); - extensions.append(extension); - extensions.append(" "); - } -#else - throw std::runtime_error("Using GL 3.0+ context with out correct headers.\n"); -#endif - } else { - extensions = MBGL_CHECK_ERROR(reinterpret_cast(glGetString(GL_EXTENSIONS))); - } + const char *extensionPtr = (char *)MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)); + + if (extensionPtr) { + const std::string extensions = extensionPtr; #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 + } // HeadlessView requires packed depth stencil gl::isPackedDepthStencilSupported = true; @@ -102,64 +86,6 @@ void HeadlessView::loadExtensions() { 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, bool& usingGl3OrNewer) { - 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. - int i = 0; - for (; !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 (coreProfileVersions[i].major >= 3) { - usingGl3OrNewer = true; - } - - // 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); @@ -174,27 +100,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], usingGl3OrNewer); - } -#endif - - if (glContext == 0) { + 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 5eed5ad69..d1e938427 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -96,6 +96,7 @@ Map::Map(View& view_, FileSource& fileSource_) view(view_), #ifndef NDEBUG mainThread(std::this_thread::get_id()), + mapThread(mainThread), #endif transform(view_), fileSource(fileSource_), @@ -113,7 +114,7 @@ Map::Map(View& view_, FileSource& fileSource_) } Map::~Map() { - if (async) { + if (mode == Mode::Continuous) { stop(); } @@ -137,11 +138,11 @@ uv::worker &Map::getWorker() { void Map::start(bool startPaused) { 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; @@ -212,7 +213,7 @@ void Map::start(bool startPaused) { void Map::stop(std::function callback) { assert(std::this_thread::get_id() == mainThread); assert(mainThread != mapThread); - assert(async); + assert(mode == Mode::Continuous); asyncTerminate->send(); @@ -234,12 +235,12 @@ void Map::stop(std::function callback) { // already finished executing. thread.join(); - async = false; + mode = Mode::None; } void Map::pause(bool waitForPause) { assert(std::this_thread::get_id() == mainThread); - assert(async); + assert(mode == Mode::Continuous); mutexRun.lock(); pausing = true; mutexRun.unlock(); @@ -257,7 +258,7 @@ void Map::pause(bool waitForPause) { void Map::resume() { assert(std::this_thread::get_id() == mainThread); - assert(async); + assert(mode == Mode::Continuous); mutexRun.lock(); pausing = false; @@ -270,21 +271,22 @@ void Map::run() { COFFEE_TRY() { #endif + if (mode == Mode::None) { #ifndef NDEBUG - if (!async) { mapThread = mainThread; - } #endif + mode = Mode::Static; + } assert(std::this_thread::get_id() == mapThread); - if (async) { + if (mode == Mode::Continuous) { checkForPause(); } setup(); prepare(); - if (async) { + if (mode == Mode::Continuous) { terminating = false; while(!terminating) { uv_run(**loop, UV_RUN_DEFAULT); @@ -299,11 +301,13 @@ 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(); } #ifdef __ANDROID__ } COFFEE_CATCH() { @@ -334,9 +338,12 @@ void Map::checkForPause() { } 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(); } } @@ -373,7 +380,7 @@ void Map::setStyleURL(const std::string &url) { // TODO: Make threadsafe. styleURL = url; - if (async) { + if (mode == Mode::Continuous) { stop(); start(); } diff --git a/src/mbgl/shader/shader.cpp b/src/mbgl/shader/shader.cpp index c43863b52..62edf1d38 100644 --- a/src/mbgl/shader/shader.cpp +++ b/src/mbgl/shader/shader.cpp @@ -5,8 +5,9 @@ #include #include -#include #include +#include +#include using namespace mbgl; @@ -25,35 +26,26 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo // Load binary shader if it exists bool skipCompile = false; if (!binaryFileName.empty() && (gl::ProgramBinary != nullptr)) { - FILE *binaryFile = fopen(binaryFileName.c_str(), "rb"); - if (binaryFile != nullptr) { - GLsizei binaryLength; - GLenum binaryFormat; - bool lengthOk = fread(&binaryLength, sizeof(binaryLength), 1, binaryFile) == 1; - bool formatOk = fread(&binaryFormat, sizeof(binaryFormat), 1, binaryFile) == 1; + std::ifstream binaryFile(binaryFileName, std::ios::in | std::ios::binary); - if (lengthOk && formatOk && binaryLength > 0) { - std::unique_ptr binary = mbgl::util::make_unique(binaryLength); + GLsizei binaryLength; + GLenum binaryFormat; + binaryFile.read(reinterpret_cast(&binaryLength), sizeof(binaryLength)); + binaryFile.read(reinterpret_cast(&binaryFormat), sizeof(binaryFormat)); - if (binary != nullptr) { - bool binaryOk = fread(binary.get(), binaryLength, 1, binaryFile) == 1; + std::unique_ptr binary = mbgl::util::make_unique(binaryLength); + binaryFile.read(binary.get(), binaryLength); - if (binaryOk) { - MBGL_CHECK_ERROR(gl::ProgramBinary(program, binaryFormat, binary.get(), binaryLength)); + MBGL_CHECK_ERROR(gl::ProgramBinary(program, binaryFormat, binary.get(), binaryLength)); - // Check if the binary was valid - GLint status; - MBGL_CHECK_ERROR(glGetProgramiv(program, GL_LINK_STATUS, &status)); - if (status == GL_TRUE) { - skipCompile = true; - } - } - } - } - - fclose(binaryFile); - binaryFile = nullptr; + // Check if the binary was valid + GLint status; + MBGL_CHECK_ERROR(glGetProgramiv(program, GL_LINK_STATUS, &status)); + if (status == GL_TRUE) { + skipCompile = true; } + + binaryFile.close(); } GLuint vertShader = 0; @@ -150,7 +142,7 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) { *shader = MBGL_CHECK_ERROR(glCreateShader(type)); const GLchar *strings[] = { source }; - const GLsizei lengths[] = { (GLsizei)strlen(source) }; + const GLsizei lengths[] = { (GLsizei)std::strlen(source) }; MBGL_CHECK_ERROR(glShaderSource(*shader, 1, strings, lengths)); MBGL_CHECK_ERROR(glCompileShader(*shader)); @@ -189,19 +181,14 @@ Shader::~Shader() { MBGL_CHECK_ERROR(glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binaryLength)); if (binaryLength > 0) { std::unique_ptr binary = mbgl::util::make_unique(binaryLength); - if (binary != nullptr) { - MBGL_CHECK_ERROR(gl::GetProgramBinary(program, binaryLength, NULL, &binaryFormat, binary.get())); + MBGL_CHECK_ERROR(gl::GetProgramBinary(program, binaryLength, NULL, &binaryFormat, binary.get())); - // Write the binary to a file - FILE *binaryFile = fopen(binaryFileName.c_str(), "wb"); - if (binaryFile != nullptr) { - fwrite(&binaryLength, sizeof(binaryLength), 1, binaryFile); - fwrite(&binaryFormat, sizeof(binaryFormat), 1, binaryFile); - fwrite(binary.get(), binaryLength, 1, binaryFile); - fclose(binaryFile); - binaryFile = nullptr; - } - } + // Write the binary to a file + std::ofstream binaryFile(binaryFileName, std::ios::out | std::ios::trunc | std::ios::binary); + binaryFile.write(reinterpret_cast(&binaryLength), sizeof(binaryLength)); + binaryFile.write(reinterpret_cast(&binaryFormat), sizeof(binaryFormat)); + binaryFile.write(binary.get(), binaryLength); + binaryFile.close(); } }