From 50374993510fc7c020a8d73add11bfa60a90da01 Mon Sep 17 00:00:00 2001 From: Mathis Logemann Date: Mon, 22 Mar 2021 22:36:16 +0200 Subject: [PATCH 1/3] Windows port --- .gitignore | 19 ++++ CMakeLists.txt | 4 +- README.md | 1 + cmake/utils.cmake | 7 +- core/CMakeLists.txt | 11 ++- core/deps/CMakeLists.txt | 33 ++++--- core/deps/pbf/pbf.hpp | 6 +- core/include/tangram/log.h | 4 + core/include/tangram/tile/tileID.h | 1 + core/include/tangram/util/variant.h | 10 +- core/src/data/rasterSource.cpp | 2 +- core/src/debug/textDisplay.cpp | 2 +- core/src/scene/importer.cpp | 4 + core/src/scene/sceneLoader.cpp | 30 +++--- core/src/text/textUtil.cpp | 2 +- core/src/util/floatFormatter.h | 4 +- core/src/util/geom.h | 6 +- platforms/common/glfwApp.cpp | 36 ++++--- platforms/common/platform_gl.h | 9 ++ platforms/common/urlClient.cpp | 112 +++++++++++++++++++--- platforms/common/urlClient.h | 23 ++++- platforms/windows/README.md | 33 +++++++ platforms/windows/config.cmake | 48 ++++++++++ platforms/windows/src/main.cpp | 39 ++++++++ platforms/windows/src/windowsPlatform.cpp | 77 +++++++++++++++ platforms/windows/src/windowsPlatform.h | 25 +++++ tests/src/mockPlatform.cpp | 8 ++ vcpkg.json | 31 ++++++ 28 files changed, 521 insertions(+), 66 deletions(-) create mode 100644 platforms/windows/README.md create mode 100644 platforms/windows/config.cmake create mode 100644 platforms/windows/src/main.cpp create mode 100644 platforms/windows/src/windowsPlatform.cpp create mode 100644 platforms/windows/src/windowsPlatform.h create mode 100644 vcpkg.json diff --git a/.gitignore b/.gitignore index 2ee38036f8..ff6873d050 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,24 @@ # Created by http://www.gitignore.io +### Windows ### +.vscode/ +.vs/ +ipch/ +build/ +out/ + +CMakeSettings.json +*.suo +*.user +*.userosscache +*.sln.docstates +*.ncb +*.opendb +*.opensdf +*.sdf +*.VC.db +*.vspx + ### OSX ### .DS_Store .AppleDouble diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b07485080..09e1bb3c99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) project(tangram) @@ -19,7 +19,7 @@ option(TANGRAM_DEV_MODE "For developers only: Don't omit the frame pointer" OFF) message(STATUS "Build type configuration: ${CMAKE_BUILD_TYPE}") # Check that submodules are present. -if(NOT EXISTS "${PROJECT_SOURCE_DIR}/core/deps/harfbuzz-icu-freetype/harfbuzz/README") +if(NOT MSVC AND NOT EXISTS "${PROJECT_SOURCE_DIR}/core/deps/harfbuzz-icu-freetype/harfbuzz/README") message(SEND_ERROR "Missing submodules - Please run:\n 'git submodule update --init'") return() endif() diff --git a/README.md b/README.md index 1788bd2567..049858b508 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ For more information about building Tangram ES or using it in your project, see - [Mac OS X](platforms/osx) - [Ubuntu Linux](platforms/linux) - [Raspberry Pi](platforms/rpi) +- [Windows](platforms/windows) ## Support diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 74219ec35f..401ba490b6 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -3,6 +3,7 @@ function(check_unsupported_compiler_version) set(MIN_GCC 4.9) set(MIN_CLANG 3.4) set(MIN_APPLECLANG 6.0) + set(MIN_MSVC 19.0) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MIN_GCC}) @@ -16,8 +17,12 @@ function(check_unsupported_compiler_version) 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() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MIN_MSVC}) + message(FATAL_ERROR "Your MSVC version does not support C++14, please install version ${MIN_MSVC} or higher") + endif() else() - message(WARNING "Compilation has only been tested with Clang, AppleClang, and GCC") + message(WARNING "Compilation has only been tested with Clang, AppleClang, GCC and MSVC") endif() endfunction(check_unsupported_compiler_version) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 4075f8ca8f..7d978d7f72 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,6 +1,11 @@ project(tangram-core) # Build core library dependencies. +find_package(ZLIB REQUIRED) +if (MSVC) + find_package(yaml-cpp CONFIG REQUIRED) +endif() + add_subdirectory(deps) add_library(tangram-core @@ -222,6 +227,10 @@ target_include_directories(tangram-core deps/double-conversion/include ) +if (WIN32) + target_compile_definitions(tangram-core PUBLIC _USE_MATH_DEFINES) +endif() + # Link core library dependencies. target_link_libraries(tangram-core PRIVATE @@ -231,7 +240,7 @@ target_link_libraries(tangram-core alfons double-conversion miniz - z + ZLIB::ZLIB ) # Add JavaScript implementation. diff --git a/core/deps/CMakeLists.txt b/core/deps/CMakeLists.txt index 4de4f69315..8e9320f26d 100644 --- a/core/deps/CMakeLists.txt +++ b/core/deps/CMakeLists.txt @@ -13,13 +13,15 @@ target_compile_definitions(glm INTERFACE GLM_FORCE_CTOR_INIT) ## yaml-cpp ## ############## -set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "") -set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "") -set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "") -set(YAML_CPP_INSTALL OFF CACHE BOOL "") -add_subdirectory(yaml-cpp) - -target_include_directories(yaml-cpp PUBLIC yaml-cpp/include) +if (NOT MSVC) + set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "") + set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "") + set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "") + set(YAML_CPP_INSTALL OFF CACHE BOOL "") + add_subdirectory(yaml-cpp) + + target_include_directories(yaml-cpp PUBLIC yaml-cpp/include) +endif() ## css-color-parser-cpp ## ########################## @@ -48,14 +50,21 @@ target_include_directories(miniz if(NOT TANGRAM_USE_SYSTEM_FONT_LIBS) ## Harfbuzz - ICU-Common - Freetype2 ## ####################################### - set(HARFBUZZ_BUILD_ICU ON CACHE BOOL "Enable building of ICU") - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/harfbuzz-icu-freetype) - - message(STATUS "harfbuzz" ${HARFBUZZ_LIBRARIES}) + if(MSVC) + #find_package(ICU REQUIRED COMPONENTS common) + find_package(harfbuzz CONFIG REQUIRED) + set(HARFBUZZ_LIB_NAME harfbuzz::harfbuzz) + else() + set(HARFBUZZ_BUILD_ICU ON CACHE BOOL "Enable building of ICU") + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/harfbuzz-icu-freetype) + + message(STATUS "harfbuzz" ${HARFBUZZ_LIBRARIES}) + set(HARFBUZZ_LIB_NAME harfbuzz ${HARFBUZZ_LIBRARIES}) + endif() set(ALFONS_DEPS_LIBRARIES ${ALFONS_DEPS_LIBRARIES} - harfbuzz ${HARFBUZZ_LIBRARIES} + ${HARFBUZZ_LIB_NAME} CACHE INTERNAL "alfons-libs" FORCE) endif() diff --git a/core/deps/pbf/pbf.hpp b/core/deps/pbf/pbf.hpp index f73b4f4056..62d4bc8871 100644 --- a/core/deps/pbf/pbf.hpp +++ b/core/deps/pbf/pbf.hpp @@ -26,9 +26,13 @@ #endif namespace protobuf { - +#if defined(__GNUC__) #define FORCEINLINE inline __attribute__((always_inline)) #define NOINLINE __attribute__((noinline)) +#else +#define FORCEINLINE inline +#define NOINLINE +#endif #define PBF_INLINE FORCEINLINE class message { diff --git a/core/include/tangram/log.h b/core/include/tangram/log.h index eb594e791a..c5d6041278 100644 --- a/core/include/tangram/log.h +++ b/core/include/tangram/log.h @@ -27,7 +27,11 @@ static constexpr const char * past_last_slash(const char * const str) { return past_last_slash(str, str); } +#ifdef TANGRAM_WINDOWS +#define __FILENAME__ __FILE__ +#else #define __FILENAME__ ({constexpr const char * const sf__ {past_last_slash(__FILE__)}; sf__;}) +#endif #define TANGRAM_MAX_BUFFER_LOG_SIZE 99999 diff --git a/core/include/tangram/tile/tileID.h b/core/include/tangram/tile/tileID.h index 57107daa0c..327a458cfa 100644 --- a/core/include/tangram/tile/tileID.h +++ b/core/include/tangram/tile/tileID.h @@ -3,6 +3,7 @@ #include #include #include +#include /* An immutable identifier for a map tile * diff --git a/core/include/tangram/util/variant.h b/core/include/tangram/util/variant.h index 87998a45c3..f0512a91b1 100644 --- a/core/include/tangram/util/variant.h +++ b/core/include/tangram/util/variant.h @@ -55,7 +55,15 @@ using Value = variant; class Value : public detail::Value { using Base = detail::Value; - using Base::Base; + +public: + Value(): Base() {} + + template + Value(const T& val): Base(val) {} + + template + Value(T&& val): Base(val) {} }; const static Value NOT_A_VALUE(none_type{}); diff --git a/core/src/data/rasterSource.cpp b/core/src/data/rasterSource.cpp index 689e9e31f5..32ef27a2eb 100644 --- a/core/src/data/rasterSource.cpp +++ b/core/src/data/rasterSource.cpp @@ -208,7 +208,7 @@ std::shared_ptr RasterSource::cacheTexture(const TileID& _tileId, std:: } texture = std::shared_ptr(_texture.release(), - [c = std::weak_ptr(m_textures), id](auto t) { + [c = std::weak_ptr(m_textures), id](auto* t) { if (auto cache = c.lock()) { cache->erase(id); LOGD("%d - remove %s", cache->size(), id.toString().c_str()); diff --git a/core/src/debug/textDisplay.cpp b/core/src/debug/textDisplay.cpp index 5b3166ee30..fff2af7105 100644 --- a/core/src/debug/textDisplay.cpp +++ b/core/src/debug/textDisplay.cpp @@ -89,7 +89,7 @@ void TextDisplay::draw(RenderState& rs, const std::string& _text, int _posx, int std::vector vertices; int nquads; - nquads = stb_easy_font_print(_posx, _posy, _text.c_str(), NULL, m_vertexBuffer, VERTEX_BUFFER_SIZE); + nquads = stb_easy_font_print(_posx, _posy, const_cast(_text.c_str()), NULL, m_vertexBuffer, VERTEX_BUFFER_SIZE); float* data = reinterpret_cast(m_vertexBuffer); diff --git a/core/src/scene/importer.cpp b/core/src/scene/importer.cpp index 54d0dd9b82..da9be5fbe0 100644 --- a/core/src/scene/importer.cpp +++ b/core/src/scene/importer.cpp @@ -188,7 +188,11 @@ void Importer::addSceneYaml(const Url& sceneUrl, const char* sceneYaml, size_t l auto& sceneNode = m_sceneNodes[sceneUrl]; try { +#ifdef _MSC_VER + sceneNode.yaml = YAML::Load(sceneYaml); +#else sceneNode.yaml = YAML::Load(sceneYaml, length); +#endif } catch (const YAML::ParserException& e) { LOGE("Parsing scene config '%s'", e.what()); return; diff --git a/core/src/scene/sceneLoader.cpp b/core/src/scene/sceneLoader.cpp index 2866edd93d..0f271971f9 100644 --- a/core/src/scene/sceneLoader.cpp +++ b/core/src/scene/sceneLoader.cpp @@ -145,7 +145,7 @@ void SceneLoader::applyScene(const Node& _node, Color& backgroundColor, Stops& backgroundStops, Scene::animate& animated) { if (!_node) { return; } if (!_node.IsMap()) { - LOGNode("Invalid 'scene' section", _node); + LOGNode("Invalid 'scene' section", _node, ""); return; } @@ -302,7 +302,7 @@ Scene::Lights SceneLoader::applyLights(const Node& _node) { } } } else if (_node) { - LOGNode("Invalid 'lights'", _node); + LOGNode("Invalid 'lights'", _node, ""); } if (lights.empty()) { @@ -436,14 +436,14 @@ void SceneLoader::parseLightPosition(const Node& _positionNode, PointLight& _lig } _light.setPosition(positionResult); } else { - LOGNode("Invalid light position parameter:", _positionNode); + LOGNode("Invalid light position parameter:", _positionNode, ""); } } void SceneLoader::applyTextures(const Node& _node, SceneTextures& _textures) { if (!_node) { return; } if (!_node.IsMap()) { - LOGNode("Invalid 'textures' section", _node); + LOGNode("Invalid 'textures' section", _node, ""); return; } @@ -543,7 +543,7 @@ bool SceneLoader::parseTexFiltering(const Node& _filteringNode, TextureOptions& void SceneLoader::applyFonts(const Node& _node, SceneFonts& _fonts) { if (!_node) { return; } if (!_node.IsMap()) { - LOGNode("Invalid 'fonts' section", _node); + LOGNode("Invalid 'fonts' section", _node, ""); return; } @@ -570,7 +570,7 @@ void SceneLoader::applyFonts(const Node& _node, SceneFonts& _fonts) { void SceneLoader::loadFontDescription(const Node& _node, const std::string& _family, SceneFonts& _fonts) { if (!_node) { return; } if (!_node.IsMap()) { - LOGNode("Invalid 'font' section", _node); + LOGNode("Invalid 'font' section", _node, ""); return; } @@ -634,18 +634,18 @@ Scene::TileSources SceneLoader::applySources(const Node& _config, const SceneOpt if (const Node& rasters = source.second["rasters"]) { if (!rasters.IsSequence()) { - LOGNode("Invalid 'rasters'", rasters); + LOGNode("Invalid 'rasters'", rasters, ""); continue; } for (const auto& raster : rasters) { if (!raster.IsScalar()) { - LOGNode("Invalid 'raster'", raster); + LOGNode("Invalid 'raster'", raster, ""); continue; } if (auto rasterSource = getTileSource(raster.Scalar())) { tileSource->addRasterSource(rasterSource); } else { - LOGNode("Missing raster source", raster); + LOGNode("Missing raster source", raster, ""); } } } @@ -676,7 +676,7 @@ Scene::TileSources SceneLoader::applySources(const Node& _config, const SceneOpt if (!data_source) { continue;} if (!data_source.IsScalar()) { - LOGNode("Invalid 'source", data); + LOGNode("Invalid 'source", data, ""); continue; } auto source = data_source.Scalar(); @@ -875,7 +875,7 @@ Scene::Styles SceneLoader::applyStyles(const Node& _node, SceneTextures& _textur if (!_node) { return styles; } if (!_node.IsMap()) { - LOGNode("Invalid 'styles' section", _node); + LOGNode("Invalid 'styles' section", _node, ""); return styles; } @@ -1256,7 +1256,7 @@ void SceneLoader::loadShaderConfig(const Node& _shaders, Style& _style, SceneTex _style.styleUniforms().emplace_back(name, styleUniform.value); } else { - LOGNode("Style uniform parsing failure", uniform.second); + LOGNode("Style uniform parsing failure", uniform.second, ""); } } } @@ -1387,7 +1387,7 @@ void SceneLoader::loadMaterial(const Node& _matNode, Material& _material, Style& // Handled as texture break; default: - LOGNode("Invalid 'material'", prop); + LOGNode("Invalid 'material'", prop, ""); break; } return glm::vec4(0.0); @@ -1441,7 +1441,7 @@ MaterialTexture SceneLoader::loadMaterialTexture(const Node& _matCompNode, Style Node textureNode = _matCompNode["texture"]; if (!textureNode) { - LOGNode("Expected a 'texture' parameter", _matCompNode); + LOGNode("Expected a 'texture' parameter", _matCompNode, ""); return MaterialTexture{}; } @@ -1501,7 +1501,7 @@ std::vector SceneLoader::applyLayers(const Node& _node, SceneFunctio SceneStops& _stops, DrawRuleNames& _ruleNames) { if (!_node) { return {}; } if (!_node.IsMap()) { - LOGNode("Invalid 'layers' section", _node); + LOGNode("Invalid 'layers' section", _node, ""); return {}; } diff --git a/core/src/text/textUtil.cpp b/core/src/text/textUtil.cpp index f1a2b76b57..8f89f18e8a 100644 --- a/core/src/text/textUtil.cpp +++ b/core/src/text/textUtil.cpp @@ -67,7 +67,7 @@ int TextWrapper::draw(alfons::TextBatch& _batch, float _maxWidth, const alfons:: TextLabelProperty::Align _alignment, float _lineSpacing, alfons::LineMetrics& _layoutMetrics) { size_t shapeStart = 0; - glm::vec2 position; + glm::vec2 position{}; for (auto wrap : m_lineWraps) { alfons::LineMetrics lineMetrics; diff --git a/core/src/util/floatFormatter.h b/core/src/util/floatFormatter.h index b6d3f0189f..9194947835 100644 --- a/core/src/util/floatFormatter.h +++ b/core/src/util/floatFormatter.h @@ -20,14 +20,14 @@ struct ff { static double stod(const std::string& _string) { int end = 0; - return stod(_string.data(), _string.size(), &end); + return stod(_string.data(), static_cast(_string.size()), &end); } static float stof(const char* _string, int _length, int* _end); static float stof(const std::string& _string) { int end = 0; - return stof(_string.data(), _string.size(), &end); + return stof(_string.data(), static_cast(_string.size()), &end); } }; diff --git a/core/src/util/geom.h b/core/src/util/geom.h index 437f49d3d4..6bf4f6e354 100644 --- a/core/src/util/geom.h +++ b/core/src/util/geom.h @@ -72,7 +72,7 @@ float signedArea(InputIt begin, InputIt end) { float area = 0; auto prev = end - 1; for (auto curr = begin; curr != end; ++curr) { - area += curr->x * prev->y - curr->y * prev->x; + area += static_cast(curr->x * prev->y - curr->y * prev->x); prev = curr; } return 0.5f * area; @@ -82,14 +82,14 @@ float signedArea(InputIt begin, InputIt end) { /// If the polygon has no area, the coordinates returned are NaN. template Vector centroid(InputIt begin, InputIt end) { - Vector centroid; + Vector centroid{}; float area = 0.f; const Vector offset(begin->x, begin->y); for (auto curr = begin, prev = end - 1; curr != end; prev = curr, ++curr) { const Vector prevPoint(prev->x - offset.x, prev->y - offset.y); const Vector currPoint(curr->x - offset.x, curr->y - offset.y); - float a = (prevPoint.x * currPoint.y - currPoint.x * prevPoint.y); + float a = static_cast(prevPoint.x * currPoint.y - currPoint.x * prevPoint.y); centroid.x += (prevPoint.x + currPoint.x) * a; centroid.y += (prevPoint.y + currPoint.y) * a; area += a; diff --git a/platforms/common/glfwApp.cpp b/platforms/common/glfwApp.cpp index 0c0c11eb5d..8de127ab97 100644 --- a/platforms/common/glfwApp.cpp +++ b/platforms/common/glfwApp.cpp @@ -3,6 +3,12 @@ #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 #define GL_SILENCE_DEPRECATION #include @@ -203,6 +209,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); @@ -535,17 +545,19 @@ void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods map->loadSceneYamlAsync("{ scene: { background: { color: red } } }", std::string("")); break; case GLFW_KEY_G: - static bool geoJSON = false; - if (!geoJSON) { - loadSceneFile(false, - { SceneUpdate{"sources.osm.type", "GeoJSON"}, - SceneUpdate{"sources.osm.url", "https://tile.mapzen.com/mapzen/vector/v1/all/{z}/{x}/{y}.json"}}); - } else { - loadSceneFile(false, - { SceneUpdate{"sources.osm.type", "MVT"}, - SceneUpdate{"sources.osm.url", "https://tile.mapzen.com/mapzen/vector/v1/all/{z}/{x}/{y}.mvt"}}); + { + static bool geoJSON = false; + if (!geoJSON) { + loadSceneFile(false, + { SceneUpdate{"sources.osm.type", "GeoJSON"}, + SceneUpdate{"sources.osm.url", "https://tile.mapzen.com/mapzen/vector/v1/all/{z}/{x}/{y}.json"}}); + } else { + loadSceneFile(false, + { SceneUpdate{"sources.osm.type", "MVT"}, + SceneUpdate{"sources.osm.url", "https://tile.mapzen.com/mapzen/vector/v1/all/{z}/{x}/{y}.mvt"}}); + } + geoJSON = !geoJSON; } - geoJSON = !geoJSON; break; case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, true); @@ -603,7 +615,7 @@ void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods case GLFW_KEY_W: map->onMemoryWarning(); break; - default: + default: break; } } @@ -661,7 +673,7 @@ void showMarkerGUI() { } point_markers.clear(); } - + ImGui::Checkbox("Add polyline marker points on click", &add_polyline_marker_on_click); if (ImGui::Button("Clear polyline marker")) { if (!polyline_marker_coordinates.empty()) { diff --git a/platforms/common/platform_gl.h b/platforms/common/platform_gl.h index 107396e3be..4fbed058b8 100644 --- a/platforms/common/platform_gl.h +++ b/platforms/common/platform_gl.h @@ -45,6 +45,15 @@ extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT; #include #endif // TANGRAM_LINUX +#ifdef TANGRAM_WINDOWS +#define GL_GLEXT_PROTOTYPES +#include +#include +// Resolve aliased names on Windows +#define glClearDepthf glClearDepth +#define glDepthRangef glDepthRange +#endif // TANGRAM_WINDOWS + #ifdef TANGRAM_RPI // Broadcom library for direct GPU access #include "bcm_host.h" diff --git a/platforms/common/urlClient.cpp b/platforms/common/urlClient.cpp index 58c5acf84d..2616b8b886 100644 --- a/platforms/common/urlClient.cpp +++ b/platforms/common/urlClient.cpp @@ -4,7 +4,9 @@ #include #include #include +#ifndef _MSC_VER #include +#endif #include constexpr char const* requestCancelledError = "Request cancelled"; @@ -22,6 +24,92 @@ struct CurlGlobals { } } s_curl; +UrlClient::SelfPipe::~SelfPipe() { +#if defined(_WIN32) + if(pipeFds[0] != SocketInvalid) { + closesocket(pipeFds[0]); + } + if(pipeFds[1] != SocketInvalid) { + closesocket(pipeFds[1]); + } +#endif +} + +bool UrlClient::SelfPipe::initialize() { +#if defined(_WIN32) + // https://stackoverflow.com/questions/3333361/how-to-cancel-waiting-in-select-on-windows + struct sockaddr_in inaddr; + int len = sizeof(inaddr); + memset(&inaddr, 0, len); + struct sockaddr addr; + memset(&addr, 0, sizeof(addr)); + + inaddr.sin_family = AF_INET; + inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + inaddr.sin_port = 0; + int yes = 1; + SOCKET lst = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(lst == INVALID_SOCKET) { + return false; + } + if( + setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)) == SOCKET_ERROR || + bind(lst, (struct sockaddr *)&inaddr, sizeof(inaddr)) == SOCKET_ERROR || + listen(lst, 1) == SOCKET_ERROR || + getsockname(lst, &addr, &len) || + (pipeFds[0] = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET + ) { + closesocket(lst); + return false; + } + + if( + (connect(pipeFds[0], &addr, len)) == SOCKET_ERROR || + (pipeFds[1] = accept(lst, 0, 0)) == INVALID_SOCKET + ) { + closesocket(pipeFds[0]); + closesocket(lst); + pipeFds[0] = -1; + return false; + } + + closesocket(lst); + return true; +#else + return pipe(pipeFds) >= 0; +#endif +} + +bool UrlClient::SelfPipe::write() { +#if defined(_WIN32) + return send(pipeFds[1], "\0", 1, 0) >= 0; +#else + return ::write(pipeFds[1], "\0", 1) >= 0; +#endif +} + +bool UrlClient::SelfPipe::read(int *error) { + char buffer[1]; +#if defined(_WIN32) + int n = recv(pipeFds[0], buffer, sizeof(buffer), 0); + if(n == SOCKET_ERROR) { + *error = WSAGetLastError(); + return false; + } +#else + int n = ::read(pipeFds[0], buffer, sizeof(buffer)); + if (n <= 0) { + *error = n; + return false; + } +#endif + return true; +} + +UrlClient::SelfPipe::Socket UrlClient::SelfPipe::getReadFd() { + return pipeFds[0]; +} + struct UrlClient::Task { // Reduce Task content capacity when it's more than 128kb and last // content size was less then half limit_capacity. @@ -96,6 +184,11 @@ struct UrlClient::Task { UrlClient::UrlClient(Options options) : m_options(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 (!m_requestNotify.initialize()) { + LOGE("Could not initialize select breaker!"); + } // Start the curl thread m_curlHandle = curl_multi_init(); @@ -105,12 +198,6 @@ UrlClient::UrlClient(Options options) : m_options(options) { // 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() { @@ -155,7 +242,7 @@ UrlClient::~UrlClient() { void UrlClient::curlWakeUp() { if (!m_curlNotified) { - if (write(m_requestNotify[1], "\0", 1) <= 0) { + if (!m_requestNotify.write()) { // err return; } @@ -293,7 +380,7 @@ void UrlClient::curlLoop() { } // Listen on requestNotify to break select when new requests are added. - FD_SET(m_requestNotify[0], &fdread); + FD_SET(m_requestNotify.getReadFd(), &fdread); // Wait for transfers // On success the value of maxfd is guaranteed to be >= -1. We call @@ -307,11 +394,12 @@ void UrlClient::curlLoop() { continue; } else { - if (FD_ISSET(m_requestNotify[0], &fdread)) { + if (FD_ISSET(m_requestNotify.getReadFd(), &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); } + int error; + if(!m_requestNotify.read(&error)) { + LOGE("Read request notify %d", error); + } //LOG("Got request notifies %d %d", n, m_curlNotified); m_curlNotified = false; } diff --git a/platforms/common/urlClient.h b/platforms/common/urlClient.h index bff5695030..faad84ecf5 100644 --- a/platforms/common/urlClient.h +++ b/platforms/common/urlClient.h @@ -13,6 +13,9 @@ #include #include +#if defined(_WIN32) +#include +#endif namespace Tangram { @@ -44,6 +47,24 @@ class UrlClient { RequestId id; }; + class SelfPipe { + public: +#if defined(_WIN32) + using Socket = SOCKET; + const Socket SocketInvalid = INVALID_SOCKET; +#else + using Socket = int; + const Socket SocketInvalid = -1; +#endif + ~SelfPipe(); + bool initialize(); + bool write(); + bool read(int *error); + Socket getReadFd(); + private: + Socket pipeFds[2] = { SocketInvalid, SocketInvalid }; + }; + struct Task; void curlLoop(); @@ -74,7 +95,7 @@ class UrlClient { std::atomic m_requestCount{0}; // File descriptors to break waiting select. - int m_requestNotify[2] = { -1, -1 }; + SelfPipe m_requestNotify; }; } // namespace Tangram diff --git a/platforms/windows/README.md b/platforms/windows/README.md new file mode 100644 index 0000000000..55737127db --- /dev/null +++ b/platforms/windows/README.md @@ -0,0 +1,33 @@ +Windows +====================== + +## Setup ## + +This project uses CMake (minimum version 3.10), you can download it [here](http://www.cmake.org/download/) + +Make sure to update git submodules before you build: + +```bash +git submodule update --init +``` + +To build on Windows you will need a Visual Studio 2019 and vcpkg(https://github.com/microsoft/vcpkg) installed. + +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 (`NEXTZEN_API_KEY`) to point to your API key. + +## Build ## + +There are two ways to build the tangram-es library and demo application on Windows: + + 1. Open reporitory folder with Visual Studio in cmake settings specify path to vcpkg toolchain /scripts/buildsystems/vcpkg.cmake + + 2. Generate solution via command line CMake + ```bash + mkdir build and cd build + cmake .. -DCMAKE_TOOLCHAIN_FILE="D:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake" + ``` + diff --git a/platforms/windows/config.cmake b/platforms/windows/config.cmake new file mode 100644 index 0000000000..f1446b4103 --- /dev/null +++ b/platforms/windows/config.cmake @@ -0,0 +1,48 @@ +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments") +endif() + +check_unsupported_compiler_version() + +add_definitions(-DTANGRAM_WINDOWS) + +find_package(OpenGL REQUIRED) +find_package(CURL REQUIRED) + +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} + wsock32 ws2_32 crypt32 wldap32 +) + +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..640b867a63 --- /dev/null +++ b/platforms/windows/src/main.cpp @@ -0,0 +1,39 @@ +#include "glfwApp.h" +#include "windowsPlatform.h" +#include "log.h" +#include "map.h" +#include +#include // _getcwd + +#define PATH_MAX 512 + +using namespace Tangram; + +int main(int argc, char* argv[]) { + + // Create the windowed app. + GlfwApp::create(std::make_unique(), 1024, 768); + + GlfwApp::sceneFile = "res/scene.yaml"; + 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 = baseUrl.resolve(Url(std::string(pathBuffer) + "/")); + } + + LOG("Base URL: %s", baseUrl.string().c_str()); + + Url sceneUrl = baseUrl.resolve(Url(GlfwApp::sceneFile)); + 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..0b36fc932f --- /dev/null +++ b/platforms/windows/src/windowsPlatform.cpp @@ -0,0 +1,77 @@ +#include "windowsPlatform.h" +#include "gl/hardware.h" +#include "log.h" +#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 diff --git a/tests/src/mockPlatform.cpp b/tests/src/mockPlatform.cpp index 23a39370fa..b6336f3fe6 100644 --- a/tests/src/mockPlatform.cpp +++ b/tests/src/mockPlatform.cpp @@ -6,14 +6,22 @@ #include #include +#ifndef TANGRAM_WINDOWS #include #include +#else +#include // _getcwd +#endif #include #define DEFAULT_FONT "res/fonts/NotoSans-Regular.ttf" #include "log.h" +#ifdef TANGRAM_WINDOWS +#define PATH_MAX 512 +#endif + namespace Tangram { void logMsg(const char* fmt, ...) { diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000000..ab906acc3a --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,31 @@ +{ + "name": "tangram-es", + "version-string": "0.1.0", + "dependencies": [ + { + "name":"curl", + "features":[ + "ssl", + "http2" + ] + }, + "glad", + "zlib", + "yaml-cpp", + "glfw3", + "benchmark", + { + "name": "harfbuzz", + "features": [ + "icu", + "graphite2" + ] + }, + { + "name": "imgui", + "features": [ + "docking-experimental" + ] + } + ] +} From 2f657f9e4535f91d9184fa77852f17923c607687 Mon Sep 17 00:00:00 2001 From: Mykhailo Parfeniuk Date: Tue, 20 Jul 2021 19:31:56 +0300 Subject: [PATCH 2/3] Fix Windows base path --- platforms/windows/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platforms/windows/src/main.cpp b/platforms/windows/src/main.cpp index 640b867a63..4bbb9c3e5a 100644 --- a/platforms/windows/src/main.cpp +++ b/platforms/windows/src/main.cpp @@ -18,10 +18,10 @@ int main(int argc, char* argv[]) { GlfwApp::parseArgs(argc, argv); // Resolve the input path against the current directory. - Url baseUrl("file:///"); + Url baseUrl("file://"); char pathBuffer[PATH_MAX] = {0}; if (getcwd(pathBuffer, PATH_MAX) != nullptr) { - baseUrl = baseUrl.resolve(Url(std::string(pathBuffer) + "/")); + baseUrl = baseUrl.resolve(Url("/" + std::string(pathBuffer) + "/")); } LOG("Base URL: %s", baseUrl.string().c_str()); From 593c309b1d975680ac3ab0b9404994b5514f2a42 Mon Sep 17 00:00:00 2001 From: Mykhailo Parfeniuk Date: Sun, 1 Aug 2021 14:10:48 +0300 Subject: [PATCH 3/3] submodule update --- core/deps/geojson-vt-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/deps/geojson-vt-cpp b/core/deps/geojson-vt-cpp index 12aae692c4..52c96b6646 160000 --- a/core/deps/geojson-vt-cpp +++ b/core/deps/geojson-vt-cpp @@ -1 +1 @@ -Subproject commit 12aae692c43590e4f4f2113ab22d4174fd6d9a00 +Subproject commit 52c96b66467e9cf154509860a1b5ff936022f957