[core] cache binary shaders on Android
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
18
proto/binary_program.proto
Normal file
18
proto/binary_program.proto
Normal 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;
|
||||
}
|
||||
@@ -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>)... };
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
7
src/mbgl/gl/features.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#if __APPLE__
|
||||
#define MBGL_HAS_BINARY_PROGRAMS 0
|
||||
#else
|
||||
#define MBGL_HAS_BINARY_PROGRAMS 1
|
||||
#endif
|
||||
@@ -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;
|
||||
|
||||
24
src/mbgl/gl/program_binary.cpp
Normal file
24
src/mbgl/gl/program_binary.cpp
Normal 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
|
||||
27
src/mbgl/gl/program_binary.hpp
Normal file
27
src/mbgl/gl/program_binary.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)... });
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
117
src/mbgl/programs/binary_program.cpp
Normal file
117
src/mbgl/programs/binary_program.cpp
Normal 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
|
||||
43
src/mbgl/programs/binary_program.hpp
Normal file
43
src/mbgl/programs/binary_program.hpp
Normal 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
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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&,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
39
test/programs/binary_program.test.cpp
Normal file
39
test/programs/binary_program.test.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user