From 153d8512b7c0bc5ee80e26c868fb52dd66ee62cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 8 Feb 2018 17:53:05 +0100 Subject: [core] allow disabling the use of extensions via GLContextMode --- include/mbgl/renderer/mode.hpp | 47 ++++++++++++++++--- include/mbgl/renderer/renderer.hpp | 2 +- include/mbgl/renderer/renderer_backend.hpp | 4 +- platform/darwin/src/MGLRendererConfiguration.h | 2 +- platform/default/mbgl/gl/headless_backend.cpp | 4 +- platform/default/mbgl/gl/headless_backend.hpp | 2 +- platform/default/mbgl/gl/headless_frontend.cpp | 30 ++++++++----- platform/default/mbgl/gl/headless_frontend.hpp | 4 +- platform/qt/src/qmapboxgl.cpp | 4 +- src/mbgl/gl/context.cpp | 62 +++++++++++++------------- src/mbgl/gl/context.hpp | 13 ++---- src/mbgl/renderer/renderer_backend.cpp | 7 ++- src/mbgl/renderer/renderer_impl.cpp | 6 +-- test/gl/context.test.cpp | 2 +- test/map/map.test.cpp | 25 ++++++----- test/renderer/backend_scope.test.cpp | 3 ++ test/text/local_glyph_rasterizer.test.cpp | 2 +- 17 files changed, 134 insertions(+), 85 deletions(-) diff --git a/include/mbgl/renderer/mode.hpp b/include/mbgl/renderer/mode.hpp index 6ff42d8058..c5883b8b82 100644 --- a/include/mbgl/renderer/mode.hpp +++ b/include/mbgl/renderer/mode.hpp @@ -1,18 +1,51 @@ #pragma once -#include +#include +#include namespace mbgl { using EnumType = uint32_t; -// We can avoid redundant GL calls when it is known that the GL context is not -// being shared. In a shared GL context case, we need to make sure that the -// correct GL configurations are in use - they might have changed between render -// calls. enum class GLContextMode : EnumType { - Unique, - Shared, + Automatic = 0, + + // We can avoid redundant GL calls when it is known that the GL context is not being shared. In + // a shared GL context case, we need to make sure that the correct GL configurations are in use + // - they might have changed between render calls. If you're issuing other OpenGL calls in the + // same context (excluding uses of custom layers), then initialize the context with this flag set. + SharedState = 0b00000001, + + // Vertex Array Objects bundle buffer state for faster binding and is available on most modern + // platforms. However, some older GPUs have bugs with this, and some devices don't support it + // altogether. Setting this flag allows testing without Vertex Array Objects. + DisableVAOExtension = 0b00000010, + + // Some platforms support exporting compiled shader programs as binaries. This allows us to + // store them and reload the precompiled binary on subsequent loads, which typically speeds + // up initialization. However, some GPUs have trouble reloading binaries due driver bugs. + // You can explicitly disable program binaries by setting this flag. + DisableProgramBinariesExtension = 000000100, }; +MBGL_CONSTEXPR GLContextMode operator|(GLContextMode lhs, GLContextMode rhs) { + return GLContextMode(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs)); +} + +MBGL_CONSTEXPR GLContextMode& operator|=(GLContextMode& lhs, GLContextMode rhs) { + return (lhs = GLContextMode(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs))); +} + +MBGL_CONSTEXPR bool operator&(GLContextMode lhs, GLContextMode rhs) { + return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs); +} + +MBGL_CONSTEXPR GLContextMode& operator&=(GLContextMode& lhs, GLContextMode rhs) { + return (lhs = GLContextMode(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs))); +} + +MBGL_CONSTEXPR GLContextMode operator~(GLContextMode value) { + return GLContextMode(~mbgl::underlying_type(value)); +} + } // namespace mbgl diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp index db28ee92fc..ab7f70bace 100644 --- a/include/mbgl/renderer/renderer.hpp +++ b/include/mbgl/renderer/renderer.hpp @@ -24,7 +24,7 @@ class UpdateParameters; class Renderer { public: Renderer(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, - GLContextMode = GLContextMode::Unique, + GLContextMode = GLContextMode::Automatic, const optional programCacheDir = {}, const optional localFontFamily = {}); ~Renderer(); diff --git a/include/mbgl/renderer/renderer_backend.hpp b/include/mbgl/renderer/renderer_backend.hpp index b83c128169..4a4c41298a 100644 --- a/include/mbgl/renderer/renderer_backend.hpp +++ b/include/mbgl/renderer/renderer_backend.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -20,7 +21,7 @@ using FramebufferID = uint32_t; // the actual rendering. class RendererBackend { public: - RendererBackend(); + RendererBackend(GLContextMode); virtual ~RendererBackend(); // Returns the backend's context which manages OpenGL state. @@ -81,6 +82,7 @@ protected: private: std::once_flag initialized; + const GLContextMode mode; friend class BackendScope; }; diff --git a/platform/darwin/src/MGLRendererConfiguration.h b/platform/darwin/src/MGLRendererConfiguration.h index 31aad0a742..9350f2d5a5 100644 --- a/platform/darwin/src/MGLRendererConfiguration.h +++ b/platform/darwin/src/MGLRendererConfiguration.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN /** The file source to use. Defaults to `mbgl::DefaultFileSource` */ @property (nonatomic, readonly) mbgl::DefaultFileSource *fileSource; -/** The GL context mode to use. Defaults to `mbgl::GLContextMode::Unique` */ +/** The GL context mode to use. Defaults to `mbgl::GLContextMode::Automatic` */ @property (nonatomic, readonly) mbgl::GLContextMode contextMode; /** The scale factor to use. diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp index ba08aecab7..f785865667 100644 --- a/platform/default/mbgl/gl/headless_backend.cpp +++ b/platform/default/mbgl/gl/headless_backend.cpp @@ -21,8 +21,8 @@ public: gl::Framebuffer framebuffer; }; -HeadlessBackend::HeadlessBackend(Size size_) - : size(size_) { +HeadlessBackend::HeadlessBackend(Size size_, GLContextMode mode_) + : RendererBackend(mode_), size(size_) { } HeadlessBackend::~HeadlessBackend() { diff --git a/platform/default/mbgl/gl/headless_backend.hpp b/platform/default/mbgl/gl/headless_backend.hpp index 7757037533..3768fef15c 100644 --- a/platform/default/mbgl/gl/headless_backend.hpp +++ b/platform/default/mbgl/gl/headless_backend.hpp @@ -9,7 +9,7 @@ namespace mbgl { class HeadlessBackend : public RendererBackend { public: - HeadlessBackend(Size = { 256, 256 }); + HeadlessBackend(Size = { 256, 256 }, GLContextMode = GLContextMode::Automatic); ~HeadlessBackend() override; void bind() override; diff --git a/platform/default/mbgl/gl/headless_frontend.cpp b/platform/default/mbgl/gl/headless_frontend.cpp index 4263d2b148..16c477dd00 100644 --- a/platform/default/mbgl/gl/headless_frontend.cpp +++ b/platform/default/mbgl/gl/headless_frontend.cpp @@ -11,18 +11,26 @@ HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Sc : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir, mode, localFontFamily) { } -HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional programCacheDir, GLContextMode mode, const optional localFontFamily) +HeadlessFrontend::HeadlessFrontend(Size size_, + float pixelRatio_, + FileSource& fileSource, + Scheduler& scheduler, + const optional programCacheDir, + GLContextMode mode, + const optional localFontFamily) : size(size_), - pixelRatio(pixelRatio_), - backend({ static_cast(size.width * pixelRatio), - static_cast(size.height * pixelRatio) }), - asyncInvalidate([this] { - if (renderer && updateParameters) { - mbgl::BackendScope guard { backend }; - renderer->render(*updateParameters); - } - }), - renderer(std::make_unique(backend, pixelRatio, fileSource, scheduler, mode, programCacheDir, localFontFamily)) { + pixelRatio(pixelRatio_), + backend({ static_cast(size.width * pixelRatio), + static_cast(size.height * pixelRatio) }, + mode), + asyncInvalidate([this] { + if (renderer && updateParameters) { + mbgl::BackendScope guard{ backend }; + renderer->render(*updateParameters); + } + }), + renderer(std::make_unique( + backend, pixelRatio, fileSource, scheduler, mode, programCacheDir, localFontFamily)) { } HeadlessFrontend::~HeadlessFrontend() = default; diff --git a/platform/default/mbgl/gl/headless_frontend.hpp b/platform/default/mbgl/gl/headless_frontend.hpp index 8ae617d37b..eab922945b 100644 --- a/platform/default/mbgl/gl/headless_frontend.hpp +++ b/platform/default/mbgl/gl/headless_frontend.hpp @@ -19,8 +19,8 @@ class TransformState; class HeadlessFrontend : public RendererFrontend { public: - HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}, GLContextMode mode = GLContextMode::Unique, const optional localFontFamily = {}); - HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}, GLContextMode mode = GLContextMode::Unique, const optional localFontFamily = {}); + HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}, GLContextMode mode = GLContextMode::Automatic, const optional localFontFamily = {}); + HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}, GLContextMode mode = GLContextMode::Automatic, const optional localFontFamily = {}); ~HeadlessFrontend() override; void reset() override; diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 2675d87862..a00c7fef67 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -62,8 +62,8 @@ using namespace QMapbox; // mbgl::GLContextMode -static_assert(mbgl::underlying_type(QMapboxGLSettings::UniqueGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Unique), "error"); -static_assert(mbgl::underlying_type(QMapboxGLSettings::SharedGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Shared), "error"); +static_assert(mbgl::underlying_type(QMapboxGLSettings::UniqueGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Automatic), "error"); +static_assert(mbgl::underlying_type(QMapboxGLSettings::SharedGLContext) == mbgl::underlying_type(mbgl::GLContextMode::SharedState), "error"); // mbgl::ConstrainMode static_assert(mbgl::underlying_type(QMapboxGLSettings::NoConstrain) == mbgl::underlying_type(mbgl::ConstrainMode::None), "error"); diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index b6244d58c7..7e51084d5a 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -92,7 +92,11 @@ Context::~Context() { } } -void Context::initializeExtensions(const std::function& getProcAddress) { +void Context::initializeExtensions( + const std::function& getProcAddress, + bool disableVAOExtension, + bool disableProgramBinariesExtension) { + const std::string renderer = reinterpret_cast(MBGL_CHECK_ERROR(glGetString(GL_RENDERER))); if (const auto* extensions = reinterpret_cast(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)))) { @@ -109,11 +113,32 @@ void Context::initializeExtensions(const std::function(fn); - if (!disableVAOExtension) { + + // Load the Vertex Array Objects extension + // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data + // Qt + ANGLE crashes with VAO enabled. + if (!disableVAOExtension && + !(renderer.find("Adreno (TM) 2") != std::string::npos || + renderer.find("Adreno (TM) 3") != std::string::npos || + renderer.find("ANGLE") != std::string::npos)) { vertexArray = std::make_unique(fn); } + + // Load the Program Binaries extension + (void)disableProgramBinariesExtension; #if MBGL_HAS_BINARY_PROGRAMS - programBinary = std::make_unique(fn); + // Blacklist Adreno 3xx, 4xx, and 5xx GPUs due to known bugs: + // https://bugs.chromium.org/p/chromium/issues/detail?id=510637 + // https://chromium.googlesource.com/chromium/src/gpu/+/master/config/gpu_driver_bug_list.json#2316 + // Blacklist Vivante GC4000 due to bugs when linking loaded programs: + // https://github.com/mapbox/mapbox-gl-native/issues/10704 + if (!disableProgramBinariesExtension && + !(renderer.find("Adreno (TM) 3") != std::string::npos || + renderer.find("Adreno (TM) 4") != std::string::npos || + renderer.find("Adreno (TM) 5") != std::string::npos || + renderer.find("Vivante GC4000") != std::string::npos)) { + programBinary = std::make_unique(fn); + } #endif if (!supportsVertexArrays()) { @@ -257,15 +282,7 @@ UniqueTexture Context::createTexture() { } bool Context::supportsVertexArrays() const { - static bool blacklisted = []() { - // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data - const std::string renderer = reinterpret_cast(glGetString(GL_RENDERER)); - return renderer.find("Adreno (TM) 2") != std::string::npos - || renderer.find("Adreno (TM) 3") != std::string::npos; - }(); - - return !blacklisted && - vertexArray && + return vertexArray && vertexArray->genVertexArrays && vertexArray->bindVertexArray && vertexArray->deleteVertexArrays; @@ -273,24 +290,9 @@ bool Context::supportsVertexArrays() const { #if MBGL_HAS_BINARY_PROGRAMS bool Context::supportsProgramBinaries() const { - if (!programBinary || !programBinary->programBinary || !programBinary->getProgramBinary) { - return false; - } - - // Blacklist Adreno 3xx, 4xx, and 5xx GPUs due to known bugs: - // https://bugs.chromium.org/p/chromium/issues/detail?id=510637 - // https://chromium.googlesource.com/chromium/src/gpu/+/master/config/gpu_driver_bug_list.json#2316 - // Blacklist Vivante GC4000 due to bugs when linking loaded programs: - // https://github.com/mapbox/mapbox-gl-native/issues/10704 - const std::string renderer = reinterpret_cast(glGetString(GL_RENDERER)); - if (renderer.find("Adreno (TM) 3") != std::string::npos - || renderer.find("Adreno (TM) 4") != std::string::npos - || renderer.find("Adreno (TM) 5") != std::string::npos - || renderer.find("Vivante GC4000") != std::string::npos) { - return false; - } - - return true; + return programBinary && + programBinary->programBinary && + programBinary->getProgramBinary; } optional> diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 14f078367f..db7b8e133e 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -41,7 +41,9 @@ public: Context(); ~Context(); - void initializeExtensions(const std::function&); + void initializeExtensions(const std::function&, + bool disableVAOExtension, + bool disableProgramBinariesExtension); void enableDebugging(); @@ -287,15 +289,6 @@ private: std::vector abandonedVertexArrays; std::vector abandonedFramebuffers; std::vector abandonedRenderbuffers; - -public: - // For testing and Windows because Qt + ANGLE - // crashes with VAO enabled. -#if defined(_WINDOWS) - bool disableVAOExtension = true; -#else - bool disableVAOExtension = false; -#endif }; } // namespace gl diff --git a/src/mbgl/renderer/renderer_backend.cpp b/src/mbgl/renderer/renderer_backend.cpp index 22d263313c..1d06cb313b 100644 --- a/src/mbgl/renderer/renderer_backend.cpp +++ b/src/mbgl/renderer/renderer_backend.cpp @@ -8,7 +8,8 @@ namespace mbgl { -RendererBackend::RendererBackend() = default; +RendererBackend::RendererBackend(GLContextMode mode_) : mode(mode_) { +} gl::Context& RendererBackend::getContext() { assert(BackendScope::exists()); @@ -16,7 +17,9 @@ gl::Context& RendererBackend::getContext() { context = std::make_unique(); context->enableDebugging(); context->initializeExtensions( - std::bind(&RendererBackend::getExtensionFunctionPointer, this, std::placeholders::_1)); + std::bind(&RendererBackend::getExtensionFunctionPointer, this, std::placeholders::_1), + mode & GLContextMode::DisableVAOExtension, + mode & GLContextMode::DisableProgramBinariesExtension); }); return *context; } diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 61e7d17242..e3aff4999c 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -272,7 +272,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { backend.updateAssumedState(); - if (parameters.contextMode == GLContextMode::Shared) { + if (parameters.contextMode & GLContextMode::SharedState) { parameters.context.setDirtyState(); } @@ -300,7 +300,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (const RenderBackgroundLayer* background = layer->as()) { const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated; - if (parameters.contextMode == GLContextMode::Unique + if (parameters.contextMode == GLContextMode::Automatic && layerImpl.get() == layerImpls->at(0).get() && paint.get().from.empty()) { // This is a solid background. We can use glClear(). @@ -470,7 +470,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { parameters.backend.bind(); if (parameters.debugOptions & MapDebugOptions::Overdraw) { parameters.context.clear(Color::black(), ClearDepth::Default, ClearStencil::Default); - } else if (parameters.contextMode == GLContextMode::Shared) { + } else if (parameters.contextMode & GLContextMode::SharedState) { parameters.context.clear({}, ClearDepth::Default, ClearStencil::Default); } else { parameters.context.clear(backgroundColor, ClearDepth::Default, ClearStencil::Default); diff --git a/test/gl/context.test.cpp b/test/gl/context.test.cpp index 179ce5de53..920e42bf09 100644 --- a/test/gl/context.test.cpp +++ b/test/gl/context.test.cpp @@ -87,7 +87,7 @@ TEST(GLContextMode, Shared) { ThreadPool threadPool(4); float pixelRatio { 1 }; - HeadlessFrontend frontend { pixelRatio, fileSource, threadPool, {}, GLContextMode::Shared }; + HeadlessFrontend frontend { pixelRatio, fileSource, threadPool, {}, GLContextMode::SharedState }; Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Static); map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 9b34ea89b0..f2bc563a5b 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -72,18 +72,23 @@ public: HeadlessFrontend frontend; Map map; - MapTest(float pixelRatio = 1, MapMode mode = MapMode::Static) - : frontend(pixelRatio, fileSource, threadPool) - , map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, mode) { + MapTest(float pixelRatio = 1, + MapMode mapMode = MapMode::Static, + GLContextMode contextMode = GLContextMode::Automatic) + : frontend(pixelRatio, fileSource, threadPool, {}, contextMode), + map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, mapMode) { } template - MapTest(const std::string& cachePath, const std::string& assetRoot, - float pixelRatio = 1, MapMode mode = MapMode::Static, + MapTest(const std::string& cachePath, + const std::string& assetRoot, + float pixelRatio = 1, + MapMode mapMode = MapMode::Static, + GLContextMode contextMode = GLContextMode::Automatic, typename std::enable_if::value>::type* = 0) - : fileSource { cachePath, assetRoot } - , frontend(pixelRatio, fileSource, threadPool) - , map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, mode) { + : fileSource{ cachePath, assetRoot }, + frontend(pixelRatio, fileSource, threadPool, {}, contextMode), + map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, mapMode) { } }; @@ -447,10 +452,10 @@ TEST(Map, AddLayer) { } TEST(Map, WithoutVAOExtension) { - MapTest test { ":memory:", "test/fixtures/api/assets" }; + MapTest test{ ":memory:", "test/fixtures/api/assets", 1, MapMode::Static, + GLContextMode::DisableVAOExtension }; BackendScope scope { *test.frontend.getBackend() }; - test.frontend.getBackend()->getContext().disableVAOExtension = true; test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); diff --git a/test/renderer/backend_scope.test.cpp b/test/renderer/backend_scope.test.cpp index 05b82695b2..7bb925d1a9 100644 --- a/test/renderer/backend_scope.test.cpp +++ b/test/renderer/backend_scope.test.cpp @@ -9,6 +9,9 @@ using namespace mbgl; class StubRendererBackend: public RendererBackend { public: + StubRendererBackend() : RendererBackend(GLContextMode::Automatic) { + } + void bind() override { } diff --git a/test/text/local_glyph_rasterizer.test.cpp b/test/text/local_glyph_rasterizer.test.cpp index 84c685d66f..85aa255220 100644 --- a/test/text/local_glyph_rasterizer.test.cpp +++ b/test/text/local_glyph_rasterizer.test.cpp @@ -32,7 +32,7 @@ namespace { class LocalGlyphRasterizerTest { public: LocalGlyphRasterizerTest(const optional fontFamily) - : frontend(pixelRatio, fileSource, threadPool, optional(), GLContextMode::Unique, fontFamily) + : frontend(pixelRatio, fileSource, threadPool, optional(), GLContextMode::Automatic, fontFamily) { } -- cgit v1.2.1