From d618d4a274023257988a91e96ecf794338189bf8 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 5 Jun 2017 12:15:27 -0700 Subject: [core] Dynamic program compilation for data-driven properties --- include/mbgl/map/map.hpp | 2 +- src/mbgl/gl/program.hpp | 11 +++-- src/mbgl/map/map.cpp | 8 ++-- src/mbgl/programs/program.hpp | 35 +++++++++++++++- src/mbgl/programs/program_parameters.cpp | 30 ++++++++++---- src/mbgl/programs/program_parameters.hpp | 14 ++++--- src/mbgl/programs/programs.hpp | 26 ++++++------ src/mbgl/programs/symbol_program.hpp | 3 +- src/mbgl/renderer/paint_property_binder.hpp | 60 ++++++++++++++++++++-------- src/mbgl/renderer/painter.cpp | 2 +- src/mbgl/renderer/painter.hpp | 2 +- src/mbgl/renderer/painter_background.cpp | 4 +- src/mbgl/renderer/painter_circle.cpp | 2 +- src/mbgl/renderer/painter_clipping.cpp | 2 +- src/mbgl/renderer/painter_fill.cpp | 4 +- src/mbgl/renderer/painter_fill_extrusion.cpp | 4 +- src/mbgl/renderer/painter_line.cpp | 2 +- src/mbgl/renderer/painter_symbol.cpp | 2 +- src/mbgl/shaders/shaders.cpp | 4 +- 19 files changed, 148 insertions(+), 69 deletions(-) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 8072eda7dd..bbeeeac6cc 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -44,7 +44,7 @@ public: GLContextMode contextMode = GLContextMode::Unique, ConstrainMode constrainMode = ConstrainMode::HeightOnly, ViewportMode viewportMode = ViewportMode::Default, - const std::string& programCacheDir = ""); + const optional& programCacheDir = {}); ~Map(); // Register a callback that will get called (on the render thread) when all resources have diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index c36543839c..47ad39de7c 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -50,7 +50,8 @@ public: const char* vertexSource_, const char* fragmentSource_) { #if MBGL_HAS_BINARY_PROGRAMS - if (!programParameters.cacheDir.empty() && context.supportsProgramBinaries()) { + optional 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(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()); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 181370dc14..35457f3a5b 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -58,7 +58,7 @@ public: GLContextMode, ConstrainMode, ViewportMode, - const std::string& programCacheDir); + optional 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 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& programCacheDir) : impl(std::make_unique(*this, backend, pixelRatio, @@ -139,7 +139,7 @@ Map::Impl::Impl(Map& map_, GLContextMode contextMode_, ConstrainMode constrainMode_, ViewportMode viewportMode_, - const std::string& programCacheDir_) + optional programCacheDir_) : map(map_), observer(backend_), backend(backend_), diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp index 420d2e1397..bbe4885745 100644 --- a/src/mbgl/programs/program.hpp +++ b/src/mbgl/programs/program.hpp @@ -9,18 +9,21 @@ #include #include +#include + namespace mbgl { template + 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; @@ -71,4 +74,34 @@ public: } }; +template +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 programs; +}; + } // namespace mbgl diff --git a/src/mbgl/programs/program_parameters.cpp b/src/mbgl/programs/program_parameters.cpp index f9f680ac1e..e76ec4be71 100644 --- a/src/mbgl/programs/program_parameters.cpp +++ b/src/mbgl/programs/program_parameters.cpp @@ -7,7 +7,7 @@ namespace mbgl { ProgramParameters::ProgramParameters(const float pixelRatio, const bool overdraw, - std::string cacheDir_) + optional 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()(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 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()(defines) << ".pbf"; + return ss.str(); + } +} + +ProgramParameters ProgramParameters::withAdditionalDefines(const std::vector& additionalDefines) const { + ProgramParameters result(*this); + for (const auto& define : additionalDefines) { + result.defines += define; + result.defines += "\n"; + } + return result; } } // namespace mbgl diff --git a/src/mbgl/programs/program_parameters.hpp b/src/mbgl/programs/program_parameters.hpp index 420658d089..e94e61c217 100644 --- a/src/mbgl/programs/program_parameters.hpp +++ b/src/mbgl/programs/program_parameters.hpp @@ -1,20 +1,24 @@ #pragma once +#include + #include +#include namespace mbgl { class ProgramParameters { public: - ProgramParameters(float pixelRatio, bool overdraw, std::string cacheDir); + ProgramParameters(float pixelRatio, bool overdraw, optional cacheDir); - const std::string defines; + const std::string& getDefines() const; + optional cachePath(const char* name) const; - std::string cachePath(const char* name) const; + ProgramParameters withAdditionalDefines(const std::vector& defines) const; private: - const std::size_t hash; - const std::string cacheDir; + std::string defines; + optional cacheDir; }; } // namespace mbgl diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index b58d2e3bee..37ced32745 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -35,21 +35,21 @@ public: collisionBox(context, programParameters) { } - CircleProgram circle; + ProgramMap circle; ExtrusionTextureProgram extrusionTexture; - FillProgram fill; - FillExtrusionProgram fillExtrusion; - FillExtrusionPatternProgram fillExtrusionPattern; - FillPatternProgram fillPattern; - FillOutlineProgram fillOutline; - FillOutlinePatternProgram fillOutlinePattern; - LineProgram line; - LineSDFProgram lineSDF; - LinePatternProgram linePattern; + ProgramMap fill; + ProgramMap fillExtrusion; + ProgramMap fillExtrusionPattern; + ProgramMap fillPattern; + ProgramMap fillOutline; + ProgramMap fillOutlinePattern; + ProgramMap line; + ProgramMap lineSDF; + ProgramMap linePattern; RasterProgram raster; - SymbolIconProgram symbolIcon; - SymbolSDFIconProgram symbolIconSDF; - SymbolSDFTextProgram symbolGlyph; + ProgramMap symbolIcon; + ProgramMap symbolIconSDF; + ProgramMap symbolGlyph; DebugProgram debug; CollisionBoxProgram collisionBox; diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 1b7232c9e4..01e95f456d 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -324,7 +324,7 @@ template + class PaintProps> class SymbolProgram { public: using LayoutAttributes = LayoutAttrs; @@ -332,6 +332,7 @@ public: using LayoutAndSizeAttributes = gl::ConcatenateAttributes; + using PaintProperties = PaintProps; using PaintPropertyBinders = typename PaintProperties::Binders; using PaintAttributes = typename PaintPropertyBinders::Attributes; using Attributes = gl::ConcatenateAttributes; diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp index a4bea24bbf..062b77888e 100644 --- a/src/mbgl/renderer/paint_property_binder.hpp +++ b/src/mbgl/renderer/paint_property_binder.hpp @@ -6,6 +6,8 @@ #include #include +#include + namespace mbgl { /* @@ -54,8 +56,7 @@ std::array zoomInterpolatedAttributeValue(const std::array * 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 zoomInterpolatedAttributeValue(const std::array 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 PaintPropertyBinder { @@ -170,9 +164,13 @@ public: return 0.0f; } - T uniformValue(const PossiblyEvaluatedPropertyValue&) const override { - // Uniform values for vertex attribute arrays are unused. - return {}; + T uniformValue(const PossiblyEvaluatedPropertyValue& 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&) const override { - // Uniform values for vertex attribute arrays are unused. - return {}; + T uniformValue(const PossiblyEvaluatedPropertyValue& 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

