summaryrefslogtreecommitdiff
path: root/src/mbgl/programs
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2017-03-15 18:43:58 +0100
committerKonstantin Käfer <mail@kkaefer.com>2017-03-22 15:59:10 +0100
commit3c91b6b7de487993e75de552ba44249740644e42 (patch)
tree69b8b2642b3d8496657db92459b0a23e130b501d /src/mbgl/programs
parentd20327844cac88b6bf42b201eac35e816a189a8a (diff)
downloadqtlocation-mapboxgl-3c91b6b7de487993e75de552ba44249740644e42.tar.gz
[core] cache binary shaders on Android
Diffstat (limited to 'src/mbgl/programs')
-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
5 files changed, 226 insertions, 11 deletions
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;