[core] cache binary shaders on Android

This commit is contained in:
Konstantin Käfer
2017-03-15 18:43:58 +01:00
parent d20327844c
commit 3c91b6b7de
29 changed files with 541 additions and 47 deletions

View File

@@ -63,6 +63,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
@@ -70,6 +71,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
@@ -130,6 +133,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

View File

@@ -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

View File

@@ -42,7 +42,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

View File

@@ -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);
}
//
@@ -961,8 +962,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();

View File

@@ -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;
@@ -1462,7 +1468,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"),

View File

@@ -42,7 +42,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();

View File

@@ -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;
}

View File

@@ -7,6 +7,7 @@
#include <mbgl/util/variant.hpp>
#include <cstddef>
#include <vector>
#include <functional>
namespace mbgl {
@@ -244,16 +245,26 @@ public:
using VariableBindings = IndexedTuple<
TypeList<As...>,
TypeList<optional<typename As::Type::VariableBinding>...>>;
using NamedLocations = std::vector<std::pair<const std::string, AttributeLocation>>;
using Vertex = detail::Vertex<typename As::Type...>;
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::Type::variableBinding(buffer, Index<As>)... };

View File

@@ -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;

View File

@@ -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>
@@ -36,12 +37,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> {

7
src/mbgl/gl/features.hpp Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#if __APPLE__
#define MBGL_HAS_BINARY_PROGRAMS 0
#else
#define MBGL_HAS_BINARY_PROGRAMS 1
#endif

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)... });
}

View File

@@ -57,7 +57,8 @@ public:
MapMode,
GLContextMode,
ConstrainMode,
ViewportMode);
ViewportMode,
const std::string& programCacheDir);
void onSourceChanged(style::Source&) override;
void onUpdate(Update) override;
@@ -82,6 +83,7 @@ public:
const MapMode mode;
const GLContextMode contextMode;
const float pixelRatio;
const std::string programCacheDir;
MapDebugOptions debugOptions { MapDebugOptions::NoDebug };
@@ -113,7 +115,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,
@@ -122,7 +125,8 @@ Map::Map(Backend& backend,
mapMode,
contextMode,
constrainMode,
viewportMode)) {
viewportMode,
programCacheDir)) {
impl->transform.resize(size);
}
@@ -134,7 +138,8 @@ Map::Impl::Impl(Map& map_,
MapMode mode_,
GLContextMode contextMode_,
ConstrainMode constrainMode_,
ViewportMode viewportMode_)
ViewportMode viewportMode_,
const std::string& programCacheDir_)
: map(map_),
observer(backend_),
backend(backend_),
@@ -146,6 +151,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) {
@@ -262,7 +268,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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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;

View File

@@ -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
}

View File

@@ -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&,

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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);
}