diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000000..6cde9e652c --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,25 @@ +environment: + MINGW_PATH: C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64 + CURL_VERSION: curl-7.63.0 + ZLIB_VERSION: zlib-1.2.11 +install: + - set PATH=C:\cmake-install\bin;C:\cmake-install\lib;%MINGW_PATH%\bin;%PATH% + - appveyor DownloadFile "https://www.zlib.net/%ZLIB_VERSION%.tar.gz" + - 7z x -o. %ZLIB_VERSION%.tar.gz + - 7z x -oC:\cmake-install %ZLIB_VERSION%.tar + - cd C:\cmake-install\%ZLIB_VERSION% + - cmake -H. -Bbuild -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:\cmake-install -DCMAKE_SH="CMAKE_SH-NOTFOUND" + - cmake --build build -- -j %NUMBER_OF_PROCESSORS% + - cmake --build build --target install + - appveyor DownloadFile "https://curl.haxx.se/download/%CURL_VERSION%.tar.gz" + - 7z x -o. %CURL_VERSION%.tar.gz + - 7z x -oC:\cmake-install %CURL_VERSION%.tar + - cd C:\cmake-install\%CURL_VERSION% + - cmake -H. -Bbuild -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:\cmake-install -DBUILD_CURL_EXE=OFF -DBUILD_TESTING=OFF -DCMAKE_USE_WINSSL=ON -DCMAKE_SH="CMAKE_SH-NOTFOUND" + - cmake --build build -- -j %NUMBER_OF_PROCESSORS% + - cmake --build build --target install + - cd C:\projects\tangram-es +before_build: + - git submodule update --init --recursive +build_script: + - mingw32-make windows -j %NUMBER_OF_PROCESSORS% CMAKE_OPTIONS="-DCURL_LIBRARIES=C:/cmake-install/bin/libcurl.dll -DCURL_INCLUDE_DIRS=C:/cmake-install/include" \ No newline at end of file diff --git a/Makefile b/Makefile index 91463097b4..030452b079 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ all: android osx ios .PHONY: clean-ios .PHONY: clean-rpi .PHONY: clean-linux +.PHONY: clean-windows .PHONY: clean-benchmark .PHONY: clean-shaders .PHONY: clean-tizen-arm @@ -21,6 +22,7 @@ all: android osx ios .PHONY: ios-docs .PHONY: rpi .PHONY: linux +.PHONY: windows .PHONY: benchmark .PHONY: tests .PHONY: cmake-osx @@ -28,6 +30,7 @@ all: android osx ios .PHONY: cmake-ios .PHONY: cmake-rpi .PHONY: cmake-linux +.PHONY: cmake-windows ANDROID_BUILD_DIR = platforms/android/tangram/build OSX_BUILD_DIR = build/osx @@ -36,6 +39,7 @@ IOS_BUILD_DIR = build/ios IOS_DOCS_BUILD_DIR = build/ios-docs RPI_BUILD_DIR = build/rpi LINUX_BUILD_DIR = build/linux +WINDOWS_BUILD_DIR = build/windows TESTS_BUILD_DIR = build/tests BENCH_BUILD_DIR = build/bench TIZEN_ARM_BUILD_DIR = build/tizen-arm @@ -99,6 +103,14 @@ LINUX_CMAKE_PARAMS = \ -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE \ ${CMAKE_OPTIONS} +WINDOWS_CMAKE_PARAMS = \ + -G "MinGW Makefiles" \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DTANGRAM_PLATFORM=windows \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE \ + -DCMAKE_SH="CMAKE_SH-NOTFOUND" \ + ${CMAKE_OPTIONS} + ifndef TIZEN_PROFILE TIZEN_PROFILE=mobile endif @@ -128,7 +140,7 @@ TIZEN_X86_CMAKE_PARAMS = \ ${CMAKE_OPTIONS} clean: clean-android clean-osx clean-ios clean-rpi clean-tests clean-xcode clean-linux clean-shaders \ - clean-tizen-arm clean-tizen-x86 + clean-tizen-arm clean-tizen-x86 clean-windows clean-android: rm -rf platforms/android/build @@ -148,6 +160,9 @@ clean-rpi: clean-linux: rm -rf ${LINUX_BUILD_DIR} +clean-windows: + rm -rf ${WINDOWS_BUILD_DIR} + clean-xcode: rm -rf ${OSX_XCODE_BUILD_DIR} @@ -253,9 +268,15 @@ cmake-rpi: linux: cmake-linux cmake --build ${LINUX_BUILD_DIR} +windows: cmake-windows + cmake --build ${WINDOWS_BUILD_DIR} + cmake-linux: cmake -H. -B${LINUX_BUILD_DIR} ${LINUX_CMAKE_PARAMS} +cmake-windows: + cmake -H. -B${WINDOWS_BUILD_DIR} ${WINDOWS_CMAKE_PARAMS} + tizen-arm: cmake-tizen-arm cmake --build ${TIZEN_ARM_BUILD_DIR} diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 74219ec35f..a65f0428cc 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -1,119 +1,49 @@ function(check_unsupported_compiler_version) + set(MIN_GCC 4.9) + set(MIN_CLANG 3.4) + set(MIN_APPLECLANG 6.0) - set(MIN_GCC 4.9) - set(MIN_CLANG 3.4) - set(MIN_APPLECLANG 6.0) - - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MIN_GCC}) - message(FATAL_ERROR "Your GCC version does not support C++14, please install version ${MIN_GCC} or higher") - endif() - elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MIN_CLANG}) - message(FATAL_ERROR "Your Clang version does not support C++14, please install version ${MIN_CLANG} or higher") - endif() - elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MIN_APPLECLANG}) - message(FATAL_ERROR "Your Xcode version does not support C++14, please install version ${MIN_APPLECLANG} or higher") - endif() - else() - message(WARNING "Compilation has only been tested with Clang, AppleClang, and GCC") + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MIN_GCC}) + message(FATAL_ERROR "Your GCC version does not support C++14, please install version ${MIN_GCC} or higher") endif() - + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MIN_CLANG}) + message(FATAL_ERROR "Your Clang version does not support C++14, please install version ${MIN_CLANG} or higher") + endif() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MIN_APPLECLANG}) + message(FATAL_ERROR "Your Xcode version does not support C++14, please install version ${MIN_APPLECLANG} or higher") + endif() + else() + message(WARNING "Compilation has only been tested with Clang, AppleClang, and GCC") + endif() endfunction(check_unsupported_compiler_version) function(get_nextzen_api_key KEY_RESULT) - - set(${KEY_RESULT} $ENV{NEXTZEN_API_KEY} PARENT_SCOPE) - - if(${KEY_RESULT} STREQUAL "") - message(SEND_ERROR - "Make sure to provide an api key to build the demo application, " - "you can create an API key at https://developers.nextzen.org/. " - "Then run 'export NEXTZEN_API_KEY yourKeyHere' or specify `NEXTZEN_API_KEY=yourKeyHere` as an argument to the make command") - return() - endif() - + set(${KEY_RESULT} $ENV{NEXTZEN_API_KEY} PARENT_SCOPE) + if(${KEY_RESULT} STREQUAL "") + message(SEND_ERROR + "Make sure to provide an api key to build the demo application, " + "you can create an API key at https://developers.nextzen.org/. " + "Then run 'export NEXTZEN_API_KEY yourKeyHere' or specify `NEXTZEN_API_KEY=yourKeyHere` as an argument to the make command") + return() + endif() endfunction(get_nextzen_api_key) - -function(find_sources_and_include_directories HEADERS_PATH SOURCES_PATH) - include_recursive_dirs(${HEADERS_PATH}) - file(GLOB_RECURSE FOUND_SOURCES ${SOURCES_PATH}) - - set(SOURCES - ${SOURCES} - ${FOUND_SOURCES} - CACHE INTERNAL "sources" FORCE) - list(REMOVE_DUPLICATES SOURCES) -endfunction(find_sources_and_include_directories) - -function(include_recursive_dirs HEADERS_PATH) - file(GLOB_RECURSE FOUND_HEADERS ${HEADERS_PATH}) - - set(INCLUDE_DIRS "") - foreach(_headerFile ${FOUND_HEADERS}) - get_filename_component(_dir ${_headerFile} PATH) - list(APPEND INCLUDE_DIRS ${_dir}) - endforeach() - list(REMOVE_DUPLICATES INCLUDE_DIRS) - - include_directories(${INCLUDE_DIRS}) - - set(HEADERS - ${HEADERS} - ${FOUND_HEADERS} - CACHE INTERNAL "headers" FORCE) - list(REMOVE_DUPLICATES HEADERS) -endfunction(include_recursive_dirs) - -function(check_and_link_libraries TARGET) - foreach(_lib ${ARGN}) - string(TOUPPER ${_lib} LIB) - find_package (${_lib}) - if(${LIB}_FOUND) - include_directories(${${LIB}_INCLUDE_DIR}) - target_link_libraries(${TARGET} ${${LIB}_LIBRARIES}) - else() - message(SEND_ERROR "You NEED ${_lib} library.") - return() - endif () - endforeach() -endfunction(check_and_link_libraries) - macro(add_bundle_resources RESOURCE_LIST RESOURCE_DIR RESOURCE_BASE) - - file(GLOB_RECURSE FULL_RESOURCE_PATHS "${RESOURCE_DIR}/[^.]**") - foreach(_full_resource_path ${FULL_RESOURCE_PATHS}) - file(RELATIVE_PATH REL_RESOURCE_PATH ${RESOURCE_DIR} ${_full_resource_path}) - get_filename_component(REL_RESOURCE_DIR ${REL_RESOURCE_PATH} PATH) - set_source_files_properties(${_full_resource_path} PROPERTIES MACOSX_PACKAGE_LOCATION "${RESOURCE_BASE}/${REL_RESOURCE_DIR}") - # message(STATUS "resource at: ${_full_resource_path}\n remapped to: ${RESOURCE_BASE}/${REL_RESOURCE_DIR}") - endforeach() - list(APPEND ${RESOURCE_LIST} ${FULL_RESOURCE_PATHS}) - + file(GLOB_RECURSE FULL_RESOURCE_PATHS "${RESOURCE_DIR}/[^.]**") + foreach(_full_resource_path ${FULL_RESOURCE_PATHS}) + file(RELATIVE_PATH REL_RESOURCE_PATH ${RESOURCE_DIR} ${_full_resource_path}) + get_filename_component(REL_RESOURCE_DIR ${REL_RESOURCE_PATH} PATH) + set_source_files_properties(${_full_resource_path} PROPERTIES MACOSX_PACKAGE_LOCATION "${RESOURCE_BASE}/${REL_RESOURCE_DIR}") + # message(STATUS "resource at: ${_full_resource_path}\n remapped to: ${RESOURCE_BASE}/${REL_RESOURCE_DIR}") + endforeach() + list(APPEND ${RESOURCE_LIST} ${FULL_RESOURCE_PATHS}) endmacro(add_bundle_resources) macro(add_resources TARGET RESOURCE_DIR DEST_DIR) - - add_custom_command(TARGET ${TARGET} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${RESOURCE_DIR} ${CMAKE_BINARY_DIR}/${DEST_DIR}) - + add_custom_command(TARGET ${TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${RESOURCE_DIR} ${CMAKE_BINARY_DIR}/${DEST_DIR}) endmacro(add_resources) - -macro(target_add_framework APPNAME FWNAME) - find_library(${FWNAME}_FRAMEWORK ${FWNAME}) - if(${${FWNAME}_FRAMEWORK}) - target_link_libraries(${APPNAME} PRIVATE ${FRAMEWORK_${FWNAME}}) - message(STATUS "Framework ${FWNAME} found") - else() - message(SEND_ERROR "Framework ${FWNAME} not found") - endif() -endmacro(target_add_framework) - -# This little macro lets you set any XCode specific property -macro (set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE) - set_property (TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE}) -endmacro (set_xcode_property) - diff --git a/core/deps/CMakeLists.txt b/core/deps/CMakeLists.txt index a9fffb520d..703d0f1f71 100644 --- a/core/deps/CMakeLists.txt +++ b/core/deps/CMakeLists.txt @@ -82,6 +82,10 @@ if (TANGRAM_MBTILES_DATASOURCE) if (ANDROID) target_compile_definitions(sqlite3 PRIVATE _FILE_OFFSET_BITS=32) endif() + if (WIN32) + target_compile_options(SQLiteCpp PRIVATE -fno-stack-protector) + target_compile_options(sqlite3 PRIVATE -fno-stack-protector) + endif() endif() ## double-conversion ## diff --git a/core/src/labels/fadeEffect.h b/core/src/labels/fadeEffect.h index 7fce72aeff..f5b12042d8 100644 --- a/core/src/labels/fadeEffect.h +++ b/core/src/labels/fadeEffect.h @@ -1,6 +1,9 @@ #pragma once #include +#ifndef M_PI // M_PI is non-standard since c++99 +#define M_PI (3.14159265358979323846264338327950288) +#endif namespace Tangram { diff --git a/core/src/util/ease.h b/core/src/util/ease.h index a1a0bb2b4e..ab5353daff 100644 --- a/core/src/util/ease.h +++ b/core/src/util/ease.h @@ -3,6 +3,9 @@ #include "map.h" #include +#ifndef M_PI // M_PI is non-standard since c++99 +#define M_PI (3.14159265358979323846264338327950288) +#endif #include namespace Tangram { diff --git a/core/src/util/rasterize.h b/core/src/util/rasterize.h index 4a3812a224..cd4f8c90fd 100644 --- a/core/src/util/rasterize.h +++ b/core/src/util/rasterize.h @@ -2,6 +2,9 @@ #include "glm/vec2.hpp" #include +#ifndef M_PI // M_PI is non-standard since c++99 +#define M_PI (3.14159265358979323846264338327950288) +#endif #include namespace Tangram { diff --git a/core/src/view/viewConstraint.h b/core/src/view/viewConstraint.h index 3a527484b0..9e13431e00 100644 --- a/core/src/view/viewConstraint.h +++ b/core/src/view/viewConstraint.h @@ -1,5 +1,8 @@ #pragma once #include +#ifndef M_PI // M_PI is non-standard since c++99 +#define M_PI (3.14159265358979323846264338327950288) +#endif namespace Tangram { diff --git a/platforms/common/glfwApp.cpp b/platforms/common/glfwApp.cpp index 94470c08ec..6ec73e3114 100644 --- a/platforms/common/glfwApp.cpp +++ b/platforms/common/glfwApp.cpp @@ -1,8 +1,15 @@ #include "glfwApp.h" + #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #include "imgui_stl.h" + +#ifdef TANGRAM_WINDOWS +#define GLFW_INCLUDE_NONE +#include +#endif // TANGRAM_WINDOWS + #define GLFW_INCLUDE_GLEXT #include #include @@ -181,6 +188,10 @@ void create(std::unique_ptr p, int w, int h) { glfwMakeContextCurrent(main_window); glfwSwapInterval(1); // Enable vsync +#ifdef TANGRAM_WINDOWS + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); +#endif + // Set input callbacks glfwSetFramebufferSizeCallback(main_window, framebufferResizeCallback); glfwSetMouseButtonCallback(main_window, mouseButtonCallback); diff --git a/platforms/common/platform_gl.h b/platforms/common/platform_gl.h index 9c62f40da7..70e7f90a92 100644 --- a/platforms/common/platform_gl.h +++ b/platforms/common/platform_gl.h @@ -38,10 +38,17 @@ extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT; #define glBindVertexArray glBindVertexArrayAPPLE #endif // TANGRAM_OSX -#ifdef TANGRAM_LINUX +#if defined(TANGRAM_LINUX) || defined(TANGRAM_WINDOWS) #define GL_GLEXT_PROTOTYPES +#ifdef TANGRAM_WINDOWS +#define GLFW_INCLUDE_NONE +#include +// Add missing stuff +#define glDepthRangef(a, b) glDepthRange((double)(a), (double)(b)) +#define glClearDepthf(a) glClearDepth((double)(a)) +#endif // TANGRAM_WINDOWS #include -#endif // TANGRAM_LINUX +#endif // defined(TANGRAM_LINUX) || defined(TANGRAM_WINDOWS) #ifdef TANGRAM_RPI // Broadcom library for direct GPU access diff --git a/platforms/common/urlClient.cpp b/platforms/common/urlClient.cpp index 07132dfd9c..251b16daac 100644 --- a/platforms/common/urlClient.cpp +++ b/platforms/common/urlClient.cpp @@ -4,17 +4,28 @@ #include #include #include -#include -#include constexpr char const* requestCancelledError = "Request cancelled"; +#ifdef DEBUG +#define CURL_CHECK(STMT) do { CURLcode err = STMT; assert(err == CURLE_OK); } while (0) +#define CURLM_CHECK(STMT) do { \ + CURLMcode err = STMT; \ + if (err != CURLM_OK) \ + LOGE("%s", curl_multi_strerror(err)); \ + assert(err == CURLM_OK); \ +} while (0) +#else +#define CURL_CHECK(STMT) do { STMT; } while (0) +#define CURLM_CHECK(STMT) do { STMT; } while (0) +#endif + namespace Tangram { struct CurlGlobals { CurlGlobals() { LOGD("curl global init"); - curl_global_init(CURL_GLOBAL_ALL); + CURL_CHECK(curl_global_init(CURL_GLOBAL_ALL)); } ~CurlGlobals() { LOGD("curl global shutdown"); @@ -50,19 +61,24 @@ struct UrlClient::Task { Task(const Options& _options) { // Set up an easy handle for reuse. handle = curl_easy_init(); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &curlWriteCallback); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, this); - curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(handle, CURLOPT_HEADER, 0L); - curl_easy_setopt(handle, CURLOPT_VERBOSE, 0L); - curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip"); - curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curlErrorString); - curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT_MS, _options.connectionTimeoutMs); - curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, _options.requestTimeoutMs); - curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 20); - curl_easy_setopt(handle, CURLOPT_TCP_NODELAY, 1); + assert(handle != nullptr); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &curlWriteCallback)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_WRITEDATA, this)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_HEADER, 0L)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_VERBOSE, 0L)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip")); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curlErrorString)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1L)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT_MS, _options.connectionTimeoutMs)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, _options.requestTimeoutMs)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 20)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_TCP_NODELAY, 1)); +#ifdef TANGRAM_WINDOWS + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L)); + CURL_CHECK(curl_easy_setopt(handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4)); +#endif } void setup() { @@ -98,18 +114,13 @@ UrlClient::UrlClient(Options options) : m_options(options) { // Start the curl thread m_curlHandle = curl_multi_init(); + assert(m_curlHandle != nullptr); m_curlWorker = std::make_unique(&UrlClient::curlLoop, this); m_curlRunning = true; // Init at least one task to avoid checking whether m_tasks is empty in // startPendingRequests() m_tasks.emplace_back(m_options); - - // Using a pipe to notify select() in curl-thread of new requests.. - // https://www.linuxquestions.org/questions/programming-9/exit-from-blocked-pselect-661200/ - if (pipe(m_requestNotify) < 0) { - LOGE("Could not initialize select breaker!"); - } } UrlClient::~UrlClient() { @@ -133,7 +144,6 @@ UrlClient::~UrlClient() { // Stop the curl threads. m_curlRunning = false; - curlWakeUp(); m_curlWorker->join(); @@ -145,24 +155,12 @@ UrlClient::~UrlClient() { // This is probably not needed since all task have been canceled and joined for (auto& task : m_tasks) { if (task.active) { - curl_multi_remove_handle(m_curlHandle, task.handle); + CURLM_CHECK(curl_multi_remove_handle(m_curlHandle, task.handle)); } } curl_multi_cleanup(m_curlHandle); } -void UrlClient::curlWakeUp() { - - if (!m_curlNotified) { - if (write(m_requestNotify[1], "\0", 1) <= 0) { - // err - return; - } - //LOG("wake up!"); - m_curlNotified = true; - } -} - UrlClient::RequestId UrlClient::addRequest(const std::string& _url, UrlCallback _onComplete) { auto id = ++m_requestCount; @@ -175,7 +173,6 @@ UrlClient::RequestId UrlClient::addRequest(const std::string& _url, UrlCallback std::lock_guard lock(m_requestMutex); m_requests.push_back(request); } - curlWakeUp(); return id; } @@ -245,7 +242,7 @@ void UrlClient::startPendingRequests() { LOGD("Tasks %d - starting request for url: %s", int(m_activeTasks), url); - curl_multi_add_handle(m_curlHandle, task.handle); + CURLM_CHECK(curl_multi_add_handle(m_curlHandle, task.handle)); } } @@ -265,7 +262,7 @@ void UrlClient::curlLoop() { int maxfd = -1; long curl_timeo = -1; - curl_multi_timeout(m_curlHandle, &curl_timeo); + CURLM_CHECK(curl_multi_timeout(m_curlHandle, &curl_timeo)); if (curl_timeo >= 0) { timeout.tv_usec = 0; @@ -280,47 +277,38 @@ void UrlClient::curlLoop() { } else { timeout.tv_usec = (curl_timeo % 1000) * 1000; } - //printf ("Timeout %ld.%06ld\n", timeout.tv_sec, timeout.tv_usec); } - // Get file descriptors from the transfers - CURLMcode mc = curl_multi_fdset(m_curlHandle, &fdread, &fdwrite, &fdexcep, - &maxfd); - if (mc != CURLM_OK) { - LOGE("curl_multi_fdset() failed, code %d.", mc); - continue; + CURLM_CHECK(curl_multi_fdset(m_curlHandle, &fdread, &fdwrite, &fdexcep, &maxfd)); + + int ready; + if (maxfd == -1) { +#ifdef TANGRAM_WINDOWS + Sleep(100); + ready = 0; +#else + ready = select(0, NULL, NULL, NULL, &timeout); +#endif + } else { + // Wait for transfers + // On success the value of maxfd is guaranteed to be >= -1. We call + // select(maxfd + 1, ...); specially in case of (maxfd == -1) there are + // no fds ready yet so we call select(0, ...) to sleep 100ms, which is + // the minimum suggested value in the curl_multi_fdset() doc. + ready = select(maxfd + 2, &fdread, &fdwrite, &fdexcep, &timeout); } - // Listen on requestNotify to break select when new requests are added. - FD_SET(m_requestNotify[0], &fdread); - - // Wait for transfers - // On success the value of maxfd is guaranteed to be >= -1. We call - // select(maxfd + 1, ...); specially in case of (maxfd == -1) there are - // no fds ready yet so we call select(0, ...) to sleep 100ms, which is - // the minimum suggested value in the curl_multi_fdset() doc. - int ready = select(maxfd + 2, &fdread, &fdwrite, &fdexcep, &timeout); - if (ready == -1) { LOGE("select() error!"); + assert(false); continue; - } else { - if (FD_ISSET(m_requestNotify[0], &fdread)) { - // Clear notify fd - char buffer[1]; - int n = read(m_requestNotify[0], buffer, sizeof(buffer)); - if (n <= 0) { LOGE("Read request notify %d", n); } - //LOG("Got request notifies %d %d", n, m_curlNotified); - m_curlNotified = false; - } - // Create tasks from request queue startPendingRequests(); // int activeRequests = 0; - curl_multi_perform(m_curlHandle, &activeRequests); + CURLM_CHECK(curl_multi_perform(m_curlHandle, &activeRequests)); } while (true) { @@ -332,7 +320,7 @@ void UrlClient::curlLoop() { //LOG("Messages left: %d / active %d", msgs_left, m_activeTasks); // Easy handle must be removed for reuse - curl_multi_remove_handle(m_curlHandle, msg->easy_handle); + CURLM_CHECK(curl_multi_remove_handle(m_curlHandle, msg->easy_handle)); // NB: DONE is the only defined message type. if (msg->msg != CURLMSG_DONE) { @@ -372,7 +360,7 @@ void UrlClient::curlLoop() { response.error = requestCancelledError; } else { - LOGW("Failed with error for url: %s", task.curlErrorString, url); + LOGW("Failed with error %s for url: %s", task.curlErrorString, url); response.error = task.curlErrorString; } diff --git a/platforms/common/urlClient.h b/platforms/common/urlClient.h index d22fd87c39..b13e501224 100644 --- a/platforms/common/urlClient.h +++ b/platforms/common/urlClient.h @@ -45,7 +45,6 @@ class UrlClient { struct Task; void curlLoop(); - void curlWakeUp(); void startPendingRequests(); @@ -55,7 +54,6 @@ class UrlClient { void *m_curlHandle = nullptr; bool m_curlRunning = false; - bool m_curlNotified = false; std::unique_ptr m_curlWorker; AsyncWorker m_dispatcher; @@ -70,9 +68,6 @@ class UrlClient { // RequestIds std::atomic_uint64_t m_requestCount{0}; - - // File descriptors to break waiting select. - int m_requestNotify[2] = { -1, -1 }; }; } // namespace Tangram diff --git a/platforms/linux/src/main.cpp b/platforms/linux/src/main.cpp index ccc36ba9c3..e4a91f06ee 100644 --- a/platforms/linux/src/main.cpp +++ b/platforms/linux/src/main.cpp @@ -24,9 +24,9 @@ int main(int argc, char* argv[]) { if (getcwd(pathBuffer, PATH_MAX) != nullptr) { baseUrl = Url(std::string(pathBuffer) + "/").resolved(baseUrl); } - + LOG("Base URL: %s", baseUrl.string().c_str()); - + Url sceneUrl = Url(GlfwApp::sceneFile).resolved(baseUrl); GlfwApp::sceneFile = sceneUrl.string(); @@ -39,4 +39,5 @@ int main(int argc, char* argv[]) { // Clean up. GlfwApp::destroy(); + return 0; } diff --git a/platforms/osx/src/main.mm b/platforms/osx/src/main.mm index ec949b39af..19d1d12394 100644 --- a/platforms/osx/src/main.mm +++ b/platforms/osx/src/main.mm @@ -103,4 +103,6 @@ int main(int argc, char* argv[]) { // Clean up. GlfwApp::destroy(); + + return 0; } diff --git a/platforms/windows/README.md b/platforms/windows/README.md new file mode 100644 index 0000000000..2dec372b08 --- /dev/null +++ b/platforms/windows/README.md @@ -0,0 +1,57 @@ +# Windows + +## Setup + +This project uses CMake (minimum version 3.0), you can download it [here](http://www.cmake.org/download/). + +Make sure to update git submodules before you build: + +```bash +git submodule update --init +``` + +Builds were tested under MinGW-w64 (required). You will need to install/build zlib and curl manually. + +The demo application uses the Nextzen vector tile service, so you will need a Nextzen API key to build and run the demo. + + 1. Visit https://developers.nextzen.org/ to get an API key. + + 2. Setup an environment variable (`SET NEXTZEN_API_KEY=YOUR_API_KEY` or via system settings) to point to your API key. + +## Build + + The **only** supported toolchain to build Tangram ES on Windows is currently MinGW (MinGW GCC, POSIX threads). Once you have installed MinGW, make sure that the MinGW bin folder is added to your `PATH` environment variable. + + 1. Download zlib sources from https://zlib.net/ + + 2. Download curl sources fom https://curl.haxx.se/download.html + +### Zlib and Curl under MinGW-w64 + +CMake typically installs everything under Program Files, which requires elevated privileges. It is encouraged you to make dedicated directory, e.g. `c:/cmake-install`. To make it work, set `CMAKE_PREFIX_PATH` env var to this directory. The build has been tested with curl 7.63 and zlib 1.2. +Make sure that this "cmake install" bin folder is added to your `PATH` environment variable. + +From the zlib source directory: +``` bat +cmake -H. -Bbuild -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=c:/cmake-install +cmake --build build +cmake --build build --target install +``` +From the curl source directory: +``` bat +cmake -H. -Bbuild -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=c:/cmake-install -DBUILD_CURL_EXE=OFF -DBUILD_TESTING=OFF -DCMAKE_USE_WINSSL=ON +cmake --build build +cmake --build build --target install +``` + +### Build Tangram + +In the root directory type `mingw32-make windows`. You can append `DEBUG=1` or `RELEASE=1` to choose the build type. + +## Running app + +You can open a different YAML scene file by using the argument `-f`: + +``` +tangram.exe -f "C:\Users\user\dev\tangram-es\build\windows\res\scene.yaml" +``` diff --git a/platforms/windows/config.cmake b/platforms/windows/config.cmake new file mode 100644 index 0000000000..f4a0332a2d --- /dev/null +++ b/platforms/windows/config.cmake @@ -0,0 +1,60 @@ +check_unsupported_compiler_version() + +add_definitions(-DTANGRAM_WINDOWS) + +find_package(OpenGL REQUIRED) + +if(NOT DEFINED CURL_LIBRARIES OR NOT DEFINED CURL_INCLUDE_DIRS) + find_package(CURL REQUIRED) +endif() + +include(cmake/glfw.cmake) + +add_executable(tangram + platforms/windows/src/windowsPlatform.cpp + platforms/windows/src/main.cpp + platforms/common/platform_gl.cpp + platforms/common/urlClient.cpp + platforms/common/glfwApp.cpp + platforms/common/imgui_impl_glfw.cpp + platforms/common/imgui_impl_opengl3.cpp + platforms/common/glfw/deps/glad.c +) + +add_resources(tangram "${PROJECT_SOURCE_DIR}/scenes" "res") + +add_subdirectory(platforms/common/imgui) + +target_include_directories(tangram + PRIVATE + platforms/common + platforms/common/glfw/deps + ${CURL_INCLUDE_DIRS} +) + +target_link_libraries(tangram + PRIVATE + tangram-core + glfw + imgui + ${GLFW_LIBRARIES} + ${OPENGL_LIBRARIES} + ${CURL_LIBRARIES} + -pthread + wsock32 ws2_32 crypt32 wldap32 +) + +target_compile_options(tangram + PRIVATE + -std=c++14 + -fno-omit-frame-pointer + -Wall + -Wreturn-type + -Wsign-compare + -Wignored-qualifiers + -Wtype-limits + -Wmissing-field-initializers +) + +get_nextzen_api_key(NEXTZEN_API_KEY) +target_compile_definitions(tangram PRIVATE NEXTZEN_API_KEY="${NEXTZEN_API_KEY}") diff --git a/platforms/windows/src/main.cpp b/platforms/windows/src/main.cpp new file mode 100644 index 0000000000..72de074426 --- /dev/null +++ b/platforms/windows/src/main.cpp @@ -0,0 +1,35 @@ +#include "glfwApp.h" +#include "windowsPlatform.h" +#include "log.h" +#include "map.h" +#include +#include // _getcwd + +using namespace Tangram; + +int main(int argc, char* argv[]) { + + // Create the windowed app. + GlfwApp::create(std::make_unique(), 1024, 768); + GlfwApp::parseArgs(argc, argv); + + // Resolve the input path against the current directory. + Url baseUrl("file:///"); + char pathBuffer[PATH_MAX] = {0}; + if (_getcwd(pathBuffer, PATH_MAX) != nullptr) { + baseUrl = Url("file://" + std::string(pathBuffer)).resolved(baseUrl); + } + + LOG("Base URL: %s", baseUrl.string().c_str()); + + Url sceneUrl = Url(GlfwApp::sceneFile).resolved(baseUrl); + GlfwApp::sceneFile = sceneUrl.string(); + + // Loop until the user closes the window + GlfwApp::run(); + + // Clean up. + GlfwApp::destroy(); + + return 0; +} diff --git a/platforms/windows/src/windowsPlatform.cpp b/platforms/windows/src/windowsPlatform.cpp new file mode 100644 index 0000000000..9f2759171b --- /dev/null +++ b/platforms/windows/src/windowsPlatform.cpp @@ -0,0 +1,79 @@ +#include "windowsPlatform.h" +#include "gl/hardware.h" +#include "log.h" +#include +#include +#include +#include + +#include + +#define DEFAULT "res/fonts/NotoSans-Regular.ttf" +#define FONT_AR "res/fonts/NotoNaskh-Regular.ttf" +#define FONT_HE "res/fonts/NotoSansHebrew-Regular.ttf" +#define FONT_JA "res/fonts/DroidSansJapanese.ttf" +#define FALLBACK "res/fonts/DroidSansFallback.ttf" + +namespace Tangram { + +void logMsg(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +WindowsPlatform::WindowsPlatform() + : WindowsPlatform(UrlClient::Options{}) {} + +WindowsPlatform::WindowsPlatform(UrlClient::Options urlClientOptions) : + m_urlClient(std::make_unique(urlClientOptions)) { +} + +WindowsPlatform::~WindowsPlatform() {} + +void WindowsPlatform::shutdown() { + // Stop all UrlWorker threads + m_urlClient.reset(); + + Platform::shutdown(); +} + +void WindowsPlatform::requestRender() const { + if (m_shutdown) { return; } + glfwPostEmptyEvent(); +} + +std::vector WindowsPlatform::systemFontFallbacksHandle() const { + std::vector handles; + + handles.emplace_back(Url(DEFAULT)); + handles.emplace_back(Url(FONT_AR)); + handles.emplace_back(Url(FONT_HE)); + handles.emplace_back(Url(FONT_JA)); + handles.emplace_back(Url(FALLBACK)); + + return handles; +} + +bool WindowsPlatform::startUrlRequestImpl(const Url& _url, const UrlRequestHandle _request, UrlRequestId& _id) { + auto onURLResponse = [this, _request](UrlResponse&& response) { + onUrlResponse(_request, std::move(response)); + }; + _id = m_urlClient->addRequest(_url.string(), onURLResponse); + return false; +} + +void WindowsPlatform::cancelUrlRequestImpl(const UrlRequestId _id) { + if (m_urlClient) { + m_urlClient->cancelRequest(_id); + } +} + +void setCurrentThreadPriority(int priority) {} + +void initGLExtensions() { + Tangram::Hardware::supportsMapBuffer = true; +} + +} // namespace Tangram diff --git a/platforms/windows/src/windowsPlatform.h b/platforms/windows/src/windowsPlatform.h new file mode 100644 index 0000000000..ad7cd91066 --- /dev/null +++ b/platforms/windows/src/windowsPlatform.h @@ -0,0 +1,25 @@ +#pragma once + +#include "platform.h" +#include "urlClient.h" + +namespace Tangram { + +class WindowsPlatform : public Platform { + +public: + + WindowsPlatform(); + explicit WindowsPlatform(UrlClient::Options urlClientOptions); + ~WindowsPlatform() override; + void shutdown() override; + void requestRender() const override; + std::vector systemFontFallbacksHandle() const override; + bool startUrlRequestImpl(const Url& _url, const UrlRequestHandle _request, UrlRequestId& _id) override; + void cancelUrlRequestImpl(const UrlRequestId _id) override; + +protected: + std::unique_ptr m_urlClient; +}; + +} // namespace Tangram