()->statistics; } + + using Bitset = std::bitset; + + template + static Bitset constants(const EvaluatedProperties& currentProperties) { + Bitset result; + util::ignore({ + result.set(TypeIndex::value, + currentProperties.template get().isConstant())... + }); + return result; + } + + template + static std::vector defines(const EvaluatedProperties& currentProperties) { + std::vector result; + util::ignore({ + (result.push_back(currentProperties.template get().isConstant() + ? std::string("#define HAS_UNIFORM_") + Ps::Uniform::name() + : std::string()), 0)... + }); + return result; + } + private: Binders binders; }; diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index eed3bfcd8b..da4903864b 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -93,7 +93,7 @@ static gl::VertexVector extrusionTextureVertices() Painter::Painter(gl::Context& context_, const TransformState& state_, float pixelRatio, - const std::string& programCacheDir) + const optional& programCacheDir) : context(context_), state(state_), tileVertexBuffer(context.createVertexBuffer(tileVertices())), diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 47b469d971..4658f0c206 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -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& programCacheDir); ~Painter(); void render(const style::Style&, diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp index 7cd9cbac5f..d01696ee3e 100644 --- a/src/mbgl/renderer/painter_background.cpp +++ b/src/mbgl/renderer/painter_background.cpp @@ -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), diff --git a/src/mbgl/renderer/painter_circle.cpp b/src/mbgl/renderer/painter_circle.cpp index 7c9194f6dd..13acb5f7fe 100644 --- a/src/mbgl/renderer/painter_circle.cpp +++ b/src/mbgl/renderer/painter_circle.cpp @@ -23,7 +23,7 @@ void Painter::renderCircle(PaintParameters& parameters, const CirclePaintProperties::Evaluated& properties = layer.evaluated; const bool scaleWithMap = properties.get() == CirclePitchScaleType::Map; - parameters.programs.circle.draw( + parameters.programs.circle.get(properties).draw( context, gl::Triangles(), depthModeForSublayer(0, gl::DepthMode::ReadOnly), diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp index 0d3b5f1504..b3a2d77b1a 100644 --- a/src/mbgl/renderer/painter_clipping.cpp +++ b/src/mbgl/renderer/painter_clipping.cpp @@ -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(), diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 622f6386ef..e1b59d8839 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -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), diff --git a/src/mbgl/renderer/painter_fill_extrusion.cpp b/src/mbgl/renderer/painter_fill_extrusion.cpp index af98cae569..95617525c7 100644 --- a/src/mbgl/renderer/painter_fill_extrusion.cpp +++ b/src/mbgl/renderer/painter_fill_extrusion.cpp @@ -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), diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp index 4d7a27d0f6..9152ac8512 100644 --- a/src/mbgl/renderer/painter_line.cpp +++ b/src/mbgl/renderer/painter_line.cpp @@ -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), diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index 5a66d1e62f..86b2146b9f 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -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 diff --git a/src/mbgl/shaders/shaders.cpp b/src/mbgl/shaders/shaders.cpp index 93e273f985..31ff405f02 100644 --- a/src/mbgl/shaders/shaders.cpp +++ b/src/mbgl/shaders/shaders.cpp @@ -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) { -- cgit v1.2.1