[core] Dynamic program compilation for data-driven properties
This commit is contained in:
@@ -44,7 +44,7 @@ public:
|
||||
GLContextMode contextMode = GLContextMode::Unique,
|
||||
ConstrainMode constrainMode = ConstrainMode::HeightOnly,
|
||||
ViewportMode viewportMode = ViewportMode::Default,
|
||||
const std::string& programCacheDir = "");
|
||||
const optional<std::string>& programCacheDir = {});
|
||||
~Map();
|
||||
|
||||
// Register a callback that will get called (on the render thread) when all resources have
|
||||
|
||||
@@ -50,7 +50,8 @@ public:
|
||||
const char* vertexSource_,
|
||||
const char* fragmentSource_) {
|
||||
#if MBGL_HAS_BINARY_PROGRAMS
|
||||
if (!programParameters.cacheDir.empty() && context.supportsProgramBinaries()) {
|
||||
optional<std::string> cachePath = programParameters.cachePath(name);
|
||||
if (cachePath && context.supportsProgramBinaries()) {
|
||||
const std::string vertexSource =
|
||||
shaders::vertexSource(programParameters, vertexSource_);
|
||||
const std::string fragmentSource =
|
||||
@@ -58,10 +59,8 @@ public:
|
||||
const std::string identifier =
|
||||
shaders::programIdentifier(vertexSource, fragmentSource_);
|
||||
|
||||
const std::string cachePath = programParameters.cachePath(name);
|
||||
|
||||
try {
|
||||
if (auto cachedBinaryProgram = util::readFile(cachePath)) {
|
||||
if (auto cachedBinaryProgram = util::readFile(*cachePath)) {
|
||||
const BinaryProgram binaryProgram(std::move(*cachedBinaryProgram));
|
||||
if (binaryProgram.identifier() == identifier) {
|
||||
return Program { context, binaryProgram };
|
||||
@@ -82,8 +81,8 @@ public:
|
||||
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());
|
||||
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());
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
GLContextMode,
|
||||
ConstrainMode,
|
||||
ViewportMode,
|
||||
const std::string& programCacheDir);
|
||||
optional<std::string> programCacheDir);
|
||||
|
||||
void onSourceChanged(style::Source&) override;
|
||||
void onUpdate(Update) override;
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
const MapMode mode;
|
||||
const GLContextMode contextMode;
|
||||
const float pixelRatio;
|
||||
const std::string programCacheDir;
|
||||
const optional<std::string> programCacheDir;
|
||||
|
||||
MapDebugOptions debugOptions { MapDebugOptions::NoDebug };
|
||||
|
||||
@@ -116,7 +116,7 @@ Map::Map(Backend& backend,
|
||||
GLContextMode contextMode,
|
||||
ConstrainMode constrainMode,
|
||||
ViewportMode viewportMode,
|
||||
const std::string& programCacheDir)
|
||||
const optional<std::string>& programCacheDir)
|
||||
: impl(std::make_unique<Impl>(*this,
|
||||
backend,
|
||||
pixelRatio,
|
||||
@@ -139,7 +139,7 @@ Map::Impl::Impl(Map& map_,
|
||||
GLContextMode contextMode_,
|
||||
ConstrainMode constrainMode_,
|
||||
ViewportMode viewportMode_,
|
||||
const std::string& programCacheDir_)
|
||||
optional<std::string> programCacheDir_)
|
||||
: map(map_),
|
||||
observer(backend_),
|
||||
backend(backend_),
|
||||
|
||||
@@ -9,18 +9,21 @@
|
||||
#include <mbgl/shaders/shaders.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
template <class Shaders,
|
||||
class Primitive,
|
||||
class LayoutAttrs,
|
||||
class Uniforms,
|
||||
class PaintProperties>
|
||||
class PaintProps>
|
||||
class Program {
|
||||
public:
|
||||
using LayoutAttributes = LayoutAttrs;
|
||||
using LayoutVertex = typename LayoutAttributes::Vertex;
|
||||
|
||||
using PaintProperties = PaintProps;
|
||||
using PaintPropertyBinders = typename PaintProperties::Binders;
|
||||
using PaintAttributes = typename PaintPropertyBinders::Attributes;
|
||||
using Attributes = gl::ConcatenateAttributes<LayoutAttributes, PaintAttributes>;
|
||||
@@ -71,4 +74,34 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class Program>
|
||||
class ProgramMap {
|
||||
public:
|
||||
using PaintProperties = typename Program::PaintProperties;
|
||||
using PaintPropertyBinders = typename Program::PaintPropertyBinders;
|
||||
using Bitset = typename PaintPropertyBinders::Bitset;
|
||||
|
||||
ProgramMap(gl::Context& context_, ProgramParameters parameters_)
|
||||
: context(context_),
|
||||
parameters(std::move(parameters_)) {
|
||||
}
|
||||
|
||||
Program& get(const typename PaintProperties::Evaluated& currentProperties) {
|
||||
Bitset bits = PaintPropertyBinders::constants(currentProperties);
|
||||
auto it = programs.find(bits);
|
||||
if (it != programs.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return programs.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(bits),
|
||||
std::forward_as_tuple(context,
|
||||
parameters.withAdditionalDefines(PaintPropertyBinders::defines(currentProperties)))).first->second;
|
||||
}
|
||||
|
||||
private:
|
||||
gl::Context& context;
|
||||
ProgramParameters parameters;
|
||||
std::unordered_map<Bitset, Program> programs;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbgl {
|
||||
|
||||
ProgramParameters::ProgramParameters(const float pixelRatio,
|
||||
const bool overdraw,
|
||||
std::string cacheDir_)
|
||||
optional<std::string> cacheDir_)
|
||||
: defines([&] {
|
||||
std::ostringstream ss;
|
||||
ss.imbue(std::locale("C"));
|
||||
@@ -18,15 +18,31 @@ ProgramParameters::ProgramParameters(const float pixelRatio,
|
||||
}
|
||||
return ss.str();
|
||||
}()),
|
||||
hash(std::hash<std::string>()(defines)),
|
||||
cacheDir(std::move(cacheDir_)) {
|
||||
}
|
||||
|
||||
std::string ProgramParameters::cachePath(const char* name) const {
|
||||
std::ostringstream ss;
|
||||
ss << cacheDir << "/com.mapbox.gl.shader." << name << "." << std::setfill('0')
|
||||
<< std::setw(sizeof(size_t) * 2) << std::hex << hash << ".pbf";
|
||||
return ss.str();
|
||||
const std::string& ProgramParameters::getDefines() const {
|
||||
return defines;
|
||||
}
|
||||
|
||||
optional<std::string> ProgramParameters::cachePath(const char* name) const {
|
||||
if (!cacheDir) {
|
||||
return {};
|
||||
} else {
|
||||
std::ostringstream ss;
|
||||
ss << *cacheDir << "/com.mapbox.gl.shader." << name << "." << std::setfill('0')
|
||||
<< std::setw(sizeof(size_t) * 2) << std::hex << std::hash<std::string>()(defines) << ".pbf";
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
ProgramParameters ProgramParameters::withAdditionalDefines(const std::vector<std::string>& additionalDefines) const {
|
||||
ProgramParameters result(*this);
|
||||
for (const auto& define : additionalDefines) {
|
||||
result.defines += define;
|
||||
result.defines += "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/util/optional.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class ProgramParameters {
|
||||
public:
|
||||
ProgramParameters(float pixelRatio, bool overdraw, std::string cacheDir);
|
||||
ProgramParameters(float pixelRatio, bool overdraw, optional<std::string> cacheDir);
|
||||
|
||||
const std::string defines;
|
||||
const std::string& getDefines() const;
|
||||
optional<std::string> cachePath(const char* name) const;
|
||||
|
||||
std::string cachePath(const char* name) const;
|
||||
ProgramParameters withAdditionalDefines(const std::vector<std::string>& defines) const;
|
||||
|
||||
private:
|
||||
const std::size_t hash;
|
||||
const std::string cacheDir;
|
||||
std::string defines;
|
||||
optional<std::string> cacheDir;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -35,21 +35,21 @@ public:
|
||||
collisionBox(context, programParameters) {
|
||||
}
|
||||
|
||||
CircleProgram circle;
|
||||
ProgramMap<CircleProgram> circle;
|
||||
ExtrusionTextureProgram extrusionTexture;
|
||||
FillProgram fill;
|
||||
FillExtrusionProgram fillExtrusion;
|
||||
FillExtrusionPatternProgram fillExtrusionPattern;
|
||||
FillPatternProgram fillPattern;
|
||||
FillOutlineProgram fillOutline;
|
||||
FillOutlinePatternProgram fillOutlinePattern;
|
||||
LineProgram line;
|
||||
LineSDFProgram lineSDF;
|
||||
LinePatternProgram linePattern;
|
||||
ProgramMap<FillProgram> fill;
|
||||
ProgramMap<FillExtrusionProgram> fillExtrusion;
|
||||
ProgramMap<FillExtrusionPatternProgram> fillExtrusionPattern;
|
||||
ProgramMap<FillPatternProgram> fillPattern;
|
||||
ProgramMap<FillOutlineProgram> fillOutline;
|
||||
ProgramMap<FillOutlinePatternProgram> fillOutlinePattern;
|
||||
ProgramMap<LineProgram> line;
|
||||
ProgramMap<LineSDFProgram> lineSDF;
|
||||
ProgramMap<LinePatternProgram> linePattern;
|
||||
RasterProgram raster;
|
||||
SymbolIconProgram symbolIcon;
|
||||
SymbolSDFIconProgram symbolIconSDF;
|
||||
SymbolSDFTextProgram symbolGlyph;
|
||||
ProgramMap<SymbolIconProgram> symbolIcon;
|
||||
ProgramMap<SymbolSDFIconProgram> symbolIconSDF;
|
||||
ProgramMap<SymbolSDFTextProgram> symbolGlyph;
|
||||
|
||||
DebugProgram debug;
|
||||
CollisionBoxProgram collisionBox;
|
||||
|
||||
@@ -324,7 +324,7 @@ template <class Shaders,
|
||||
class Primitive,
|
||||
class LayoutAttrs,
|
||||
class Uniforms,
|
||||
class PaintProperties>
|
||||
class PaintProps>
|
||||
class SymbolProgram {
|
||||
public:
|
||||
using LayoutAttributes = LayoutAttrs;
|
||||
@@ -332,6 +332,7 @@ public:
|
||||
|
||||
using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolSizeAttributes>;
|
||||
|
||||
using PaintProperties = PaintProps;
|
||||
using PaintPropertyBinders = typename PaintProperties::Binders;
|
||||
using PaintAttributes = typename PaintPropertyBinders::Attributes;
|
||||
using Attributes = gl::ConcatenateAttributes<LayoutAndSizeAttributes, PaintAttributes>;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <mbgl/util/type_list.hpp>
|
||||
#include <mbgl/renderer/paint_property_statistics.hpp>
|
||||
|
||||
#include <bitset>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
/*
|
||||
@@ -54,8 +56,7 @@ std::array<float, N*2> zoomInterpolatedAttributeValue(const std::array<float, N>
|
||||
|
||||
* For _constant_ properties -- those whose value is a constant, or the constant
|
||||
result of evaluating a camera function at a particular camera position -- we
|
||||
don't need a vertex buffer, and can instead use a constant attribute binding
|
||||
via the `glVertexAttrib*` family of functions.
|
||||
don't need a vertex buffer, and instead use a uniform.
|
||||
* For source functions, we use a vertex buffer with a single attribute value,
|
||||
the evaluated result of the source function for the given feature.
|
||||
* For composite functions, we use a vertex buffer with two attributes: min and
|
||||
@@ -66,15 +67,8 @@ std::array<float, N*2> zoomInterpolatedAttributeValue(const std::array<float, N>
|
||||
between the min and max value at the final displayed zoom level. The use of a
|
||||
uniform allows us to cheaply update the value on every frame.
|
||||
|
||||
Note that the shader source is the same regardless of the strategy used to bind
|
||||
the attribute -- in all cases the attribute is declared as a vec2, in order to
|
||||
support composite min and max values (color attributes use a vec4 with special
|
||||
packing). When the constant or source function strategies are used, the
|
||||
interpolation uniform value is set to zero, and the second attribute element is
|
||||
unused. This differs from the GL JS implementation, which dynamically generates
|
||||
shader source based on the strategy used. We found that in WebGL, using
|
||||
`glVertexAttrib*` was unnacceptably slow. Additionally, in GL Native we have
|
||||
implemented binary shader caching, which works better if the shaders are constant.
|
||||
Note that the shader source varies depending on whether we're using a uniform or
|
||||
attribute. Like GL JS, we dynamically compile shaders at runtime to accomodate this.
|
||||
*/
|
||||
template <class T, class A>
|
||||
class PaintPropertyBinder {
|
||||
@@ -170,9 +164,13 @@ public:
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
T uniformValue(const PossiblyEvaluatedPropertyValue<T>&) const override {
|
||||
// Uniform values for vertex attribute arrays are unused.
|
||||
return {};
|
||||
T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
|
||||
if (currentValue.isConstant()) {
|
||||
return *currentValue.constant();
|
||||
} else {
|
||||
// Uniform values for vertex attribute arrays are unused.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -230,9 +228,13 @@ public:
|
||||
return util::interpolationFactor(1.0f, std::get<0>(coveringRanges), currentZoom);
|
||||
}
|
||||
|
||||
T uniformValue(const PossiblyEvaluatedPropertyValue<T>&) const override {
|
||||
// Uniform values for vertex attribute arrays are unused.
|
||||
return {};
|
||||
T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
|
||||
if (currentValue.isConstant()) {
|
||||
return *currentValue.constant();
|
||||
} else {
|
||||
// Uniform values for vertex attribute arrays are unused.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -342,6 +344,30 @@ public:
|
||||
return binders.template get<P>()->statistics;
|
||||
}
|
||||
|
||||
|
||||
using Bitset = std::bitset<sizeof...(Ps)>;
|
||||
|
||||
template <class EvaluatedProperties>
|
||||
static Bitset constants(const EvaluatedProperties& currentProperties) {
|
||||
Bitset result;
|
||||
util::ignore({
|
||||
result.set(TypeIndex<Ps, Ps...>::value,
|
||||
currentProperties.template get<Ps>().isConstant())...
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class EvaluatedProperties>
|
||||
static std::vector<std::string> defines(const EvaluatedProperties& currentProperties) {
|
||||
std::vector<std::string> result;
|
||||
util::ignore({
|
||||
(result.push_back(currentProperties.template get<Ps>().isConstant()
|
||||
? std::string("#define HAS_UNIFORM_") + Ps::Uniform::name()
|
||||
: std::string()), 0)...
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
Binders binders;
|
||||
};
|
||||
|
||||
@@ -93,7 +93,7 @@ static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices()
|
||||
Painter::Painter(gl::Context& context_,
|
||||
const TransformState& state_,
|
||||
float pixelRatio,
|
||||
const std::string& programCacheDir)
|
||||
const optional<std::string>& programCacheDir)
|
||||
: context(context_),
|
||||
state(state_),
|
||||
tileVertexBuffer(context.createVertexBuffer(tileVertices())),
|
||||
|
||||
@@ -74,7 +74,7 @@ struct FrameData {
|
||||
|
||||
class Painter : private util::noncopyable {
|
||||
public:
|
||||
Painter(gl::Context&, const TransformState&, float pixelRatio, const std::string& programCacheDir);
|
||||
Painter(gl::Context&, const TransformState&, float pixelRatio, const optional<std::string>& programCacheDir);
|
||||
~Painter();
|
||||
|
||||
void render(const style::Style&,
|
||||
|
||||
@@ -33,7 +33,7 @@ void Painter::renderBackground(PaintParameters& parameters, const RenderBackgrou
|
||||
spriteAtlas->bind(true, context, 0);
|
||||
|
||||
for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) {
|
||||
parameters.programs.fillPattern.draw(
|
||||
parameters.programs.fillPattern.get(properties).draw(
|
||||
context,
|
||||
gl::Triangles(),
|
||||
depthModeForSublayer(0, gl::DepthMode::ReadOnly),
|
||||
@@ -58,7 +58,7 @@ void Painter::renderBackground(PaintParameters& parameters, const RenderBackgrou
|
||||
}
|
||||
} else {
|
||||
for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) {
|
||||
parameters.programs.fill.draw(
|
||||
parameters.programs.fill.get(properties).draw(
|
||||
context,
|
||||
gl::Triangles(),
|
||||
depthModeForSublayer(0, gl::DepthMode::ReadOnly),
|
||||
|
||||
@@ -23,7 +23,7 @@ void Painter::renderCircle(PaintParameters& parameters,
|
||||
const CirclePaintProperties::Evaluated& properties = layer.evaluated;
|
||||
const bool scaleWithMap = properties.get<CirclePitchScale>() == CirclePitchScaleType::Map;
|
||||
|
||||
parameters.programs.circle.draw(
|
||||
parameters.programs.circle.get(properties).draw(
|
||||
context,
|
||||
gl::Triangles(),
|
||||
depthModeForSublayer(0, gl::DepthMode::ReadOnly),
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace mbgl {
|
||||
void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& clip) {
|
||||
static const style::FillPaintProperties::Evaluated properties {};
|
||||
static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
|
||||
programs->fill.draw(
|
||||
programs->fill.get(properties).draw(
|
||||
context,
|
||||
gl::Triangles(),
|
||||
gl::DepthMode::disabled(),
|
||||
|
||||
@@ -38,7 +38,7 @@ void Painter::renderFill(PaintParameters& parameters,
|
||||
const auto& drawMode,
|
||||
const auto& indexBuffer,
|
||||
const auto& segments) {
|
||||
program.draw(
|
||||
program.get(properties).draw(
|
||||
context,
|
||||
drawMode,
|
||||
depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite),
|
||||
@@ -85,7 +85,7 @@ void Painter::renderFill(PaintParameters& parameters,
|
||||
const auto& drawMode,
|
||||
const auto& indexBuffer,
|
||||
const auto& segments) {
|
||||
program.draw(
|
||||
program.get(properties).draw(
|
||||
context,
|
||||
drawMode,
|
||||
depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite),
|
||||
|
||||
@@ -36,7 +36,7 @@ void Painter::renderFillExtrusion(PaintParameters& parameters,
|
||||
|
||||
spriteAtlas->bind(true, context, 0);
|
||||
|
||||
parameters.programs.fillExtrusionPattern.draw(
|
||||
parameters.programs.fillExtrusionPattern.get(properties).draw(
|
||||
context,
|
||||
gl::Triangles(),
|
||||
depthModeForSublayer(0, gl::DepthMode::ReadWrite),
|
||||
@@ -62,7 +62,7 @@ void Painter::renderFillExtrusion(PaintParameters& parameters,
|
||||
state.getZoom());
|
||||
|
||||
} else {
|
||||
parameters.programs.fillExtrusion.draw(
|
||||
parameters.programs.fillExtrusion.get(properties).draw(
|
||||
context,
|
||||
gl::Triangles(),
|
||||
depthModeForSublayer(0, gl::DepthMode::ReadWrite),
|
||||
|
||||
@@ -24,7 +24,7 @@ void Painter::renderLine(PaintParameters& parameters,
|
||||
const LinePaintProperties::Evaluated& properties = layer.evaluated;
|
||||
|
||||
auto draw = [&] (auto& program, auto&& uniformValues) {
|
||||
program.draw(
|
||||
program.get(properties).draw(
|
||||
context,
|
||||
gl::Triangles(),
|
||||
depthModeForSublayer(0, gl::DepthMode::ReadOnly),
|
||||
|
||||
@@ -41,7 +41,7 @@ void Painter::renderSymbol(PaintParameters& parameters,
|
||||
// We clip symbols to their tile extent in still mode.
|
||||
const bool needsClipping = frame.mapMode == MapMode::Still;
|
||||
|
||||
program.draw(
|
||||
program.get(paintProperties).draw(
|
||||
context,
|
||||
gl::Triangles(),
|
||||
values_.pitchAlignment == AlignmentType::Map
|
||||
|
||||
@@ -10,11 +10,11 @@ namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
std::string fragmentSource(const ProgramParameters& parameters, const char* fragmentSource) {
|
||||
return parameters.defines + fragmentPrelude + fragmentSource;
|
||||
return parameters.getDefines() + fragmentPrelude + fragmentSource;
|
||||
}
|
||||
|
||||
std::string vertexSource(const ProgramParameters& parameters, const char* vertexSource) {
|
||||
return parameters.defines + vertexPrelude + vertexSource;
|
||||
return parameters.getDefines() + vertexPrelude + vertexSource;
|
||||
}
|
||||
|
||||
std::string programIdentifier(const std::string& vertexSource, const std::string& fragmentSource) {
|
||||
|
||||
Reference in New Issue
Block a user