diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2017-03-15 18:43:58 +0100 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2017-03-22 15:59:10 +0100 |
commit | 3c91b6b7de487993e75de552ba44249740644e42 (patch) | |
tree | 69b8b2642b3d8496657db92459b0a23e130b501d /src/mbgl/programs | |
parent | d20327844cac88b6bf42b201eac35e816a189a8a (diff) | |
download | qtlocation-mapboxgl-3c91b6b7de487993e75de552ba44249740644e42.tar.gz |
[core] cache binary shaders on Android
Diffstat (limited to 'src/mbgl/programs')
-rw-r--r-- | src/mbgl/programs/binary_program.cpp | 117 | ||||
-rw-r--r-- | src/mbgl/programs/binary_program.hpp | 43 | ||||
-rw-r--r-- | src/mbgl/programs/program.hpp | 58 | ||||
-rw-r--r-- | src/mbgl/programs/program_parameters.hpp | 15 | ||||
-rw-r--r-- | src/mbgl/programs/programs.hpp | 4 |
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; |