open source

Cross-platform geospatial visualization with deck.gl-native

By Ib Green September 02, 2020

Note: This is a cross-post from the Google Earth and Earth Engine Blog, written by Unfolded’s Ib Green and Ilija Puaca.

Google and Unfolded have partnered to develop an open source C++ version of deck.gl. An initial version supporting a limited subset of deck.gl layers and features is now freely available on GitHub.

deck.gl PolygonLayer showing Vancouver property prices, running natively on an iOS device

Deck.gl is a widely used large-scale geospatial data visualization framework that has its roots in JavaScript and WebGL. The choice of JavaScript has served the deck.gl community well: Visualization is front-end computing and thus the browser is often the natural vehicle to reach users.

But there are cases where native implementation can provide better performance, user experience, and architectural fit, primarily in native mobile applications but also for certain high-performance computing use cases. It’s always been clear that a C++ version would enable new opportunities.

A few months ago, a collaboration with engineers working on Google Earth allowed us to fine-tune the architectural vision for deck.gl-native. The goal was to prototype new deck.gl-powered visualization experiences in Google Earth across web (using WebAssembly), iOS, and Android platforms. A native solution was required to enable a high-performance integration with Google Earth’s rendering engine on all platforms. With our designs solidified, and a concrete use case in hand, we were ready to start building deck.gl in C++.

A new API that’s both familiar and efficient

The new deck.gl C++ API has been designed to correspond closely to the JavaScript API whenever that makes sense and performance is not sacrificed.

As can be seen in the sample code, familiar classes such as Deck, ScatterplotLayer, MapView and ViewState work just as a JavaScript deck.gl user would expect.

At the same time, layer accessor methods ( getPosition and getFillColor ) that enable data-driven styling are now based on C++ lambda functions for maximum flexibility and performance.

auto layerProps = std::make_shared<ScatterplotLayer::Props>();
layerProps>getPosition = [](const Row &row) {
    return row.getVector3<float>("position");
};
layerProps>getFillColor = [](const Row &row) {
    return mathgl::Vector4<float>{255, 0, 0, 255};
};
layerProps>data = /* your dataset (arrow::Table) goes here */;

auto deckProps = std::make_shared<Deck::Props>();
deckProps->layers = {layerProps};
deckProps->initialViewState = std::make_shared<ViewState>();
deckProps->views = {std::make_shared<MapView>()};

auto deck = std::make_shared<Deck>(deckProps);
deck->run();

deck.gl-native C++ code that renders a ScatterplotLayer with data-driven styling.

ScatterplotLayer of Manhattan population, running natively on an iOS device

Software architecture of deck.gl-native

Implementing a sophisticated WebGL framework such as deck.gl from scratch in a new programming language (without the need to support backwards compatibility and all the specialized features deck.gl has accumulated on the JavaScript side) gave us the ability to take a fresh look at a number of foundational architectural choices.

For more information on deck.gl architecture, the deck.gl Technical Deep-Dive is a good read. While written for the JavaScript version of deck.gl, many concepts apply to the C++ port as well.

Apache Arrow

All in-memory table processing is based on the Apache Arrow C++ API, which provided significant performance advantages because tables are stored in a GPU-friendly columnar binary data layout.

Note that while Apache Arrow has a fairly sophisticated API, this will be almost invisible to deck.gl-native applications as long they use the loaders provided by deck.gl. CSV and JSON table loaders are provided as part of the deck.gl library in the initial version, and .arrow tables can be loaded with the Arrow API.

Naturally, for advanced table processing use cases, applications are free to work directly with the Arrow API to build and process tables, and then pass resulting Arrow tables to deck.gl.

To get started with Arrow, useful resources might be:

Graphics backend: native WebGPU via Dawn

Deck.gl-native is being built on top of WebGPU API using the Dawn framework.

Deck.gl LineLayer showing flights out of London

The Dawn framework is a C++ implementation of the WebGPU API and is a compelling choice for deck.gl-native:

  • The JavaScript version of deck.gl will inevitably move from WebGL to WebGPU, so having both C++ and JavaScript work against a common 3D API will significantly increase the ease of aligning the JavaScript and C++ code bases.
  • The Dawn project has the ambition to provide backends on basically all platforms/rendering APIs of interest, including Vulkan, Metal, D3D12, and OpenGL. This ideally means that deck.gl-native itself will only have to implement a single backend, namely Dawn.

Note that Dawn is still a work in progress (with different levels of support for different platforms; the prototype is only being tested on iOS), and there is some risk with this technology choice. However, given the momentum behind WebGPU in browsers, we feel that the prospects are currently looking good.

Future directions

Beyond mobile: workstations and servers

While the JavaScript version of deck.gl runs well in a mobile web view inside a native mobile application, this mode prevents application from sharing the WebGL context with other rendering engines and take full control of rendering.

Thus, a “native” C++ implementation of deck.gl is not only required to provide optimal user experience on mobile devices and through integrations with apps such as Google Earth for mobile; it also provides exciting integration opportunities with a number of advanced, high-performance (usually C++ based) software packages that are not available or performant in browsers. These could be math libraries, computer vision libraries, etc.

Workstations and servers sometimes provide better GPU capabilities than browsers can expose through WebGL, and a C++ deck.gl implementation opens the option to use more advanced GPU acceleration when specialized hardware is available, such as nVidia’s CUDA technology. The ideal setup would be that vis.gl automatically takes maximum advantage of the best hardware that it is available at run-time (whether WebGL, WebGPU or CUDA).

deck.gl as a cross-programming-language framework

With the addition of a native C++ implementation, deck.gl is taking another big step away from being a “JavaScript first” framework, moving towards becoming a multi-language visualization architecture, complementing pydeck (Python deck.gl bindings) and the completely declarative deck.gl JSON API.

For more information, see the deck.gl cross-platform architecture that describes approaches to keeping multi-language implementations of deck.gl compatible. It’s still a work-in-progress, but gives a sense of the higher level direction.

Additional loaders

CSV and JSON table loaders are provided as part of the deck.gl library. To support additional table formats, additional “loaders” can be implemented that load various formats and “convert” the loaded tables to Arrow representation. Integrations with other C++ libraries, such as GDAL for geospatial and imaging formats, are also interesting.

Acknowledgements

Unfolded would like to thank Google for support and encouragement in getting the deck.gl-native development started.

Links:


Ib Green

Ib Green is the CTO of Unfolded Inc, a company dedicated to building products and services on top of the deck.gl technology stack. Ib created the first version of deck.gl back in 2015 while at working at Uber, and is still actively involved in development and governance of deck.gl.