summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2017-03-15 18:43:58 +0100
committerJohn Firebaugh <john.firebaugh@gmail.com>2017-04-03 11:01:43 -0700
commitf86333961eeacb9f2dd83a4c3680d30e06f947a7 (patch)
treee26d375a7852526196428bc86e9b7b94e2baf6f9
parentc1cd6759b4a87ef58442e864a192317284cf20ae (diff)
downloadqtlocation-mapboxgl-f86333961eeacb9f2dd83a4c3680d30e06f947a7.tar.gz
[core] cache binary shaders on Android
-rw-r--r--cmake/core-files.cmake5
-rw-r--r--cmake/test-files.cmake3
-rw-r--r--include/mbgl/map/map.hpp3
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java11
-rwxr-xr-xplatform/android/src/native_map_view.cpp26
-rwxr-xr-xplatform/android/src/native_map_view.hpp8
-rw-r--r--proto/binary_program.proto18
-rw-r--r--src/mbgl/gl/attribute.hpp13
-rw-r--r--src/mbgl/gl/context.cpp49
-rw-r--r--src/mbgl/gl/context.hpp10
-rw-r--r--src/mbgl/gl/features.hpp7
-rw-r--r--src/mbgl/gl/program.hpp30
-rw-r--r--src/mbgl/gl/program_binary.cpp24
-rw-r--r--src/mbgl/gl/program_binary.hpp27
-rw-r--r--src/mbgl/gl/types.hpp2
-rw-r--r--src/mbgl/gl/uniform.hpp13
-rw-r--r--src/mbgl/map/map.cpp16
-rw-r--r--src/mbgl/programs/binary_program.cpp117
-rw-r--r--src/mbgl/programs/binary_program.hpp43
-rw-r--r--src/mbgl/programs/program.hpp58
-rw-r--r--src/mbgl/programs/program_parameters.hpp15
-rw-r--r--src/mbgl/programs/programs.hpp4
-rw-r--r--src/mbgl/renderer/painter.cpp16
-rw-r--r--src/mbgl/renderer/painter.hpp2
-rw-r--r--src/mbgl/shaders/shaders.cpp14
-rw-r--r--src/mbgl/shaders/shaders.hpp2
-rw-r--r--src/mbgl/util/io.cpp10
-rw-r--r--src/mbgl/util/io.hpp3
-rw-r--r--test/programs/binary_program.test.cpp39
29 files changed, 541 insertions, 47 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index afc45a24cc..61bc79c334 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -67,6 +67,7 @@ set(MBGL_CORE_FILES
src/mbgl/gl/draw_mode.hpp
src/mbgl/gl/extension.cpp
src/mbgl/gl/extension.hpp
+ src/mbgl/gl/features.hpp
src/mbgl/gl/framebuffer.hpp
src/mbgl/gl/gl.cpp
src/mbgl/gl/index_buffer.hpp
@@ -75,6 +76,8 @@ set(MBGL_CORE_FILES
src/mbgl/gl/object.hpp
src/mbgl/gl/primitives.hpp
src/mbgl/gl/program.hpp
+ src/mbgl/gl/program_binary.cpp
+ src/mbgl/gl/program_binary.hpp
src/mbgl/gl/renderbuffer.hpp
src/mbgl/gl/segment.cpp
src/mbgl/gl/segment.hpp
@@ -134,6 +137,8 @@ set(MBGL_CORE_FILES
# programs
src/mbgl/programs/attributes.hpp
+ src/mbgl/programs/binary_program.cpp
+ src/mbgl/programs/binary_program.hpp
src/mbgl/programs/circle_program.cpp
src/mbgl/programs/circle_program.hpp
src/mbgl/programs/collision_box_program.cpp
diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake
index 873afa3d9f..951514e38a 100644
--- a/cmake/test-files.cmake
+++ b/cmake/test-files.cmake
@@ -38,6 +38,9 @@ set(MBGL_TEST_FILES
test/math/minmax.test.cpp
test/math/wrap.test.cpp
+ # programs
+ test/programs/binary_program.test.cpp
+
# sprite
test/sprite/sprite_atlas.test.cpp
test/sprite/sprite_image.test.cpp
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 02e14bea4e..6b07173747 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -41,7 +41,8 @@ public:
MapMode mapMode = MapMode::Continuous,
GLContextMode contextMode = GLContextMode::Unique,
ConstrainMode constrainMode = ConstrainMode::HeightOnly,
- ViewportMode viewportMode = ViewportMode::Default);
+ ViewportMode viewportMode = ViewportMode::Default,
+ const std::string& programCacheDir = "");
~Map();
// Register a callback that will get called (on the render thread) when all resources have
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
index 1460f08e10..a9394d0b66 100755
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
@@ -97,7 +97,8 @@ final class NativeMapView {
onMapChangedListeners = new CopyOnWriteArrayList<>();
this.mapView = mapView;
- nativeInitialize(this, fileSource, pixelRatio, availableProcessors, totalMemory);
+ String programCacheDir = context.getCacheDir().getAbsolutePath();
+ nativeInitialize(this, fileSource, pixelRatio, programCacheDir, availableProcessors, totalMemory);
}
//
@@ -957,8 +958,12 @@ final class NativeMapView {
// JNI methods
//
- private native void nativeInitialize(NativeMapView nativeMapView, FileSource fileSource,
- float pixelRatio, int availableProcessors, long totalMemory);
+ private native void nativeInitialize(NativeMapView nativeMapView,
+ FileSource fileSource,
+ float pixelRatio,
+ String programCacheDir,
+ int availableProcessors,
+ long totalMemory);
private native void nativeDestroy();
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 696849b11b..529ea23304 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -48,13 +48,18 @@
namespace mbgl {
namespace android {
-NativeMapView::NativeMapView(jni::JNIEnv& _env, jni::Object<NativeMapView> _obj, jni::Object<FileSource> jFileSource,
- jni::jfloat _pixelRatio, jni::jint _availableProcessors, jni::jlong _totalMemory) :
- javaPeer(_obj.NewWeakGlobalRef(_env)),
- pixelRatio(_pixelRatio),
- availableProcessors(_availableProcessors),
- totalMemory(_totalMemory),
- threadPool(sharedThreadPool()) {
+NativeMapView::NativeMapView(jni::JNIEnv& _env,
+ jni::Object<NativeMapView> _obj,
+ jni::Object<FileSource> jFileSource,
+ jni::jfloat _pixelRatio,
+ jni::String _programCacheDir,
+ jni::jint _availableProcessors,
+ jni::jlong _totalMemory)
+ : javaPeer(_obj.NewWeakGlobalRef(_env)),
+ pixelRatio(_pixelRatio),
+ availableProcessors(_availableProcessors),
+ totalMemory(_totalMemory),
+ threadPool(sharedThreadPool()) {
// Get a reference to the JavaVM for callbacks
if (_env.GetJavaVM(&vm) < 0) {
@@ -65,8 +70,9 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env, jni::Object<NativeMapView> _obj,
// Create the core map
map = std::make_unique<mbgl::Map>(
*this, mbgl::Size{ static_cast<uint32_t>(width), static_cast<uint32_t>(height) },
- pixelRatio, mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource)
- , *threadPool, MapMode::Continuous);
+ pixelRatio, mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource), *threadPool,
+ MapMode::Continuous, GLContextMode::Unique, ConstrainMode::HeightOnly,
+ ViewportMode::Default, jni::Make<std::string>(_env, _programCacheDir));
//Calculate a fitting cache size based on device parameters
float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1;
@@ -1398,7 +1404,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
// Register the peer
jni::RegisterNativePeer<NativeMapView>(env, NativeMapView::javaClass, "nativePtr",
- std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jlong>,
+ std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::String, jni::jint, jni::jlong>,
"nativeInitialize",
"nativeDestroy",
METHOD(&NativeMapView::render, "nativeRender"),
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index 4afea036bf..08133b85cd 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -40,7 +40,13 @@ public:
static void registerNative(jni::JNIEnv&);
- NativeMapView(jni::JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jlong);
+ NativeMapView(jni::JNIEnv&,
+ jni::Object<NativeMapView>,
+ jni::Object<FileSource>,
+ jni::jfloat pixelRatio,
+ jni::String programCacheDir,
+ jni::jint availableProcessors,
+ jni::jlong totalMemory);
virtual ~NativeMapView();
diff --git a/proto/binary_program.proto b/proto/binary_program.proto
new file mode 100644
index 0000000000..9d06a209c3
--- /dev/null
+++ b/proto/binary_program.proto
@@ -0,0 +1,18 @@
+// Protocol Version 1
+
+package mapboxgl.binary_program;
+
+option optimize_for = LITE_RUNTIME;
+
+message binding {
+ required string name = 1;
+ required uint32 value = 2;
+}
+
+message binary_program {
+ required uint32 format = 1;
+ required bytes code = 2;
+ repeated binding attribute = 3;
+ repeated binding uniform = 4;
+ optional string identifier = 5;
+}
diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp
index 43e2c2d794..e23c4f9e47 100644
--- a/src/mbgl/gl/attribute.hpp
+++ b/src/mbgl/gl/attribute.hpp
@@ -7,6 +7,7 @@
#include <mbgl/util/variant.hpp>
#include <cstddef>
+#include <vector>
#include <functional>
namespace mbgl {
@@ -241,16 +242,26 @@ public:
using VariableBindings = IndexedTuple<
TypeList<As...>,
TypeList<optional<typename As::VariableBinding>...>>;
+ using NamedLocations = std::vector<std::pair<const std::string, AttributeLocation>>;
using Vertex = detail::Vertex<As...>;
template <class A>
static constexpr std::size_t Index = TypeIndex<A, As...>::value;
- static Locations locations(const ProgramID& id) {
+ static Locations bindLocations(const ProgramID& id) {
return Locations { bindAttributeLocation(id, Index<As>, As::name())... };
}
+ template <class Program>
+ static Locations loadNamedLocations(const Program& program) {
+ return Locations{ program.attributeLocation(As::name())... };
+ }
+
+ static NamedLocations getNamedLocations(const Locations& locations) {
+ return NamedLocations{ { As::name(), locations.template get<As>() }... };
+ }
+
template <class DrawMode>
static Bindings allVariableBindings(const VertexBuffer<Vertex, DrawMode>& buffer) {
return Bindings { As::variableBinding(buffer, Index<As>)... };
diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp
index 9c2031ccfd..97ebf687f9 100644
--- a/src/mbgl/gl/context.cpp
+++ b/src/mbgl/gl/context.cpp
@@ -2,6 +2,7 @@
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/vertex_array.hpp>
+#include <mbgl/gl/program_binary.hpp>
#include <mbgl/util/traits.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/logging.hpp>
@@ -34,6 +35,8 @@ static_assert(std::is_same<std::underlying_type_t<TextureFormat>, GLenum>::value
static_assert(underlying_type(TextureFormat::RGBA) == GL_RGBA, "OpenGL type mismatch");
static_assert(underlying_type(TextureFormat::Alpha) == GL_ALPHA, "OpenGL type mismatch");
+static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mismatch");
+
Context::~Context() {
reset();
}
@@ -72,9 +75,27 @@ UniqueProgram Context::createProgram(ShaderID vertexShader, ShaderID fragmentSha
return result;
}
+#if MBGL_HAS_BINARY_PROGRAMS
+UniqueProgram Context::createProgram(BinaryProgramFormat binaryFormat,
+ const std::string& binaryProgram) {
+ UniqueProgram result{ MBGL_CHECK_ERROR(glCreateProgram()), { this } };
+ MBGL_CHECK_ERROR(ProgramBinary(result, static_cast<GLenum>(binaryFormat), binaryProgram.data(),
+ static_cast<GLint>(binaryProgram.size())));
+ verifyProgramLinkage(result);
+ return result;
+}
+#else
+UniqueProgram Context::createProgram(BinaryProgramFormat, const std::string&) {
+ throw std::runtime_error("binary programs are not supported");
+}
+#endif
+
void Context::linkProgram(ProgramID program_) {
MBGL_CHECK_ERROR(glLinkProgram(program_));
+ verifyProgramLinkage(program_);
+}
+void Context::verifyProgramLinkage(ProgramID program_) {
GLint status;
MBGL_CHECK_ERROR(glGetProgramiv(program_, GL_LINK_STATUS, &status));
if (status == GL_TRUE) {
@@ -129,6 +150,34 @@ bool Context::supportsVertexArrays() const {
!disableVAOExtension;
}
+#if MBGL_HAS_BINARY_PROGRAMS
+bool Context::supportsProgramBinaries() const {
+ return gl::ProgramBinary && gl::GetProgramBinary;
+}
+
+optional<std::pair<BinaryProgramFormat, std::string>>
+Context::getBinaryProgram(ProgramID program_) const {
+ if (!supportsProgramBinaries()) {
+ return {};
+ }
+ GLint binaryLength;
+ MBGL_CHECK_ERROR(glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &binaryLength));
+ std::string binary;
+ binary.resize(binaryLength);
+ GLenum binaryFormat;
+ MBGL_CHECK_ERROR(GetProgramBinary(program_, binaryLength, &binaryLength, &binaryFormat,
+ const_cast<char*>(binary.data())));
+ if (size_t(binaryLength) != binary.size()) {
+ return {};
+ }
+ return { { binaryFormat, std::move(binary) } };
+}
+#else
+optional<std::pair<BinaryProgramFormat, std::string>> Context::getBinaryProgram(ProgramID) const {
+ return {};
+}
+#endif
+
UniqueVertexArray Context::createVertexArray() {
assert(supportsVertexArrays());
VertexArrayID id = 0;
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 9d3ecec662..fd6683a0cc 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <mbgl/gl/features.hpp>
#include <mbgl/gl/object.hpp>
#include <mbgl/gl/state.hpp>
#include <mbgl/gl/value.hpp>
@@ -37,12 +38,21 @@ public:
UniqueShader createShader(ShaderType type, const std::string& source);
UniqueProgram createProgram(ShaderID vertexShader, ShaderID fragmentShader);
+ UniqueProgram createProgram(BinaryProgramFormat binaryFormat, const std::string& binaryProgram);
+ void verifyProgramLinkage(ProgramID);
void linkProgram(ProgramID);
UniqueTexture createTexture();
bool supportsVertexArrays() const;
UniqueVertexArray createVertexArray();
+#if MBGL_HAS_BINARY_PROGRAMS
+ bool supportsProgramBinaries() const;
+#else
+ constexpr bool supportsProgramBinaries() const { return false; }
+#endif
+ optional<std::pair<BinaryProgramFormat, std::string>> getBinaryProgram(ProgramID) const;
+
template <class Vertex, class DrawMode>
VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v) {
return VertexBuffer<Vertex, DrawMode> {
diff --git a/src/mbgl/gl/features.hpp b/src/mbgl/gl/features.hpp
new file mode 100644
index 0000000000..04dc4fa02e
--- /dev/null
+++ b/src/mbgl/gl/features.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#if __APPLE__
+ #define MBGL_HAS_BINARY_PROGRAMS 0
+#else
+ #define MBGL_HAS_BINARY_PROGRAMS 1
+#endif \ No newline at end of file
diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp
index fa0470796e..7d7fca5263 100644
--- a/src/mbgl/gl/program.hpp
+++ b/src/mbgl/gl/program.hpp
@@ -24,11 +24,29 @@ public:
using AttributeBindings = typename Attributes::Bindings;
Program(Context& context, const std::string& vertexSource, const std::string& fragmentSource)
- : vertexShader(context.createShader(ShaderType::Vertex, vertexSource)),
- fragmentShader(context.createShader(ShaderType::Fragment, fragmentSource)),
- program(context.createProgram(vertexShader, fragmentShader)),
- attributeLocations(Attributes::locations(program)),
- uniformsState((context.linkProgram(program), Uniforms::state(program))) {}
+ : program(
+ context.createProgram(context.createShader(ShaderType::Vertex, vertexSource),
+ context.createShader(ShaderType::Fragment, fragmentSource))),
+ attributeLocations(Attributes::bindLocations(program)),
+ uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))) {
+ }
+
+ template <class BinaryProgram>
+ Program(Context& context, const BinaryProgram& binaryProgram)
+ : program(context.createProgram(binaryProgram.format(), binaryProgram.code())),
+ attributeLocations(Attributes::loadNamedLocations(binaryProgram)),
+ uniformsState(Uniforms::loadNamedLocations(binaryProgram)) {
+ }
+
+ template <class BinaryProgram>
+ optional<BinaryProgram> get(Context& context, const std::string& identifier) const {
+ if (auto binaryProgram = context.getBinaryProgram(program)) {
+ return BinaryProgram{ binaryProgram->first, std::move(binaryProgram->second),
+ identifier, Attributes::getNamedLocations(attributeLocations),
+ Uniforms::getNamedLocations(uniformsState) };
+ }
+ return {};
+ }
template <class DrawMode>
void draw(Context& context,
@@ -64,8 +82,6 @@ public:
}
private:
- UniqueShader vertexShader;
- UniqueShader fragmentShader;
UniqueProgram program;
typename Attributes::Locations attributeLocations;
diff --git a/src/mbgl/gl/program_binary.cpp b/src/mbgl/gl/program_binary.cpp
new file mode 100644
index 0000000000..ad147c819f
--- /dev/null
+++ b/src/mbgl/gl/program_binary.cpp
@@ -0,0 +1,24 @@
+#include <mbgl/gl/program_binary.hpp>
+
+#if MBGL_HAS_BINARY_PROGRAMS
+
+namespace mbgl {
+namespace gl {
+
+ExtensionFunction<
+ void(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary)>
+ GetProgramBinary({
+ { "GL_OES_get_program_binary", "glGetProgramBinaryOES" },
+ { "GL_ARB_get_program_binary", "glGetProgramBinary" },
+ });
+
+ExtensionFunction<void(GLuint program, GLenum binaryFormat, const GLvoid* binary, GLint length)>
+ ProgramBinary({
+ { "GL_OES_get_program_binary", "glProgramBinaryOES" },
+ { "GL_ARB_get_program_binary", "glProgramBinary" },
+ });
+
+} // namespace gl
+} // namespace mbgl
+
+#endif
diff --git a/src/mbgl/gl/program_binary.hpp b/src/mbgl/gl/program_binary.hpp
new file mode 100644
index 0000000000..e888ed3d4a
--- /dev/null
+++ b/src/mbgl/gl/program_binary.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <mbgl/gl/features.hpp>
+#include <mbgl/gl/extension.hpp>
+#include <mbgl/gl/gl.hpp>
+
+#if MBGL_HAS_BINARY_PROGRAMS
+
+#define GL_PROGRAM_BINARY_LENGTH 0x8741
+#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
+#define GL_PROGRAM_BINARY_FORMATS 0x87FF
+
+namespace mbgl {
+namespace gl {
+
+extern ExtensionFunction<void(
+ GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary)>
+ GetProgramBinary;
+
+extern ExtensionFunction<void(
+ GLuint program, GLenum binaryFormat, const GLvoid* binary, GLint length)>
+ ProgramBinary;
+
+} // namespace gl
+} // namespace mbgl
+
+#endif
diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp
index 565ca5754f..7d436693c9 100644
--- a/src/mbgl/gl/types.hpp
+++ b/src/mbgl/gl/types.hpp
@@ -73,5 +73,7 @@ constexpr bool operator!=(const PixelStorageType& a, const PixelStorageType& b)
#endif // MBGL_USE_GLES2
+using BinaryProgramFormat = uint32_t;
+
} // namespace gl
} // namespace mbgl
diff --git a/src/mbgl/gl/uniform.hpp b/src/mbgl/gl/uniform.hpp
index 92136b61c2..34a32aeee9 100644
--- a/src/mbgl/gl/uniform.hpp
+++ b/src/mbgl/gl/uniform.hpp
@@ -6,6 +6,7 @@
#include <mbgl/util/indexed_tuple.hpp>
#include <array>
+#include <vector>
#include <functional>
namespace mbgl {
@@ -66,11 +67,21 @@ public:
using Types = TypeList<Us...>;
using State = IndexedTuple<TypeList<Us...>, TypeList<typename Us::State...>>;
using Values = IndexedTuple<TypeList<Us...>, TypeList<typename Us::Value...>>;
+ using NamedLocations = std::vector<std::pair<const std::string, UniformLocation>>;
- static State state(const ProgramID& id) {
+ static State bindLocations(const ProgramID& id) {
return State { { uniformLocation(id, Us::name()) }... };
}
+ template <class Program>
+ static State loadNamedLocations(const Program& program) {
+ return State{ { program.uniformLocation(Us::name()) }... };
+ }
+
+ static NamedLocations getNamedLocations(const State& state) {
+ return NamedLocations{ { Us::name(), state.template get<Us>().location }... };
+ }
+
static void bind(State& state, Values&& values) {
util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... });
}
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index f4e994c931..aca1b77e7a 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -56,7 +56,8 @@ public:
MapMode,
GLContextMode,
ConstrainMode,
- ViewportMode);
+ ViewportMode,
+ const std::string& programCacheDir);
void onSourceAttributionChanged(style::Source&, const std::string&) override;
void onUpdate(Update) override;
@@ -80,6 +81,7 @@ public:
const MapMode mode;
const GLContextMode contextMode;
const float pixelRatio;
+ const std::string programCacheDir;
MapDebugOptions debugOptions { MapDebugOptions::NoDebug };
@@ -111,7 +113,8 @@ Map::Map(Backend& backend,
MapMode mapMode,
GLContextMode contextMode,
ConstrainMode constrainMode,
- ViewportMode viewportMode)
+ ViewportMode viewportMode,
+ const std::string& programCacheDir)
: impl(std::make_unique<Impl>(*this,
backend,
pixelRatio,
@@ -120,7 +123,8 @@ Map::Map(Backend& backend,
mapMode,
contextMode,
constrainMode,
- viewportMode)) {
+ viewportMode,
+ programCacheDir)) {
impl->transform.resize(size);
}
@@ -132,7 +136,8 @@ Map::Impl::Impl(Map& map_,
MapMode mode_,
GLContextMode contextMode_,
ConstrainMode constrainMode_,
- ViewportMode viewportMode_)
+ ViewportMode viewportMode_,
+ const std::string& programCacheDir_)
: map(map_),
backend(backend_),
fileSource(fileSource_),
@@ -143,6 +148,7 @@ Map::Impl::Impl(Map& map_,
mode(mode_),
contextMode(contextMode_),
pixelRatio(pixelRatio_),
+ programCacheDir(programCacheDir_),
annotationManager(std::make_unique<AnnotationManager>(pixelRatio)),
asyncInvalidate([this] {
if (mode == MapMode::Continuous) {
@@ -259,7 +265,7 @@ void Map::Impl::render(View& view) {
updateFlags = Update::Nothing;
if (!painter) {
- painter = std::make_unique<Painter>(backend.getContext(), transform.getState(), pixelRatio);
+ painter = std::make_unique<Painter>(backend.getContext(), transform.getState(), pixelRatio, programCacheDir);
}
if (mode == MapMode::Continuous) {
diff --git a/src/mbgl/programs/binary_program.cpp b/src/mbgl/programs/binary_program.cpp
new file mode 100644
index 0000000000..3b37cfa442
--- /dev/null
+++ b/src/mbgl/programs/binary_program.cpp
@@ -0,0 +1,117 @@
+#include <mbgl/programs/binary_program.hpp>
+
+#include <protozero/pbf_reader.hpp>
+#include <protozero/pbf_writer.hpp>
+
+template <class Binding>
+static std::pair<const std::string, Binding> parseBinding(protozero::pbf_reader&& pbf) {
+ bool hasName = false, hasValue = false;
+ std::pair<std::string, Binding> binding;
+ while (pbf.next()) {
+ switch (pbf.tag()) {
+ case 1: // name
+ binding.first = pbf.get_string();
+ hasName = true;
+ break;
+ case 2: // value
+ binding.second = pbf.get_uint32();
+ hasValue = true;
+ break;
+ default:
+ pbf.skip();
+ break;
+ }
+ }
+ if (!hasName || !hasValue) {
+ throw std::runtime_error("BinaryProgram binding is missing required fields");
+ }
+ return binding;
+}
+
+namespace mbgl {
+
+BinaryProgram::BinaryProgram(std::string&& data) {
+ bool hasFormat = false, hasCode = false;
+ protozero::pbf_reader pbf(data);
+ while (pbf.next()) {
+ switch (pbf.tag()) {
+ case 1: // format
+ binaryFormat = pbf.get_uint32();
+ hasFormat = true;
+ break;
+ case 2: // code
+ binaryCode = pbf.get_bytes();
+ hasCode = true;
+ break;
+ case 3: // variable
+ attributes.emplace_back(parseBinding<gl::AttributeLocation>(pbf.get_message()));
+ break;
+ case 4: // uniform
+ uniforms.emplace_back(parseBinding<gl::UniformLocation>(pbf.get_message()));
+ break;
+ case 5: // identifier
+ default:
+ binaryIdentifier = pbf.get_string();
+ break;
+ }
+ }
+
+ if (!hasFormat || !hasCode) {
+ throw std::runtime_error("BinaryProgram is missing required fields");
+ }
+}
+
+BinaryProgram::BinaryProgram(
+ gl::BinaryProgramFormat binaryFormat_,
+ std::string&& binaryCode_,
+ const std::string& binaryIdentifier_,
+ std::vector<std::pair<const std::string, gl::AttributeLocation>>&& attributes_,
+ std::vector<std::pair<const std::string, gl::UniformLocation>>&& uniforms_)
+ : binaryFormat(binaryFormat_),
+ binaryCode(std::move(binaryCode_)),
+ binaryIdentifier(binaryIdentifier_),
+ attributes(std::move(attributes_)),
+ uniforms(std::move(uniforms_)) {
+}
+
+std::string BinaryProgram::serialize() const {
+ std::string data;
+ data.reserve(32 + binaryCode.size() + uniforms.size() * 32 + attributes.size() * 32);
+ protozero::pbf_writer pbf(data);
+ pbf.add_uint32(1 /* format */, binaryFormat);
+ pbf.add_bytes(2 /* code */, binaryCode.data(), binaryCode.size());
+ for (const auto& binding : attributes) {
+ protozero::pbf_writer pbf_binding(pbf, 3 /* attribute */);
+ pbf_binding.add_string(1 /* name */, binding.first);
+ pbf_binding.add_uint32(2 /* value */, binding.second);
+ }
+ for (const auto& binding : uniforms) {
+ protozero::pbf_writer pbf_binding(pbf, 4 /* uniform */);
+ pbf_binding.add_string(1 /* name */, binding.first);
+ pbf_binding.add_uint32(2 /* value */, binding.second);
+ }
+ if (!binaryIdentifier.empty()) {
+ pbf.add_string(5 /* identifier */, binaryIdentifier);
+ }
+ return data;
+}
+
+gl::AttributeLocation BinaryProgram::attributeLocation(const std::string& name) const {
+ for (const auto& pair : attributes) {
+ if (pair.first == name) {
+ return pair.second;
+ }
+ }
+ return {};
+}
+
+gl::UniformLocation BinaryProgram::uniformLocation(const std::string& name) const {
+ for (const auto& pair : uniforms) {
+ if (pair.first == name) {
+ return pair.second;
+ }
+ }
+ return {};
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/binary_program.hpp b/src/mbgl/programs/binary_program.hpp
new file mode 100644
index 0000000000..8ff3863dc1
--- /dev/null
+++ b/src/mbgl/programs/binary_program.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <mbgl/gl/types.hpp>
+
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+class BinaryProgram {
+public:
+ // Initialize a BinaryProgram object from a serialized represenation.
+ BinaryProgram(std::string&& data);
+
+ BinaryProgram(gl::BinaryProgramFormat,
+ std::string&& binaryCode,
+ const std::string& binaryIdentifier,
+ std::vector<std::pair<const std::string, gl::AttributeLocation>>&&,
+ std::vector<std::pair<const std::string, gl::UniformLocation>>&&);
+
+ std::string serialize() const;
+
+ gl::BinaryProgramFormat format() const {
+ return binaryFormat;
+ }
+ const std::string& code() const {
+ return binaryCode;
+ }
+ const std::string& identifier() const {
+ return binaryIdentifier;
+ }
+ gl::AttributeLocation attributeLocation(const std::string& name) const;
+ gl::UniformLocation uniformLocation(const std::string& name) const;
+
+private:
+ gl::BinaryProgramFormat binaryFormat = 0;
+ std::string binaryCode;
+ std::string binaryIdentifier;
+ std::vector<std::pair<const std::string, gl::AttributeLocation>> attributes;
+ std::vector<std::pair<const std::string, gl::UniformLocation>> uniforms;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp
index 8437e3a651..8925bc75d6 100644
--- a/src/mbgl/programs/program.hpp
+++ b/src/mbgl/programs/program.hpp
@@ -1,9 +1,13 @@
#pragma once
#include <mbgl/gl/program.hpp>
+#include <mbgl/gl/features.hpp>
+#include <mbgl/programs/binary_program.hpp>
#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/program_parameters.hpp>
#include <mbgl/style/paint_property.hpp>
#include <mbgl/shaders/shaders.hpp>
+#include <mbgl/util/io.hpp>
namespace mbgl {
@@ -30,10 +34,56 @@ public:
ProgramType program;
Program(gl::Context& context, const ProgramParameters& programParameters)
- : program(context,
- shaders::vertexSource(programParameters, Shaders::vertexSource),
- shaders::fragmentSource(programParameters, Shaders::fragmentSource))
- {}
+ : program([&] {
+#if MBGL_HAS_BINARY_PROGRAMS
+ if (!programParameters.cacheDir.empty() && context.supportsProgramBinaries()) {
+ const std::string vertexSource =
+ shaders::vertexSource(programParameters, Shaders::vertexSource);
+ const std::string fragmentSource =
+ shaders::fragmentSource(programParameters, Shaders::fragmentSource);
+ const std::string cachePath =
+ shaders::programCachePath(programParameters, Shaders::name);
+ const std::string identifier =
+ shaders::programIdentifier(vertexSource, fragmentSource);
+
+ try {
+ if (auto cachedBinaryProgram = util::readFile(cachePath)) {
+ const BinaryProgram binaryProgram(std::move(*cachedBinaryProgram));
+ if (binaryProgram.identifier() == identifier) {
+ return ProgramType{ context, binaryProgram };
+ } else {
+ Log::Warning(Event::OpenGL,
+ "Cached program %s changed. Recompilation required.",
+ Shaders::name);
+ }
+ }
+ } catch (std::runtime_error& error) {
+ Log::Warning(Event::OpenGL, "Could not load cached program: %s",
+ error.what());
+ }
+
+ // Compile the shader
+ ProgramType result{ context, vertexSource, fragmentSource };
+
+ try {
+ if (const auto binaryProgram =
+ result.template get<BinaryProgram>(context, identifier)) {
+ util::write_file(cachePath, binaryProgram->serialize());
+ Log::Warning(Event::OpenGL, "Caching program in: %s", cachePath.c_str());
+ }
+ } catch (std::runtime_error& error) {
+ Log::Warning(Event::OpenGL, "Failed to cache program: %s", error.what());
+ }
+
+ return std::move(result);
+ }
+#endif
+ return ProgramType{
+ context, shaders::vertexSource(programParameters, Shaders::vertexSource),
+ shaders::fragmentSource(programParameters, Shaders::fragmentSource)
+ };
+ }()) {
+ }
template <class DrawMode>
void draw(gl::Context& context,
diff --git a/src/mbgl/programs/program_parameters.hpp b/src/mbgl/programs/program_parameters.hpp
index ad8cbf1bf8..b91b41f358 100644
--- a/src/mbgl/programs/program_parameters.hpp
+++ b/src/mbgl/programs/program_parameters.hpp
@@ -1,15 +1,20 @@
#pragma once
+#include <string>
+
namespace mbgl {
class ProgramParameters {
public:
- ProgramParameters(float pixelRatio_ = 1.0, bool overdraw_ = false)
- : pixelRatio(pixelRatio_),
- overdraw(overdraw_) {}
+ ProgramParameters(float pixelRatio_ = 1.0,
+ bool overdraw_ = false,
+ const std::string& cacheDir_ = "")
+ : pixelRatio(pixelRatio_), overdraw(overdraw_), cacheDir(cacheDir_) {
+ }
- float pixelRatio;
- bool overdraw;
+ const float pixelRatio;
+ const bool overdraw;
+ const std::string cacheDir;
};
} // namespace mbgl
diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp
index 742c5a221b..dbf5b9e87d 100644
--- a/src/mbgl/programs/programs.hpp
+++ b/src/mbgl/programs/programs.hpp
@@ -26,8 +26,8 @@ public:
symbolIcon(context, programParameters),
symbolIconSDF(context, programParameters),
symbolGlyph(context, programParameters),
- debug(context, ProgramParameters(programParameters.pixelRatio, false)),
- collisionBox(context, ProgramParameters(programParameters.pixelRatio, false)) {
+ debug(context, ProgramParameters(programParameters.pixelRatio, false, programParameters.cacheDir)),
+ collisionBox(context, ProgramParameters(programParameters.pixelRatio, false, programParameters.cacheDir)) {
}
CircleProgram circle;
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index 27d24d14a9..754959438b 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -33,6 +33,8 @@
#include <mbgl/util/offscreen_texture.hpp>
+#include <mbgl/util/stopwatch.hpp>
+
#include <cassert>
#include <algorithm>
#include <iostream>
@@ -77,7 +79,10 @@ static gl::VertexVector<RasterLayoutVertex> rasterVertices() {
return result;
}
-Painter::Painter(gl::Context& context_, const TransformState& state_, float pixelRatio)
+Painter::Painter(gl::Context& context_,
+ const TransformState& state_,
+ float pixelRatio,
+ const std::string& programCacheDir)
: context(context_),
state(state_),
tileVertexBuffer(context.createVertexBuffer(tileVertices())),
@@ -91,12 +96,11 @@ Painter::Painter(gl::Context& context_, const TransformState& state_, float pixe
gl::debugging::enable();
- ProgramParameters programParameters{ pixelRatio, false };
- programs = std::make_unique<Programs>(context, programParameters);
+ programs = std::make_unique<Programs>(context,
+ ProgramParameters{ pixelRatio, false, programCacheDir });
#ifndef NDEBUG
-
- ProgramParameters programParametersOverdraw{ pixelRatio, true };
- overdrawPrograms = std::make_unique<Programs>(context, programParametersOverdraw);
+ overdrawPrograms =
+ std::make_unique<Programs>(context, ProgramParameters{ pixelRatio, true, programCacheDir });
#endif
}
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
index 91f329a6eb..3dcc1d5d46 100644
--- a/src/mbgl/renderer/painter.hpp
+++ b/src/mbgl/renderer/painter.hpp
@@ -68,7 +68,7 @@ struct FrameData {
class Painter : private util::noncopyable {
public:
- Painter(gl::Context&, const TransformState&, float pixelRatio);
+ Painter(gl::Context&, const TransformState&, float pixelRatio, const std::string& programCacheDir);
~Painter();
void render(const style::Style&,
diff --git a/src/mbgl/shaders/shaders.cpp b/src/mbgl/shaders/shaders.cpp
index f7f5b4d44f..03d796edba 100644
--- a/src/mbgl/shaders/shaders.cpp
+++ b/src/mbgl/shaders/shaders.cpp
@@ -4,6 +4,7 @@
#include <cassert>
#include <sstream>
+#include <iomanip>
namespace mbgl {
namespace shaders {
@@ -29,5 +30,18 @@ std::string vertexSource(const ProgramParameters& parameters, const char* vertex
return pixelRatioDefine(parameters) + vertexPrelude + vertexSource;
}
+std::string programCachePath(const ProgramParameters& parameters, const char* name) {
+ return parameters.cacheDir + "/com.mapbox.gl.shader." + name +
+ (parameters.overdraw ? ".overdraw.pbf" : ".pbf");
+}
+
+std::string programIdentifier(const std::string& vertexSource, const std::string& fragmentSource) {
+ std::ostringstream ss;
+ ss << std::setfill('0') << std::setw(sizeof(size_t) * 2) << std::hex;
+ ss << std::hash<std::string>()(vertexSource);
+ ss << std::hash<std::string>()(fragmentSource);
+ return ss.str();
+}
+
} // namespace shaders
} // namespace mbgl
diff --git a/src/mbgl/shaders/shaders.hpp b/src/mbgl/shaders/shaders.hpp
index e2912c5688..126c64bb9e 100644
--- a/src/mbgl/shaders/shaders.hpp
+++ b/src/mbgl/shaders/shaders.hpp
@@ -10,6 +10,8 @@ namespace shaders {
std::string fragmentSource(const ProgramParameters&, const char* fragmentSource);
std::string vertexSource(const ProgramParameters&, const char* vertexSource);
+std::string programCachePath(const ProgramParameters&, const char* name);
+std::string programIdentifier(const std::string& vertexSource, const std::string& fragmentSource);
} // namespace shaders
} // namespace mbgl
diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp
index f37ce09cff..9adc3b8988 100644
--- a/src/mbgl/util/io.cpp
+++ b/src/mbgl/util/io.cpp
@@ -32,6 +32,16 @@ std::string read_file(const std::string &filename) {
}
}
+optional<std::string> readFile(const std::string &filename) {
+ std::ifstream file(filename);
+ if (file.good()) {
+ std::stringstream data;
+ data << file.rdbuf();
+ return data.str();
+ }
+ return {};
+}
+
void deleteFile(const std::string& filename) {
const int ret = unlink(filename.c_str());
if (ret == -1) {
diff --git a/src/mbgl/util/io.hpp b/src/mbgl/util/io.hpp
index 795a465328..847271acf0 100644
--- a/src/mbgl/util/io.hpp
+++ b/src/mbgl/util/io.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <mbgl/util/optional.hpp>
+
#include <string>
#include <stdexcept>
@@ -15,6 +17,7 @@ struct IOException : std::runtime_error {
void write_file(const std::string &filename, const std::string &data);
std::string read_file(const std::string &filename);
+optional<std::string> readFile(const std::string &filename);
void deleteFile(const std::string& filename);
} // namespace util
diff --git a/test/programs/binary_program.test.cpp b/test/programs/binary_program.test.cpp
new file mode 100644
index 0000000000..ce544e7652
--- /dev/null
+++ b/test/programs/binary_program.test.cpp
@@ -0,0 +1,39 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/programs/binary_program.hpp>
+
+using namespace mbgl;
+
+TEST(BinaryProgram, ObtainValues) {
+ const BinaryProgram binaryProgram{ 42,
+ "binary code",
+ "identifier",
+ { { "a_pos", 1 }, { "a_data", 4 } },
+ { { "u_world", 1 }, { "u_ratio", 3 } } };
+
+ EXPECT_EQ(42u, binaryProgram.format());
+ EXPECT_EQ("binary code", binaryProgram.code());
+ EXPECT_EQ("identifier", binaryProgram.identifier());
+ EXPECT_EQ(1, binaryProgram.attributeLocation("a_pos"));
+ EXPECT_EQ(0, binaryProgram.attributeLocation("u_world"));
+ EXPECT_EQ(4, binaryProgram.attributeLocation("a_data"));
+ EXPECT_EQ(1, binaryProgram.uniformLocation("u_world"));
+ EXPECT_EQ(3, binaryProgram.uniformLocation("u_ratio"));
+ EXPECT_EQ(0, binaryProgram.uniformLocation("a_data"));
+
+ auto serialized = binaryProgram.serialize();
+
+ const BinaryProgram binaryProgram2(std::move(serialized));
+
+ EXPECT_EQ(42u, binaryProgram2.format());
+ EXPECT_EQ("binary code", binaryProgram2.code());
+ EXPECT_EQ("identifier", binaryProgram2.identifier());
+ EXPECT_EQ(1, binaryProgram2.attributeLocation("a_pos"));
+ EXPECT_EQ(0, binaryProgram2.attributeLocation("u_world"));
+ EXPECT_EQ(4, binaryProgram2.attributeLocation("a_data"));
+ EXPECT_EQ(1, binaryProgram2.uniformLocation("u_world"));
+ EXPECT_EQ(3, binaryProgram2.uniformLocation("u_ratio"));
+ EXPECT_EQ(0, binaryProgram2.uniformLocation("a_data"));
+
+ EXPECT_THROW(BinaryProgram(""), std::runtime_error);
+}