From db9403715d608d4e7a785ac34d3b914fdf34e0a1 Mon Sep 17 00:00:00 2001 From: Alex Andrewschenko Date: Tue, 19 Nov 2024 15:24:06 +0100 Subject: [PATCH 1/5] Several optimizations for Android Auto --- .../OsmAndCore/Map/AtlasMapRenderer_Metrics.h | 3 - include/OsmAndCore/Map/IMapRenderer.h | 7 +- include/OsmAndCore/Map/MapRendererState.h | 3 + src/Map/AtlasMapRenderer.cpp | 30 +- src/Map/AtlasMapRenderer.h | 3 +- src/Map/GPUAPI.cpp | 33 - src/Map/GPUAPI.h | 5 - src/Map/MapRenderer.cpp | 157 +- src/Map/MapRenderer.h | 11 +- src/Map/MapRendererState.cpp | 1 + .../AtlasMapRendererDebugStage_OpenGL.cpp | 648 ++++--- .../AtlasMapRendererDebugStage_OpenGL.h | 15 + .../AtlasMapRendererMapLayersStage_OpenGL.cpp | 1642 ++++++++--------- .../AtlasMapRendererMapLayersStage_OpenGL.h | 4 +- .../AtlasMapRendererSkyStage_OpenGL.cpp | 168 +- .../OpenGL/AtlasMapRendererSkyStage_OpenGL.h | 3 + ...sMapRendererSymbolsStageModel3D_OpenGL.cpp | 182 +- ...lasMapRendererSymbolsStageModel3D_OpenGL.h | 7 +- .../AtlasMapRendererSymbolsStage_OpenGL.cpp | 1350 +++++++------- .../AtlasMapRendererSymbolsStage_OpenGL.h | 18 + src/Map/OpenGL/AtlasMapRenderer_OpenGL.cpp | 42 +- src/Map/OpenGL/AtlasMapRenderer_OpenGL.h | 7 +- src/Map/OpenGL/GPUAPI_OpenGL.cpp | 79 +- src/Map/OpenGL/GPUAPI_OpenGL.h | 18 +- .../OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.cpp | 136 +- .../OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.h | 15 +- .../OpenGLES2plus/GPUAPI_OpenGLES2plus.cpp | 695 +------ .../OpenGLES2plus/GPUAPI_OpenGLES2plus.h | 39 +- .../osmand/core/android/MapRendererView.java | 650 ++++--- 29 files changed, 2781 insertions(+), 3190 deletions(-) diff --git a/include/OsmAndCore/Map/AtlasMapRenderer_Metrics.h b/include/OsmAndCore/Map/AtlasMapRenderer_Metrics.h index 930db489e..016c7641c 100644 --- a/include/OsmAndCore/Map/AtlasMapRenderer_Metrics.h +++ b/include/OsmAndCore/Map/AtlasMapRenderer_Metrics.h @@ -23,9 +23,6 @@ namespace OsmAnd /* Time elapsed for map layers stage */ \ FIELD_ACTION(float, elapsedTimeForMapLayersStage, "s"); \ \ - /* Time elapsed for capturing terrain depth buffer */ \ - FIELD_ACTION(float, elapsedTimeForTerrainDepthBufferCapture, "s"); \ - \ /* Time elapsed for symbols stage */ \ FIELD_ACTION(float, elapsedTimeForSymbolsStage, "s"); \ FIELD_ACTION(float, elapsedTimeForPreparingSymbols, "s"); \ diff --git a/include/OsmAndCore/Map/IMapRenderer.h b/include/OsmAndCore/Map/IMapRenderer.h index c7b53149d..20f8b5779 100644 --- a/include/OsmAndCore/Map/IMapRenderer.h +++ b/include/OsmAndCore/Map/IMapRenderer.h @@ -62,16 +62,12 @@ namespace OsmAnd virtual void setConfiguration(const std::shared_ptr& configuration, bool forcedUpdate = false) = 0; virtual bool isRenderingInitialized() const = 0; - virtual bool initializeRendering(bool renderTargetAvailable) = 0; + virtual bool initializeRendering(bool fresh = true) = 0; virtual bool update(IMapRenderer_Metrics::Metric_update* const metric = nullptr) = 0; virtual bool prepareFrame(IMapRenderer_Metrics::Metric_prepareFrame* const metric = nullptr) = 0; virtual bool renderFrame(IMapRenderer_Metrics::Metric_renderFrame* const metric = nullptr) = 0; virtual bool releaseRendering(bool gpuContextLost = false) = 0; - virtual bool attachToRenderTarget() = 0; - virtual bool isAttachedToRenderTarget() = 0; - virtual bool detachFromRenderTarget() = 0; - virtual bool isIdle() const = 0; virtual QString getNotIdleReason() const = 0; @@ -135,6 +131,7 @@ namespace OsmAnd virtual bool setWindowSize(const PointI& windowSize, bool forcedUpdate = false) = 0; virtual bool setViewport(const AreaI& viewport, bool forcedUpdate = false) = 0; + virtual bool setFlip(bool flip, bool forcedUpdate = false) = 0; virtual bool setFieldOfView(const float fieldOfView, bool forcedUpdate = false) = 0; virtual bool setVisibleDistance(const float visibleDistance, bool forcedUpdate = false) = 0; virtual bool setDetailedDistance(const float detailedDistance, bool forcedUpdate = false) = 0; diff --git a/include/OsmAndCore/Map/MapRendererState.h b/include/OsmAndCore/Map/MapRendererState.h index 98ebcdb16..710471585 100644 --- a/include/OsmAndCore/Map/MapRendererState.h +++ b/include/OsmAndCore/Map/MapRendererState.h @@ -41,6 +41,7 @@ namespace OsmAnd Symbols_Configuration, WindowSize, Viewport, + Flip, FieldOfView, VisibleDistance, DetailedDistance, @@ -75,6 +76,7 @@ namespace OsmAnd PointI windowSize; AreaI viewport; + bool flip; float fieldOfView; float visibleDistance; float detailedDistance; @@ -134,6 +136,7 @@ namespace OsmAnd PointI windowSize; AreaI viewport; + bool flip; float fieldOfView; float visibleDistance; float detailedDistance; diff --git a/src/Map/AtlasMapRenderer.cpp b/src/Map/AtlasMapRenderer.cpp index 2c7431c5d..6d9beb347 100644 --- a/src/Map/AtlasMapRenderer.cpp +++ b/src/Map/AtlasMapRenderer.cpp @@ -171,9 +171,9 @@ bool OsmAnd::AtlasMapRenderer::preInitializeRendering() return true; } -bool OsmAnd::AtlasMapRenderer::doInitializeRendering() +bool OsmAnd::AtlasMapRenderer::doInitializeRendering(bool reinitialize) { - bool ok = MapRenderer::doInitializeRendering(); + bool ok = MapRenderer::doInitializeRendering(reinitialize); if (!ok) return false; @@ -200,28 +200,24 @@ bool OsmAnd::AtlasMapRenderer::doReleaseRendering(bool gpuContextLost) { if (!_skyStage->release(gpuContextLost)) ok = false; - _skyStage.reset(); } if (_mapLayersStage) { if (!_mapLayersStage->release(gpuContextLost)) ok = false; - _mapLayersStage.reset(); } if (_symbolsStage) { if (!_symbolsStage->release(gpuContextLost)) ok = false; - _symbolsStage.reset(); } if (_debugStage) { if (!_debugStage->release(gpuContextLost)) ok = false; - _debugStage.reset(); } if (!MapRenderer::doReleaseRendering(gpuContextLost)) @@ -230,6 +226,28 @@ bool OsmAnd::AtlasMapRenderer::doReleaseRendering(bool gpuContextLost) return ok; } +bool OsmAnd::AtlasMapRenderer::postReleaseRendering(bool gpuContextLost) +{ + bool ok = true; + + if (_skyStage) + _skyStage.reset(); + + if (_mapLayersStage) + _mapLayersStage.reset(); + + if (_symbolsStage) + _symbolsStage.reset(); + + if (_debugStage) + _debugStage.reset(); + + if (!MapRenderer::postReleaseRendering(gpuContextLost)) + ok = false; + + return ok; +} + int OsmAnd::AtlasMapRenderer::getTileSize3D() const { return TileSize3D; diff --git a/src/Map/AtlasMapRenderer.h b/src/Map/AtlasMapRenderer.h index 7413f0f6b..21e13a189 100644 --- a/src/Map/AtlasMapRenderer.h +++ b/src/Map/AtlasMapRenderer.h @@ -64,12 +64,13 @@ namespace OsmAnd // Customization points: bool preInitializeRendering() override; - bool doInitializeRendering() override; + bool doInitializeRendering(bool reinitialize) override; bool prePrepareFrame() override; bool postPrepareFrame() override; bool doReleaseRendering(bool gpuContextLost) override; + bool postReleaseRendering(bool gpuContextLost) override; // Stages: std::shared_ptr _skyStage; diff --git a/src/Map/GPUAPI.cpp b/src/Map/GPUAPI.cpp index 5fb464cfa..44f1e349d 100644 --- a/src/Map/GPUAPI.cpp +++ b/src/Map/GPUAPI.cpp @@ -5,7 +5,6 @@ #include "Logging.h" OsmAnd::GPUAPI::GPUAPI() - : _isAttachedToRenderTarget(false) { } @@ -26,11 +25,6 @@ OsmAnd::GPUAPI::~GPUAPI() assert(resourcesRemaining == 0); } -bool OsmAnd::GPUAPI::isAttachedToRenderTarget() -{ - return _isAttachedToRenderTarget; -} - bool OsmAnd::GPUAPI::initialize() { return true; @@ -46,35 +40,8 @@ bool OsmAnd::GPUAPI::elementIsVisible(int queryIndex) return true; } -bool OsmAnd::GPUAPI::attachToRenderTarget() -{ - _isAttachedToRenderTarget = true; - return true; -} - -bool OsmAnd::GPUAPI::detachFromRenderTarget(bool gpuContextLost) -{ - if (!isAttachedToRenderTarget()) - { - return false; - } - - _isAttachedToRenderTarget = false; - - return true; -} - bool OsmAnd::GPUAPI::release(bool gpuContextLost) { - bool ok; - - if (isAttachedToRenderTarget()) - { - ok = detachFromRenderTarget(gpuContextLost); - if (!ok) - return false; - } - return true; } diff --git a/src/Map/GPUAPI.h b/src/Map/GPUAPI.h index dee7f7548..44dac6047 100644 --- a/src/Map/GPUAPI.h +++ b/src/Map/GPUAPI.h @@ -295,7 +295,6 @@ namespace OsmAnd QAtomicInt _allocatedResourcesCounter; #endif - bool _isAttachedToRenderTarget; QHash< AtlasTypeId, std::shared_ptr > _atlasTexturesPools; protected: GPUAPI(); @@ -315,13 +314,9 @@ namespace OsmAnd public: virtual ~GPUAPI(); - virtual bool isAttachedToRenderTarget(); - virtual bool initialize() = 0; virtual int checkElementVisibility(int queryIndex, float pointSize) = 0; virtual bool elementIsVisible(int queryIndex) = 0; - virtual bool attachToRenderTarget() = 0; - virtual bool detachFromRenderTarget(bool gpuContextLost) = 0; virtual bool release(bool gpuContextLost) = 0; virtual bool uploadTiledDataToGPU( diff --git a/src/Map/MapRenderer.cpp b/src/Map/MapRenderer.cpp index 63507f80d..1b8b1f304 100644 --- a/src/Map/MapRenderer.cpp +++ b/src/Map/MapRenderer.cpp @@ -97,6 +97,8 @@ bool OsmAnd::MapRenderer::isInRenderThread() const OsmAnd::MapRendererSetupOptions OsmAnd::MapRenderer::getSetupOptions() const { + QReadLocker scopedLocker(&_setupOptionsLock); + return _setupOptions; } @@ -104,9 +106,13 @@ bool OsmAnd::MapRenderer::setup(const MapRendererSetupOptions& setupOptions) { // We can not change setup options renderer once rendering has been initialized if (_isRenderingInitialized) - return false; + { + QWriteLocker scopedLocker(&_setupOptionsLock); - _setupOptions = setupOptions; + _setupOptions = setupOptions; + } + else + _setupOptions = setupOptions; return true; } @@ -213,6 +219,8 @@ void OsmAnd::MapRenderer::invalidateFrame() // Increment frame-invalidated counter by 1 _frameInvalidatesCounter.fetchAndAddOrdered(1); + QReadLocker scopedLocker(&_setupOptionsLock); + // Request frame, if such callback is defined if (_setupOptions.frameUpdateRequestCallback) _setupOptions.frameUpdateRequestCallback(this); @@ -220,14 +228,16 @@ void OsmAnd::MapRenderer::invalidateFrame() void OsmAnd::MapRenderer::gpuWorkerThreadProcedure() { - assert(_setupOptions.gpuWorkerThreadEnabled); - // Capture thread ID _gpuWorkerThreadId = QThread::currentThreadId(); - // Call prologue if such exists - if (_setupOptions.gpuWorkerThreadPrologue) - _setupOptions.gpuWorkerThreadPrologue(this); + { + QReadLocker scopedLocker(&_setupOptionsLock); + + // Call prologue if such exists + if (_setupOptions.gpuWorkerThreadPrologue) + _setupOptions.gpuWorkerThreadPrologue(this); + } while (_gpuWorkerThreadIsAlive) { @@ -245,9 +255,13 @@ void OsmAnd::MapRenderer::gpuWorkerThreadProcedure() processGpuWorker(); } - // Call epilogue - if (_setupOptions.gpuWorkerThreadEpilogue) - _setupOptions.gpuWorkerThreadEpilogue(this); + { + QReadLocker scopedLocker(&_setupOptionsLock); + + // Call epilogue + if (_setupOptions.gpuWorkerThreadEpilogue) + _setupOptions.gpuWorkerThreadEpilogue(this); + } _gpuWorkerThreadId = nullptr; } @@ -295,36 +309,53 @@ void OsmAnd::MapRenderer::processGpuWorker() _gpuThreadDispatcher.runAll(); } -bool OsmAnd::MapRenderer::initializeRendering(bool renderTargetAvailable) +bool OsmAnd::MapRenderer::initializeRendering(bool fresh /* = true */) { + QMutexLocker scopedLocker(&_initializationLock); + + if (_isRenderingInitialized && fresh) + return false; + bool ok; - _resourcesGpuSyncRequestsCounter.storeRelaxed(0); + if (_isRenderingInitialized) + { + ok = gpuAPI->release(true); + if (!ok) + return false; + } + else + _resourcesGpuSyncRequestsCounter.storeRelaxed(0); ok = gpuAPI->initialize(); if (!ok) return false; - ok = preInitializeRendering(); - if (!ok) - return false; + if (_isRenderingInitialized) + { + _renderThreadId = QThread::currentThreadId(); - ok = doInitializeRendering(); - if (!ok) - return false; + ok = doReleaseRendering(true); + if (!ok) + return false; + } + else + { + ok = preInitializeRendering(); + if (!ok) + return false; + } - ok = postInitializeRendering(); + ok = doInitializeRendering(_isRenderingInitialized); if (!ok) return false; - // DEPTH BUFFER READING IS NOT NEEDED - // Once rendering is initialized, attach to render target if available - //if (renderTargetAvailable) - //{ - // ok = attachToRenderTarget(); - // if (!ok) - // return false; - //} + if (!_isRenderingInitialized) + { + ok = postInitializeRendering(); + if (!ok) + return false; + } // Once rendering is initialized, invalidate frame invalidateFrame(); @@ -357,8 +388,13 @@ bool OsmAnd::MapRenderer::preInitializeRendering() return true; } -bool OsmAnd::MapRenderer::doInitializeRendering() +bool OsmAnd::MapRenderer::doInitializeRendering(bool reinitialize) { + if (reinitialize) + return true; + + QReadLocker scopedLocker(&_setupOptionsLock); + // Create GPU worker thread if (_setupOptions.gpuWorkerThreadEnabled) { @@ -661,15 +697,25 @@ bool OsmAnd::MapRenderer::postRenderFrame(IMapRenderer_Metrics::Metric_renderFra bool OsmAnd::MapRenderer::releaseRendering(bool gpuContextLost) { - assert(_renderThreadId == QThread::currentThreadId()); + if (!gpuContextLost) + assert(_renderThreadId == QThread::currentThreadId()); + + QMutexLocker scopedLocker(&_initializationLock); if (gpuContextLost) gpuContextIsLost = true; - _setupOptions.gpuWorkerThreadEnabled = false; - _setupOptions.gpuWorkerThreadPrologue = nullptr; - _setupOptions.gpuWorkerThreadEpilogue = nullptr; - _setupOptions.frameUpdateRequestCallback = nullptr; + { + QWriteLocker scopedLocker(&_setupOptionsLock); + + _setupOptions.gpuWorkerThreadEnabled = false; + _setupOptions.gpuWorkerThreadPrologue = nullptr; + _setupOptions.gpuWorkerThreadEpilogue = nullptr; + _setupOptions.frameUpdateRequestCallback = nullptr; + } + + if (!_isRenderingInitialized) + return false; bool ok; @@ -695,9 +741,6 @@ bool OsmAnd::MapRenderer::releaseRendering(bool gpuContextLost) bool OsmAnd::MapRenderer::preReleaseRendering(const bool gpuContextLost) { - if (!_isRenderingInitialized) - return false; - return true; } @@ -781,31 +824,6 @@ bool OsmAnd::MapRenderer::handleStateChange(const MapRendererState& state, MapRe return ok; } -bool OsmAnd::MapRenderer::attachToRenderTarget() -{ - if (isAttachedToRenderTarget()) - return false; - - bool ok; - ok = gpuAPI->attachToRenderTarget(); - if (!ok) - return false; - - invalidateFrame(); - - return true; -} - -bool OsmAnd::MapRenderer::isAttachedToRenderTarget() -{ - return gpuAPI->isAttachedToRenderTarget(); -} - -bool OsmAnd::MapRenderer::detachFromRenderTarget() -{ - return gpuAPI->detachFromRenderTarget(false); -} - bool OsmAnd::MapRenderer::isIdle() const { bool isNotIdle = false; @@ -1847,6 +1865,23 @@ bool OsmAnd::MapRenderer::setViewport(const AreaI& viewport, bool forcedUpdate / return true; } +bool OsmAnd::MapRenderer::setFlip(bool flip, bool forcedUpdate /*= false*/) +{ + QMutexLocker scopedLocker(&_requestedStateMutex); + + bool update = forcedUpdate || (_requestedState.flip != flip); + if (!update) + return false; + + _requestedState.flip = flip; + + setMapTarget(_requestedState, forcedUpdate); + + notifyRequestedStateWasUpdated(MapRendererStateChange::Flip); + + return true; +} + bool OsmAnd::MapRenderer::setFieldOfView(const float fieldOfView, bool forcedUpdate /*= false*/) { QMutexLocker scopedLocker(&_requestedStateMutex); diff --git a/src/Map/MapRenderer.h b/src/Map/MapRenderer.h index a2f78f68d..e415edbda 100644 --- a/src/Map/MapRenderer.h +++ b/src/Map/MapRenderer.h @@ -68,6 +68,8 @@ namespace OsmAnd private: // General: bool _isRenderingInitialized; + mutable QMutex _initializationLock; + mutable QReadWriteLock _setupOptionsLock; MapRendererSetupOptions _setupOptions; // Configuration-related: @@ -278,7 +280,7 @@ namespace OsmAnd // Customization points: virtual bool preInitializeRendering(); - virtual bool doInitializeRendering(); + virtual bool doInitializeRendering(bool reinitialize); virtual bool postInitializeRendering(); virtual bool preUpdate(IMapRenderer_Metrics::Metric_update* metric); @@ -314,16 +316,12 @@ namespace OsmAnd const std::shared_ptr& configuration, bool forcedUpdate = false) Q_DECL_OVERRIDE; - bool initializeRendering(bool renderTargetAvailable) override; + bool initializeRendering(bool fresh = true) override; bool update(IMapRenderer_Metrics::Metric_update* metric = nullptr) final; bool prepareFrame(IMapRenderer_Metrics::Metric_prepareFrame* metric = nullptr) final; bool renderFrame(IMapRenderer_Metrics::Metric_renderFrame* metric = nullptr) final; bool releaseRendering(bool gpuContextLost) override; - bool attachToRenderTarget() override; - bool isAttachedToRenderTarget() override; - bool detachFromRenderTarget() override; - virtual bool isIdle() const Q_DECL_OVERRIDE; virtual QString getNotIdleReason() const Q_DECL_OVERRIDE; @@ -367,6 +365,7 @@ namespace OsmAnd virtual bool setWindowSize(const PointI& windowSize, bool forcedUpdate = false) Q_DECL_OVERRIDE; virtual bool setViewport(const AreaI& viewport, bool forcedUpdate = false) Q_DECL_OVERRIDE; + virtual bool setFlip(bool flip, bool forcedUpdate = false) Q_DECL_OVERRIDE; virtual bool setFieldOfView(const float fieldOfView, bool forcedUpdate = false) Q_DECL_OVERRIDE; virtual bool setVisibleDistance(const float visibleDistance, bool forcedUpdate = false) Q_DECL_OVERRIDE; virtual bool setDetailedDistance(const float detailedDistance, bool forcedUpdate = false) Q_DECL_OVERRIDE; diff --git a/src/Map/MapRendererState.cpp b/src/Map/MapRendererState.cpp index c4d7ee36c..950a98044 100644 --- a/src/Map/MapRendererState.cpp +++ b/src/Map/MapRendererState.cpp @@ -48,6 +48,7 @@ OsmAnd::MapState OsmAnd::MapRendererState::getMapState() const mapState.windowSize = windowSize; mapState.viewport = viewport; + mapState.flip = flip; mapState.fieldOfView = fieldOfView; mapState.visibleDistance = visibleDistance; mapState.detailedDistance = detailedDistance; diff --git a/src/Map/OpenGL/AtlasMapRendererDebugStage_OpenGL.cpp b/src/Map/OpenGL/AtlasMapRendererDebugStage_OpenGL.cpp index a31bbfcb6..d069b559f 100644 --- a/src/Map/OpenGL/AtlasMapRendererDebugStage_OpenGL.cpp +++ b/src/Map/OpenGL/AtlasMapRendererDebugStage_OpenGL.cpp @@ -99,59 +99,69 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializePoints2D() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mProjectionViewModel; ""\n" - "uniform vec3 param_vs_point; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec2 v = param_vs_point.xy + in_vs_vertexPosition * param_vs_point.z; ""\n" - " gl_Position = param_vs_mProjectionViewModel * vec4(v.x, v.y, -1.0, 1.0); ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _programPoint2D.id = 0; + if (!_programPoint2D.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); - return false; + _programPoint2D.id = gpuAPI->linkProgram(0, nullptr, + _programPoint2D.binaryCache, _programPoint2D.cacheFormat, true, &variablesMap); } - - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Parameters: common data - "uniform lowp vec4 param_fs_color; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) + if (!_programPoint2D.id.isValid()) { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mProjectionViewModel; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + "uniform vec3 param_vs_point; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec2 v = param_vs_point.xy + in_vs_vertexPosition * param_vs_point.z; ""\n" + " vec4 vertex = param_vs_mProjectionViewModel * vec4(v.x, v.y, -1.0, 1.0); ""\n" + " gl_Position = vertex * param_vs_resultScale; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); + return false; + } - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); - return false; - } + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Parameters: common data + "uniform lowp vec4 param_fs_color; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _programPoint2D.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _programPoint2D.id = gpuAPI->linkProgram(2, shaders, + _programPoint2D.binaryCache, _programPoint2D.cacheFormat, true, &variablesMap); + } if (!_programPoint2D.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -162,7 +172,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializePoints2D() bool ok = true; const auto& lookup = gpuAPI->obtainVariablesLookupContext(_programPoint2D.id, variablesMap); ok = ok && lookup->lookupLocation(_programPoint2D.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); - ok = ok && lookup->lookupLocation(_programPoint2D.vs.param.mProjectionViewModel, "param_vs_mProjectionViewModel", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_programPoint2D.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programPoint2D.vs.param.point, "param_vs_point", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programPoint2D.fs.param.color, "param_fs_color", GlslVariableType::Uniform); if (!ok) @@ -257,6 +267,14 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderPoints2D() 1, GL_FALSE, glm::value_ptr(internalState.mOrthographicProjection)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_programPoint2D.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + const auto pointSize = static_cast(std::min(currentState.viewport.width(), currentState.viewport.height())) / 50.0f; @@ -330,7 +348,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::releasePoints2D(bool gpuContextL glDeleteProgram(_programPoint2D.id); GL_CHECK_RESULT; } - _programPoint2D = ProgramPoint2D(); + _programPoint2D.id = 0; } return true; @@ -348,70 +366,80 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeRects2D() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mProjectionViewModel; ""\n" - "uniform vec4 param_vs_rect; ""\n" - "uniform float param_vs_angle; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec2 rectCenter = param_vs_rect.yx; ""\n" - " vec2 p = in_vs_vertexPosition*param_vs_rect.wz + rectCenter; ""\n" - " vec4 v; ""\n" - " float cos_a = cos(param_vs_angle); ""\n" - " float sin_a = sin(param_vs_angle); ""\n" - " p -= rectCenter; ""\n" - " v.x = rectCenter.x + p.x*cos_a - p.y*sin_a; ""\n" - " v.y = rectCenter.y + p.x*sin_a + p.y*cos_a; ""\n" - " v.z = -1.0; ""\n" - " v.w = 1.0; ""\n" - " ""\n" - " gl_Position = param_vs_mProjectionViewModel * v; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _programRect2D.id = 0; + if (!_programRect2D.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); - return false; + _programRect2D.id = gpuAPI->linkProgram(0, nullptr, + _programRect2D.binaryCache, _programRect2D.cacheFormat, true, &variablesMap); } - - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Parameters: common data - "uniform lowp vec4 param_fs_color; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) + if (!_programRect2D.id.isValid()) { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mProjectionViewModel; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + "uniform vec4 param_vs_rect; ""\n" + "uniform float param_vs_angle; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec2 rectCenter = param_vs_rect.yx; ""\n" + " vec2 p = in_vs_vertexPosition*param_vs_rect.wz + rectCenter; ""\n" + " vec4 v; ""\n" + " float cos_a = cos(param_vs_angle); ""\n" + " float sin_a = sin(param_vs_angle); ""\n" + " p -= rectCenter; ""\n" + " v.x = rectCenter.x + p.x*cos_a - p.y*sin_a; ""\n" + " v.y = rectCenter.y + p.x*sin_a + p.y*cos_a; ""\n" + " v.z = -1.0; ""\n" + " v.w = 1.0; ""\n" + " ""\n" + " v = param_vs_mProjectionViewModel * v; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); + return false; + } - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); - return false; - } + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Parameters: common data + "uniform lowp vec4 param_fs_color; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _programRect2D.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _programRect2D.id = gpuAPI->linkProgram(2, shaders, + _programRect2D.binaryCache, _programRect2D.cacheFormat, true, &variablesMap); + } if (!_programRect2D.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -423,6 +451,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeRects2D() const auto& lookup = gpuAPI->obtainVariablesLookupContext(_programRect2D.id, variablesMap); ok = ok && lookup->lookupLocation(_programRect2D.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_programRect2D.vs.param.mProjectionViewModel, "param_vs_mProjectionViewModel", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_programRect2D.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programRect2D.vs.param.rect, "param_vs_rect", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programRect2D.vs.param.angle, "param_vs_angle", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programRect2D.fs.param.color, "param_fs_color", GlslVariableType::Uniform); @@ -507,6 +536,14 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderRects2D() glUniformMatrix4fv(_programRect2D.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(internalState.mOrthographicProjection)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_programRect2D.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + for(const auto& primitive : constOf(_rects2D)) { const auto& rect = std::get<0>(primitive); @@ -579,7 +616,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::releaseRects2D(bool gpuContextLo glDeleteProgram(_programRect2D.id); GL_CHECK_RESULT; } - _programRect2D = ProgramRect2D(); + _programRect2D.id = 0; } return true; @@ -597,64 +634,74 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeLines2D() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; // (1.0, 0.0) for first point, (0.0, 1.0) for second ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mProjectionViewModel; ""\n" - "uniform vec2 param_vs_v0; ""\n" - "uniform vec2 param_vs_v1; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec4 v; ""\n" - " v.xy = in_vs_vertexPosition.x*param_vs_v0 + in_vs_vertexPosition.y*param_vs_v1; ""\n" - " v.z = -1.0; ""\n" - " v.w = 1.0; ""\n" - " ""\n" - " gl_Position = param_vs_mProjectionViewModel * v; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _programLine2D.id = 0; + if (!_programLine2D.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); - return false; + _programLine2D.id = gpuAPI->linkProgram(0, nullptr, + _programLine2D.binaryCache, _programLine2D.cacheFormat, true, &variablesMap); } - - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Parameters: common data - "uniform lowp vec4 param_fs_color; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) + if (!_programLine2D.id.isValid()) { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; // (1.0, 0.0) for first point, (0.0, 1.0) for second ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mProjectionViewModel; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + "uniform vec2 param_vs_v0; ""\n" + "uniform vec2 param_vs_v1; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec4 v; ""\n" + " v.xy = in_vs_vertexPosition.x*param_vs_v0 + in_vs_vertexPosition.y*param_vs_v1; ""\n" + " v.z = -1.0; ""\n" + " v.w = 1.0; ""\n" + " ""\n" + " v = param_vs_mProjectionViewModel * v; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); + return false; + } - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); - return false; - } + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Parameters: common data + "uniform lowp vec4 param_fs_color; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _programLine2D.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _programLine2D.id = gpuAPI->linkProgram(2, shaders, + _programLine2D.binaryCache, _programLine2D.cacheFormat, true, &variablesMap); + } if (!_programLine2D.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -666,6 +713,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeLines2D() const auto& lookup = gpuAPI->obtainVariablesLookupContext(_programLine2D.id, variablesMap); ok = ok && lookup->lookupLocation(_programLine2D.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_programLine2D.vs.param.mProjectionViewModel, "param_vs_mProjectionViewModel", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_programLine2D.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine2D.vs.param.v0, "param_vs_v0", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine2D.vs.param.v1, "param_vs_v1", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine2D.fs.param.color, "param_fs_color", GlslVariableType::Uniform); @@ -747,6 +795,14 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderLines2D() glUniformMatrix4fv(_programLine2D.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(internalState.mOrthographicProjection)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_programLine2D.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + for(const auto& primitive : constOf(_lines2D)) { const auto& line = primitive.first; @@ -824,7 +880,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::releaseLines2D(bool gpuContextLo glDeleteProgram(_programLine2D.id); GL_CHECK_RESULT; } - _programLine2D = ProgramLine2D(); + _programLine2D.id = 0; } return true; @@ -842,62 +898,72 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeLines3D() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; // (1.0, 0.0) for first point, (0.0, 1.0) for second ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mProjectionViewModel; ""\n" - "uniform vec4 param_vs_v0; ""\n" - "uniform vec4 param_vs_v1; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec4 v; ""\n" - " v = in_vs_vertexPosition.x*param_vs_v0 + in_vs_vertexPosition.y*param_vs_v1; ""\n" - " ""\n" - " gl_Position = param_vs_mProjectionViewModel * v; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _programLine3D.id = 0; + if (!_programLine3D.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); - return false; + _programLine3D.id = gpuAPI->linkProgram(0, nullptr, + _programLine3D.binaryCache, _programLine3D.cacheFormat, true, &variablesMap); } - - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Parameters: common data - "uniform lowp vec4 param_fs_color; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) + if (!_programLine3D.id.isValid()) { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; // (1.0, 0.0) for first point, (0.0, 1.0) for second ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mProjectionViewModel; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + "uniform vec4 param_vs_v0; ""\n" + "uniform vec4 param_vs_v1; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec4 v; ""\n" + " v = in_vs_vertexPosition.x*param_vs_v0 + in_vs_vertexPosition.y*param_vs_v1; ""\n" + " ""\n" + " v = param_vs_mProjectionViewModel * v; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); + return false; + } - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); - return false; - } + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Parameters: common data + "uniform lowp vec4 param_fs_color; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _programLine3D.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _programLine3D.id = gpuAPI->linkProgram(2, shaders, + _programLine3D.binaryCache, _programLine3D.cacheFormat, true, &variablesMap); + } if (!_programLine3D.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -909,6 +975,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeLines3D() const auto& lookup = gpuAPI->obtainVariablesLookupContext(_programLine3D.id, variablesMap); ok = ok && lookup->lookupLocation(_programLine3D.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_programLine3D.vs.param.mProjectionViewModel, "param_vs_mProjectionViewModel", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_programLine3D.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine3D.vs.param.v0, "param_vs_v0", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine3D.vs.param.v1, "param_vs_v1", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programLine3D.fs.param.color, "param_fs_color", GlslVariableType::Uniform); @@ -990,6 +1057,14 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderLines3D() glUniformMatrix4fv(_programLine3D.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(internalState.mPerspectiveProjectionView)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_programLine3D.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + for(const auto& primitive : constOf(_lines3D)) { const auto& line = primitive.first; @@ -1067,7 +1142,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::releaseLines3D(bool gpuContextLo glDeleteProgram(_programLine3D.id); GL_CHECK_RESULT; } - _programLine3D = ProgramLine3D(); + _programLine3D.id = 0; } return true; @@ -1085,71 +1160,81 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeQuads3D() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - // (1.0, 0.0, 0.0, 0.0) for first point - // (0.0, 1.0, 0.0, 0.0) for second point - // (0.0, 0.0, 1.0, 0.0) for third point - // (0.0, 0.0, 0.0, 1.0) for fourth point - "INPUT vec4 in_vs_vertexPosition; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mProjectionViewModel; ""\n" - "uniform vec4 param_vs_v0; ""\n" - "uniform vec4 param_vs_v1; ""\n" - "uniform vec4 param_vs_v2; ""\n" - "uniform vec4 param_vs_v3; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec4 v = ""\n" - " in_vs_vertexPosition.x*param_vs_v0 + ""\n" - " in_vs_vertexPosition.y*param_vs_v1 + ""\n" - " in_vs_vertexPosition.z*param_vs_v2 + ""\n" - " in_vs_vertexPosition.w*param_vs_v3; ""\n" - " ""\n" - " gl_Position = param_vs_mProjectionViewModel * v; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _programQuad3D.id = 0; + if (!_programQuad3D.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); - return false; + _programQuad3D.id = gpuAPI->linkProgram(0, nullptr, + _programQuad3D.binaryCache, _programQuad3D.cacheFormat, true, &variablesMap); } - - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Parameters: common data - "uniform lowp vec4 param_fs_color; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) + if (!_programQuad3D.id.isValid()) { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + // (1.0, 0.0, 0.0, 0.0) for first point + // (0.0, 1.0, 0.0, 0.0) for second point + // (0.0, 0.0, 1.0, 0.0) for third point + // (0.0, 0.0, 0.0, 1.0) for fourth point + "INPUT vec4 in_vs_vertexPosition; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mProjectionViewModel; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + "uniform vec4 param_vs_v0; ""\n" + "uniform vec4 param_vs_v1; ""\n" + "uniform vec4 param_vs_v2; ""\n" + "uniform vec4 param_vs_v3; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec4 v = ""\n" + " in_vs_vertexPosition.x*param_vs_v0 + ""\n" + " in_vs_vertexPosition.y*param_vs_v1 + ""\n" + " in_vs_vertexPosition.z*param_vs_v2 + ""\n" + " in_vs_vertexPosition.w*param_vs_v3; ""\n" + " ""\n" + " v = param_vs_mProjectionViewModel * v; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL vertex shader"); + return false; + } - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); - return false; - } + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Parameters: common data + "uniform lowp vec4 param_fs_color; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = param_fs_color; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _programQuad3D.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererDebugStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _programQuad3D.id = gpuAPI->linkProgram(2, shaders, + _programQuad3D.binaryCache, _programQuad3D.cacheFormat, true, &variablesMap); + } if (!_programQuad3D.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -1161,6 +1246,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::initializeQuads3D() const auto& lookup = gpuAPI->obtainVariablesLookupContext(_programQuad3D.id, variablesMap); ok = ok && lookup->lookupLocation(_programQuad3D.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_programQuad3D.vs.param.mProjectionViewModel, "param_vs_mProjectionViewModel", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_programQuad3D.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programQuad3D.vs.param.v0, "param_vs_v0", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programQuad3D.vs.param.v1, "param_vs_v1", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_programQuad3D.vs.param.v2, "param_vs_v2", GlslVariableType::Uniform); @@ -1247,6 +1333,14 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::renderQuads3D() glUniformMatrix4fv(_programQuad3D.vs.param.mProjectionViewModel, 1, GL_FALSE, glm::value_ptr(internalState.mPerspectiveProjectionView)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_programQuad3D.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + for(const auto& primitive : constOf(_quads3D)) { const auto& p0 = std::get<0>(primitive); @@ -1322,7 +1416,7 @@ bool OsmAnd::AtlasMapRendererDebugStage_OpenGL::releaseQuads3D(bool gpuContextLo glDeleteProgram(_programQuad3D.id); GL_CHECK_RESULT; } - _programQuad3D = ProgramQuad3D(); + _programQuad3D.id = 0; } return true; diff --git a/src/Map/OpenGL/AtlasMapRendererDebugStage_OpenGL.h b/src/Map/OpenGL/AtlasMapRendererDebugStage_OpenGL.h index 5c25fb60d..add322ec2 100644 --- a/src/Map/OpenGL/AtlasMapRendererDebugStage_OpenGL.h +++ b/src/Map/OpenGL/AtlasMapRendererDebugStage_OpenGL.h @@ -24,6 +24,8 @@ namespace OsmAnd GLname _iboPoint2D; struct ProgramPoint2D { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -35,6 +37,7 @@ namespace OsmAnd struct { // Common data GLlocation mProjectionViewModel; + GLlocation resultScale; GLlocation point; } param; } vs; @@ -55,6 +58,8 @@ namespace OsmAnd GLname _iboRect2D; struct ProgramRect2D { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -66,6 +71,7 @@ namespace OsmAnd struct { // Common data GLlocation mProjectionViewModel; + GLlocation resultScale; GLlocation rect; GLlocation angle; } param; @@ -88,6 +94,8 @@ namespace OsmAnd GLname _iboLine2D; struct ProgramLine2D { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -99,6 +107,7 @@ namespace OsmAnd struct { // Common data GLlocation mProjectionViewModel; + GLlocation resultScale; GLlocation v0; GLlocation v1; } param; @@ -121,6 +130,8 @@ namespace OsmAnd GLname _iboLine3D; struct ProgramLine3D { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -132,6 +143,7 @@ namespace OsmAnd struct { // Common data GLlocation mProjectionViewModel; + GLlocation resultScale; GLlocation v0; GLlocation v1; } param; @@ -154,6 +166,8 @@ namespace OsmAnd GLname _iboQuad3D; struct ProgramQuad3D { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -165,6 +179,7 @@ namespace OsmAnd struct { // Common data GLlocation mProjectionViewModel; + GLlocation resultScale; GLlocation v0; GLlocation v1; GLlocation v2; diff --git a/src/Map/OpenGL/AtlasMapRendererMapLayersStage_OpenGL.cpp b/src/Map/OpenGL/AtlasMapRendererMapLayersStage_OpenGL.cpp index 07228e8c1..5c15189fe 100644 --- a/src/Map/OpenGL/AtlasMapRendererMapLayersStage_OpenGL.cpp +++ b/src/Map/OpenGL/AtlasMapRendererMapLayersStage_OpenGL.cpp @@ -172,6 +172,7 @@ bool OsmAnd::AtlasMapRendererMapLayersStage_OpenGL::initializeRasterLayers() 1 /*sampler*/; const auto vsOtherUniforms = 4 /*param_vs_mPerspectiveProjectionView*/ + + 1 /*param_vs_resultScale*/ + 1 /*param_vs_targetInTilePosN*/ + 1 /*param_vs_tileSize*/ + (!gpuAPI->isSupported_textureLod @@ -232,6 +233,7 @@ bool OsmAnd::AtlasMapRendererMapLayersStage_OpenGL::initializeRasterLayers() }); // Get minimal and limit further with override if set + const auto previousMaxNumberOfRasterMapLayersInBatch = _maxNumberOfRasterMapLayersInBatch; _maxNumberOfRasterMapLayersInBatch = std::min({ maxBatchSizeByUniforms, maxBatchSizeByVaryingFloats, @@ -254,17 +256,34 @@ bool OsmAnd::AtlasMapRendererMapLayersStage_OpenGL::initializeRasterLayers() // Initialize programs that support [1 ... _maxNumberOfRasterMapLayersInBatch] as number of layers auto supportedMaxNumberOfRasterMapLayersInBatch = _maxNumberOfRasterMapLayersInBatch; - for (auto numberOfLayersInBatch = _maxNumberOfRasterMapLayersInBatch; numberOfLayersInBatch >= 1; numberOfLayersInBatch--) + if (_rasterLayerTilePrograms.size() != _maxNumberOfRasterMapLayersInBatch) { - RasterLayerTileProgram rasterLayerTileProgram; - const auto success = initializeRasterLayersProgram(numberOfLayersInBatch, rasterLayerTileProgram); - if (!success) + _rasterLayerTilePrograms.clear(); + for (auto numberOfLayersInBatch = _maxNumberOfRasterMapLayersInBatch; numberOfLayersInBatch >= 1; numberOfLayersInBatch--) { - supportedMaxNumberOfRasterMapLayersInBatch -= 1; - continue; - } + RasterLayerTileProgram rasterLayerTileProgram; + const auto success = initializeRasterLayersProgram(numberOfLayersInBatch, rasterLayerTileProgram); + if (!success) + { + supportedMaxNumberOfRasterMapLayersInBatch -= 1; + continue; + } - _rasterLayerTilePrograms.insert(numberOfLayersInBatch, rasterLayerTileProgram); + _rasterLayerTilePrograms.insert(numberOfLayersInBatch, rasterLayerTileProgram); + } + } + else + { + for (auto numberOfLayersInBatch = _maxNumberOfRasterMapLayersInBatch; numberOfLayersInBatch >= 1; numberOfLayersInBatch--) + { + auto& rasterLayerTileProgram = _rasterLayerTilePrograms[numberOfLayersInBatch]; + const auto success = initializeRasterLayersProgram(numberOfLayersInBatch, rasterLayerTileProgram); + if (!success) + { + supportedMaxNumberOfRasterMapLayersInBatch -= 1; + continue; + } + } } if (supportedMaxNumberOfRasterMapLayersInBatch != _maxNumberOfRasterMapLayersInBatch) { @@ -296,733 +315,743 @@ bool OsmAnd::AtlasMapRendererMapLayersStage_OpenGL::initializeRasterLayersProgra GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - const auto& vertexShader = QString::fromLatin1( - // Definitions - "#define ELEVATION_VISUALIZATION_ENABLED %ElevationVisualizationEnabled% ""\n" - " ""\n" - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - "INPUT vec2 in_vs_vertexTexCoords; ""\n" - " ""\n" - // Output data to next shader stages - "%UnrolledPerRasterLayerTexCoordsDeclarationCode% ""\n" - "PARAM_OUTPUT float v2f_metersPerUnit; ""\n" - "#if TEXTURE_LOD_SUPPORTED ""\n" - " PARAM_OUTPUT float v2f_mipmapLOD; ""\n" - "#endif // TEXTURE_LOD_SUPPORTED ""\n" - "#if ELEVATION_VISUALIZATION_ENABLED ""\n" - " PARAM_OUTPUT lowp vec4 v2f_elevationColor; ""\n" - "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" - "PARAM_OUTPUT vec4 v2f_position; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" - "uniform vec2 param_vs_targetInTilePosN; ""\n" - "uniform float param_vs_tileSize; ""\n" - "#if TEXTURE_LOD_SUPPORTED ""\n" - " uniform float param_vs_distanceFromCameraToTarget; ""\n" - " uniform float param_vs_cameraElevationAngleN; ""\n" - " uniform vec2 param_vs_groundCameraPosition; ""\n" - " uniform float param_vs_scaleToRetainProjectedSize; ""\n" - "#endif // TEXTURE_LOD_SUPPORTED ""\n" - "uniform vec4 param_vs_elevation_configuration; ""\n" - "#if ELEVATION_VISUALIZATION_ENABLED ""\n" - " uniform vec4 param_vs_elevation_hillshadeConfiguration; ""\n" - " uniform vec4 param_vs_elevation_colorMapKeys[%MaxElevationColorMapEntriesCount%]; ""\n" - " uniform vec4 param_vs_elevation_colorMapValues[%MaxElevationColorMapEntriesCount%]; ""\n" - "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" - " ""\n" - // Parameters: per-tile data - "uniform vec2 param_vs_tileCoordsOffset; ""\n" - "uniform vec4 param_vs_elevation_scale; ""\n" - "uniform highp sampler2D param_vs_elevation_dataSampler; ""\n" - " ""\n" - // Parameters: per-layer-in-tile data - "struct VsRasterLayerTile ""\n" - "{ ""\n" - " highp vec4 texCoordsOffsetAndScale; ""\n" - "}; ""\n" - "%UnrolledPerRasterLayerParamsDeclarationCode% ""\n" - "uniform VsRasterLayerTile param_vs_elevationLayer; ""\n" - "uniform highp vec4 param_vs_elevationLayerTexelSize; ""\n" - "uniform highp vec4 param_vs_elevationLayerDataPlace; ""\n" - " ""\n" - "float interpolatedHeight(in vec2 inTexCoords) ""\n" - "{ ""\n" - " vec2 heixelSize = param_vs_elevationLayerDataPlace.zw * 2.0; ""\n" - " vec2 texCoords = (inTexCoords - param_vs_elevationLayerDataPlace.zw) / heixelSize; ""\n" - " vec2 pixOffset = fract(texCoords); ""\n" - " texCoords = floor(texCoords) * heixelSize + param_vs_elevationLayerDataPlace.zw; ""\n" - " vec2 minCoords = param_vs_elevationLayerDataPlace.xy - heixelSize; ""\n" - " vec2 maxCoords = minCoords + heixelSize * (%HeixelsPerTileSide%.0 + 2.0); ""\n" - " float blHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" - " texCoords.x += heixelSize.x; ""\n" - " float brHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" - " texCoords.y += heixelSize.y; ""\n" - " float trHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" - " texCoords.x -= heixelSize.x; ""\n" - " float tlHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" - " float avbPixel = mix(blHeixel, brHeixel, pixOffset.x); ""\n" - " float avtPixel = mix(tlHeixel, trHeixel, pixOffset.x); ""\n" - " return mix(avbPixel, avtPixel, pixOffset.y); ""\n" - "} ""\n" - " ""\n" - "void calculateTextureCoordinates(in VsRasterLayerTile tileLayer, out vec2 outTexCoords) ""\n" - "{ ""\n" - " vec2 texCoords = in_vs_vertexTexCoords; ""\n" - " texCoords = texCoords * tileLayer.texCoordsOffsetAndScale.zw + tileLayer.texCoordsOffsetAndScale.xy; ""\n" - " outTexCoords = texCoords; ""\n" - "} ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec4 v = vec4(in_vs_vertexPosition.x, 0.0, in_vs_vertexPosition.y, 1.0); ""\n" - " ""\n" - // Scale and shift vertex to it's proper position - " v.xz *= param_vs_tileSize; ""\n" - " v.xz += param_vs_tileSize * (param_vs_tileCoordsOffset - param_vs_targetInTilePosN); ""\n" - " ""\n" - // Get meters per unit, which is needed at both shader stages - " v2f_metersPerUnit = mix(param_vs_elevation_scale.x, param_vs_elevation_scale.y, in_vs_vertexTexCoords.t); ""\n" - " ""\n" - // Process each tile layer texture coordinates (except elevation) - "%UnrolledPerRasterLayerTexCoordsProcessingCode% ""\n" - " ""\n" - // If elevation data is active, use it - "#if ELEVATION_VISUALIZATION_ENABLED ""\n" - " v2f_elevationColor = vec4(0.0, 0.0, 0.0, 0.0); ""\n" - "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" - " if (abs(param_vs_elevation_scale.w) > 0.0) ""\n" - " { ""\n" - " float slopeAlgorithm = param_vs_elevation_configuration.x; ""\n" - " ""\n" - // [0][0] - TL (0); [1][0] - T (1); [2][0] - TR (2) - // [0][1] - L (3); [1][1] - O (4); [2][1] - R (5) - // [0][2] - BL (6); [1][2] - B (7); [2][2] - BR (8) - " mat3 heightInMeters = mat3(0.0); ""\n" - " vec2 elevationTexCoordsO; ""\n" - " calculateTextureCoordinates( ""\n" - " param_vs_elevationLayer, ""\n" - " elevationTexCoordsO); ""\n" - " heightInMeters[1][1] = interpolatedHeight(elevationTexCoordsO); ""\n" - " ""\n" - "#if ELEVATION_VISUALIZATION_ENABLED ""\n" - " if (slopeAlgorithm > %SlopeAlgorithm_None%.0) ""\n" - " { ""\n" - " vec2 elevationTexCoordsT = elevationTexCoordsO; ""\n" - " elevationTexCoordsT.t -= param_vs_elevationLayerTexelSize.y; ""\n" - " heightInMeters[1][0] = interpolatedHeight(elevationTexCoordsT); ""\n" - " ""\n" - " vec2 elevationTexCoordsL = elevationTexCoordsO; ""\n" - " elevationTexCoordsL.s -= param_vs_elevationLayerTexelSize.x; ""\n" - " heightInMeters[0][1] = interpolatedHeight(elevationTexCoordsL); ""\n" - " ""\n" - " vec2 elevationTexCoordsB = elevationTexCoordsO; ""\n" - " elevationTexCoordsB.t += param_vs_elevationLayerTexelSize.y; ""\n" - " heightInMeters[1][2] = interpolatedHeight(elevationTexCoordsB); ""\n" - " ""\n" - " vec2 elevationTexCoordsR = elevationTexCoordsO; ""\n" - " elevationTexCoordsR.s += param_vs_elevationLayerTexelSize.x; ""\n" - " heightInMeters[2][1] = interpolatedHeight(elevationTexCoordsR); ""\n" - " ""\n" - " if (slopeAlgorithm > %SlopeAlgorithm_ZevenbergenThorne%.0) ""\n" - " { ""\n" - " vec2 elevationTexCoordsTL = elevationTexCoordsO; ""\n" - " elevationTexCoordsTL.s -= param_vs_elevationLayerTexelSize.x; ""\n" - " elevationTexCoordsTL.t -= param_vs_elevationLayerTexelSize.y; ""\n" - " heightInMeters[0][0] = interpolatedHeight(elevationTexCoordsTL); ""\n" - " ""\n" - " vec2 elevationTexCoordsTR = elevationTexCoordsO; ""\n" - " elevationTexCoordsTR.s += param_vs_elevationLayerTexelSize.x; ""\n" - " elevationTexCoordsTR.t -= param_vs_elevationLayerTexelSize.y; ""\n" - " heightInMeters[2][0] = interpolatedHeight(elevationTexCoordsTR); ""\n" - " ""\n" - " vec2 elevationTexCoordsBL = elevationTexCoordsO; ""\n" - " elevationTexCoordsBL.s -= param_vs_elevationLayerTexelSize.x; ""\n" - " elevationTexCoordsBL.t += param_vs_elevationLayerTexelSize.y; ""\n" - " heightInMeters[0][2] = interpolatedHeight(elevationTexCoordsBL); ""\n" - " ""\n" - " vec2 elevationTexCoordsBR = elevationTexCoordsO; ""\n" - " elevationTexCoordsBR.s += param_vs_elevationLayerTexelSize.x; ""\n" - " elevationTexCoordsBR.t += param_vs_elevationLayerTexelSize.y; ""\n" - " heightInMeters[2][2] = interpolatedHeight(elevationTexCoordsBR); ""\n" - " } ""\n" - " } ""\n" - "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" - " heightInMeters *= param_vs_elevation_scale.w * param_vs_elevation_scale.z; ""\n" - " ""\n" - "#if ELEVATION_VISUALIZATION_ENABLED ""\n" - " float visualizationStyle = param_vs_elevation_configuration.y; ""\n" - " if (visualizationStyle > %VisualizationStyle_None%.0) ""\n" - " { ""\n" - " const float M_PI = 3.1415926535897932384626433832795; ""\n" - " const float M_PI_2 = M_PI / 2.0; ""\n" - " const float M_2PI = 2.0 * M_PI; ""\n" - " const float M_3PI_2 = 3.0 * M_PI_2; ""\n" - " const float M_COS_225_D = -0.70710678118; ""\n" - " ""\n" - " float heixelInMeters = v2f_metersPerUnit * (param_vs_tileSize / %HeixelsPerTileSide%.0); ""\n" - " ""\n" - " vec2 slopeInMeters = vec2(0.0); ""\n" - " float slopeAlgorithmScale = 1.0; ""\n" - " if (abs(slopeAlgorithm - %SlopeAlgorithm_ZevenbergenThorne%.0) < 0.00001) ""\n" - " { ""\n" - " slopeInMeters.x = heightInMeters[0][1] - heightInMeters[2][1]; ""\n" - " slopeInMeters.y = heightInMeters[1][2] - heightInMeters[1][0]; ""\n" - " ""\n" - " slopeAlgorithmScale = 2.0; ""\n" - " } ""\n" - " else if (abs(slopeAlgorithm - %SlopeAlgorithm_Horn%.0) < 0.00001) ""\n" - " { ""\n" - " slopeInMeters.x = ( ""\n" - " heightInMeters[0][0] + heightInMeters[0][1] + heightInMeters[0][1] + heightInMeters[0][2] ""\n" - " ); ""\n" - " slopeInMeters.x -= ( ""\n" - " heightInMeters[2][0] + heightInMeters[2][1] + heightInMeters[2][1] + heightInMeters[2][2] ""\n" - " ); ""\n" - " ""\n" - " slopeInMeters.y = ( ""\n" - " heightInMeters[0][2] + heightInMeters[1][2] + heightInMeters[1][2] + heightInMeters[2][2] ""\n" - " ); ""\n" - " slopeInMeters.y -= ( ""\n" - " heightInMeters[0][0] + heightInMeters[1][0] + heightInMeters[1][0] + heightInMeters[2][0] ""\n" - " ); ""\n" - " ""\n" - " slopeAlgorithmScale = 8.0; ""\n" - " } ""\n" - " highp float slope_XX = slopeInMeters.x*slopeInMeters.x; ""\n" - " highp float slope_YY = slopeInMeters.y*slopeInMeters.y; ""\n" - " highp float slope_XXpYY = slope_XX + slope_YY; ""\n" - " ""\n" - " float zFactor = param_vs_elevation_configuration.w; ""\n" - " float zFactorN = zFactor / (slopeAlgorithmScale * heixelInMeters); ""\n" - " float zFactorNSq = zFactorN * zFactorN; ""\n" - " float sunZenith = param_vs_elevation_hillshadeConfiguration.x; ""\n" - " float zNCosZenith = zFactorN * cos(sunZenith); ""\n" - " ""\n" - " float colorMapKey_0 = param_vs_elevation_colorMapKeys[0].w; ""\n" - " float colorMapKey = colorMapKey_0 - 1.0; ""\n" - " if (abs(visualizationStyle - %VisualizationStyle_SlopeDegrees%.0) < 0.00001) ""\n" - " { ""\n" - " vec2 slopeN = slopeInMeters / (slopeAlgorithmScale * heixelInMeters); ""\n" - " float slopeN_XXpYY = slopeN.x*slopeN.x + slopeN.y*slopeN.y; ""\n" - " colorMapKey = degrees(atan(zFactor * sqrt(slopeN_XXpYY))); ""\n" - " colorMapKey = max(colorMapKey, colorMapKey_0); ""\n" - " } ""\n" - " else if (abs(visualizationStyle - %VisualizationStyle_SlopePercents%.0) < 0.00001) ""\n" - " { ""\n" - " vec2 slopeN = slopeInMeters / (slopeAlgorithmScale * heixelInMeters); ""\n" - " float slopeN_XXpYY = slopeN.x*slopeN.x + slopeN.y*slopeN.y; ""\n" - " colorMapKey = 100.0 * zFactor * sqrt(slopeN_XXpYY); ""\n" - " colorMapKey = max(colorMapKey, colorMapKey_0); ""\n" - " } ""\n" - " else if (abs(visualizationStyle - %VisualizationStyle_HillshadeMultidirectional%.0) < 0.00001) ""\n" - " { ""\n" - // NOTE: See http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf - " float sinZenith = sin(sunZenith); ""\n" - " ""\n" - " if (slope_XXpYY < 0.00001) ""\n" - " { ""\n" - " float value = 1.0 + 254.0 * sinZenith; ""\n" - " colorMapKey = max(value, colorMapKey_0); ""\n" - " } ""\n" - " else ""\n" - " { ""\n" - " highp float weight_225 = 0.5 * slope_XXpYY - slopeInMeters.x * slopeInMeters.y; ""\n" - " highp float weight_270 = slope_XX; ""\n" - " highp float weight_315 = slope_XXpYY - weight_225; ""\n" - " highp float weight_360 = slope_YY; ""\n" - " ""\n" - " float value_225 = sinZenith - (slopeInMeters.x - slopeInMeters.y) * M_COS_225_D * zNCosZenith; ""\n" - " float value_270 = sinZenith - slopeInMeters.x * zNCosZenith; ""\n" - " float value_315 = sinZenith + (slopeInMeters.x + slopeInMeters.y) * M_COS_225_D * zNCosZenith; ""\n" - " float value_360 = sinZenith - slopeInMeters.y * zNCosZenith; ""\n" - " ""\n" - " float value = weight_225 * max(value_225, 0.0); ""\n" - " value += weight_270 * max(value_270, 0.0); ""\n" - " value += weight_315 * max(value_315, 0.0); ""\n" - " value += weight_360 * max(value_360, 0.0); ""\n" - " value *= 127.0; ""\n" - " value /= slope_XXpYY; ""\n" - " value /= sqrt(1.0 + zFactorNSq * slope_XXpYY); ""\n" - " value = 1.0 + value; ""\n" - " colorMapKey = max(value, colorMapKey_0); ""\n" - " } ""\n" - " } ""\n" - " else ""\n" - " { ""\n" - " float sunAzimuth = M_PI - param_vs_elevation_hillshadeConfiguration.y; ""\n" - " ""\n" - " float zNCosZenithCosAzimuth = zNCosZenith * cos(sunAzimuth); ""\n" - " float zNCosZenithSinAzimuth = zNCosZenith * sin(sunAzimuth); ""\n" - " ""\n" - " if (abs(visualizationStyle - %VisualizationStyle_HillshadeTraditional%.0) < 0.00001) ""\n" - " { ""\n" - " float value = sin(sunZenith); ""\n" - " value -= slopeInMeters.y*zNCosZenithCosAzimuth - slopeInMeters.x*zNCosZenithSinAzimuth; ""\n" - " value = 254.0 * value / sqrt(1.0 + zFactorNSq * slope_XXpYY); ""\n" - " value = 1.0 + max(value, 0.0); ""\n" - " colorMapKey = max(value, colorMapKey_0); ""\n" - " } ""\n" - " else if (abs(visualizationStyle - %VisualizationStyle_HillshadeIgor%.0) < 0.00001) ""\n" - " { ""\n" - " float slopeAngle = atan(sqrt(slope_XXpYY) * zFactorN); ""\n" - " float aspect = atan(slopeInMeters.y, slopeInMeters.x); ""\n" - " float slopeStrength = slopeAngle / M_PI_2; ""\n" - " float aspectN = mod(M_2PI + aspect, M_2PI); ""\n" - " float wAzimuthN = mod(M_2PI + mod(M_3PI_2 - sunAzimuth, M_2PI), M_2PI); ""\n" - " float aspectDiffWAzimuth = mod(abs(aspectN - wAzimuthN), M_PI); ""\n" - " float value = 255.0 * (1.0 - slopeStrength * (1.0 - aspectDiffWAzimuth / M_PI)); ""\n" - " colorMapKey = max(value, colorMapKey_0); ""\n" - " } ""\n" - " else if (abs(visualizationStyle - %VisualizationStyle_HillshadeCombined%.0) < 0.00001) ""\n" - " { ""\n" - // NOTE: First part produces same result as HillshadeTraditional except for mapping to 1..255 scale (oblique shading) - " float value = sin(sunZenith); ""\n" - " value -= slopeInMeters.y*zNCosZenithCosAzimuth - slopeInMeters.x*zNCosZenithSinAzimuth; ""\n" - " value /= sqrt(1.0 + zFactorNSq * slope_XXpYY); ""\n" - " ""\n" - // NOTE: Combine with slope shading - // NOTE: Implemented differently that in GDAL, see source that https://trac.osgeo.org/gdal/ticket/4753 references - " vec2 slopeN = slopeInMeters / (slopeAlgorithmScale * heixelInMeters); ""\n" - " float slopeN_XXpYY = slopeN.x*slopeN.x + slopeN.y*slopeN.y; ""\n" - " value *= 1.0 - abs(atan(zFactor * sqrt(slopeN_XXpYY)) / M_PI); ""\n" - " ""\n" - " value = 1.0 + 254.0 * max(value, 0.0); ""\n" - " colorMapKey = max(value, colorMapKey_0); ""\n" - " } ""\n" - " } ""\n" - " ""\n" - " if (colorMapKey >= colorMapKey_0) ""\n" - " { ""\n" - " float colorMapEntryKeyA = colorMapKey_0; ""\n" - " float colorMapEntryKeyB = colorMapKey; ""\n" - " vec4 colorMapEntryValueA = vec4(0.0); ""\n" - " vec4 colorMapEntryValueB = vec4(0.0); ""\n" - " ""\n" - "%UnrolledPerElevationColorMapEntryCode% ""\n" - " ""\n" - " float interpolation = (colorMapKey - colorMapEntryKeyA) / (colorMapEntryKeyB - colorMapEntryKeyA); ""\n" - " v2f_elevationColor = mix( ""\n" - " colorMapEntryValueA, ""\n" - " colorMapEntryValueB, ""\n" - " clamp(interpolation, 0.0, 1.0)); ""\n" - " } ""\n" - " v2f_elevationColor.a *= param_vs_elevation_configuration.z; ""\n" - " } ""\n" - "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" - " v.y = heightInMeters[1][1] / v2f_metersPerUnit; ""\n" - " } ""\n" - " ""\n" - "#if TEXTURE_LOD_SUPPORTED ""\n" - // Calculate mipmap LOD - " vec2 groundVertex = v.xz; ""\n" - " vec2 groundCameraToVertex = groundVertex - param_vs_groundCameraPosition; ""\n" - " float mipmapK = log(1.0 + 10.0 * log2(1.0 + param_vs_cameraElevationAngleN)); ""\n" - " float mipmapBaseLevelEndDistance = mipmapK * param_vs_distanceFromCameraToTarget; ""\n" - " v2f_mipmapLOD = 1.0 + (length(groundCameraToVertex) - mipmapBaseLevelEndDistance) ""\n" - " / (param_vs_scaleToRetainProjectedSize * param_vs_tileSize); ""\n" - "#endif // TEXTURE_LOD_SUPPORTED ""\n" - " ""\n" - // Finally output processed modified vertex - " v2f_position = v; ""\n" - " gl_Position = param_vs_mPerspectiveProjectionView * v; ""\n" - "} ""\n"); - const auto& vertexShader_perRasterLayerTexCoordsDeclaration = QString::fromLatin1( - "PARAM_OUTPUT vec2 v2f_texCoordsPerLayer_%rasterLayerIndex%; ""\n"); - const auto& vertexShader_perRasterLayerParamsDeclaration = QString::fromLatin1( - "uniform VsRasterLayerTile param_vs_rasterTileLayer_%rasterLayerIndex%; ""\n"); - const auto& vertexShader_perRasterLayerTexCoordsProcessing = QString::fromLatin1( - " calculateTextureCoordinates( ""\n" - " param_vs_rasterTileLayer_%rasterLayerIndex%, ""\n" - " v2f_texCoordsPerLayer_%rasterLayerIndex%); ""\n" - " ""\n"); - const auto& vertexShader_perElevationColorMapEntryCode = QString::fromLatin1( - " float colorMapKey_%entryIndex% = param_vs_elevation_colorMapKeys[%entryIndex%].w; ""\n" - " if (colorMapKey_%entryIndex% > colorMapKey_%prevEntryIndex%) ""\n" - " { ""\n" - " if (colorMapKey >= colorMapKey_%prevEntryIndex%) ""\n" - " { ""\n" - " colorMapEntryKeyA = colorMapKey_%prevEntryIndex%; ""\n" - " colorMapEntryValueA = param_vs_elevation_colorMapValues[%prevEntryIndex%]; ""\n" - " colorMapEntryKeyB = colorMapKey_%entryIndex%; ""\n" - " colorMapEntryValueB = param_vs_elevation_colorMapValues[%entryIndex%]; ""\n" - " } ""\n" - " } ""\n" - " ""\n"); - - const auto& fragmentShader = QString::fromLatin1( - // Definitions - "#define ELEVATION_VISUALIZATION_ENABLED %ElevationVisualizationEnabled% ""\n" - " ""\n" - // Input data - "%UnrolledPerRasterLayerTexCoordsDeclarationCode% ""\n" - "PARAM_INPUT float v2f_metersPerUnit; ""\n" - "#if TEXTURE_LOD_SUPPORTED ""\n" - " PARAM_INPUT float v2f_mipmapLOD; ""\n" - "#endif // TEXTURE_LOD_SUPPORTED ""\n" - "#if ELEVATION_VISUALIZATION_ENABLED ""\n" - " PARAM_INPUT lowp vec4 v2f_elevationColor; ""\n" - "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" - "PARAM_INPUT vec4 v2f_position; ""\n" - " ""\n" - // Parameters: common data - "uniform lowp float param_fs_lastBatch; ""\n" - "uniform lowp float param_fs_blendingEnabled; ""\n" - "uniform lowp vec4 param_fs_backgroundColor; ""\n" - "uniform lowp vec4 param_fs_myLocationColor; ""\n" - "uniform vec4 param_fs_myLocation; ""\n" - "uniform vec2 param_fs_myDirection; ""\n" - "uniform vec4 param_fs_worldCameraPosition; ""\n" - "uniform vec4 param_fs_mistConfiguration; ""\n" - "uniform vec4 param_fs_mistColor; ""\n" - // Parameters: per-layer data - "struct FsRasterLayerTile ""\n" - "{ ""\n" - " lowp float isPremultipliedAlpha; ""\n" - " lowp float opacityFactor; ""\n" - " highp vec4 texCoordsOffsetAndScale; ""\n" - " highp vec4 transitionPhase; ""\n" - " highp float texelSize; ""\n" - " lowp sampler2D sampler; ""\n" - "}; ""\n" - "%UnrolledPerRasterLayerParamsDeclarationCode% ""\n" - " ""\n" - "void addExtraAlpha(inout lowp vec4 color, in lowp float alpha, in lowp float isPremultipliedAlpha) ""\n" - "{ ""\n" - " lowp float colorAlpha = 1.0 - isPremultipliedAlpha + isPremultipliedAlpha * alpha; ""\n" - " color *= vec4(colorAlpha, colorAlpha, colorAlpha, alpha); ""\n" - "} ""\n" - " ""\n" - "void mixColors(inout lowp vec4 destColor, in lowp vec4 srcColor) ""\n" - "{ ""\n" - " destColor = destColor * (1.0 - srcColor.a) + srcColor * vec4(srcColor.a, srcColor.a, srcColor.a, 1.0); ""\n" - "} ""\n" - " ""\n" - "void mixColors(inout lowp vec4 destColor, in lowp vec4 srcColor, in lowp float isPremultipliedAlpha) ""\n" - "{ ""\n" - " lowp float srcColorMultiplier = ""\n" - " isPremultipliedAlpha + (1.0 - isPremultipliedAlpha) * srcColor.a; ""\n" - " destColor *= 1.0 - srcColor.a; ""\n" - " destColor += srcColor * vec4(srcColorMultiplier, srcColorMultiplier, srcColorMultiplier, 1.0); ""\n" - "} ""\n" - " ""\n" - "void fromTexture(in lowp sampler2D sampler, in vec2 coords, out lowp vec4 destColor) ""\n" - "{ ""\n" - "#if TEXTURE_LOD_SUPPORTED ""\n" - " destColor = SAMPLE_TEXTURE_2D_LOD(sampler, coords, v2f_mipmapLOD); ""\n" - "#else // !TEXTURE_LOD_SUPPORTED ""\n" - " destColor = SAMPLE_TEXTURE_2D(sampler, coords); ""\n" - "#endif // TEXTURE_LOD_SUPPORTED ""\n" - "} ""\n" - " ""\n" - "void getWind(in lowp sampler2D sampler, in vec2 coords, out vec2 windVector) ""\n" - "{ ""\n" - " lowp vec4 windColor; ""\n" - " fromTexture(sampler, coords, windColor); ""\n" - " vec2 result = (windColor.rb + windColor.ga * 255.0 - 8.0) * 10.0; ""\n" - " windVector = vec2(result.x, -result.y); ""\n" - "} ""\n" - " ""\n" - "float getRandPixel(in vec2 pixCoords, in vec2 tileSize, in float normSeed) ""\n" - "{ ""\n" - " vec2 intCoords = floor(pixCoords); ""\n" - " float randValue = abs(fract(sin(dot(intCoords / tileSize + normSeed, vec2(12.9898, 78.233))) * 43758.5453)); ""\n" - " return randValue > 0.98 ? 1.0 : 0.0; ""\n" - "} ""\n" - " ""\n" - "void getParticleColor(in vec2 pixCoords, in vec2 windVector, in vec2 tileSize, in float seed, out lowp vec4 color) ""\n" - "{ ""\n" - " float normSeed = seed * 0.001; ""\n" - " float windMagnitude = length(windVector); ""\n" - " vec2 shift = windMagnitude >= 0.5 ? windVector / windMagnitude : vec2(0.0); ""\n" - // Rescale two times: to get out of tile data without overlap (- 0.1 / 0.8) and to access full texture (/ 0.5) - " vec2 coords = (pixCoords - 0.05) * tileSize / 0.4; ""\n" - " vec2 center = coords + shift; ""\n" - " float nextPixel = 0.5 * getRandPixel(center, tileSize, normSeed); ""\n" - " float prevPixel = getRandPixel(coords - shift, tileSize, normSeed); ""\n" - " center = prevPixel > 0.0 ? coords - shift : center; ""\n" - " float actPixel = 0.8 * getRandPixel(coords, tileSize, normSeed); ""\n" - " center = actPixel > 0.0 ? coords : center; ""\n" - " float alpha = max(max(actPixel, nextPixel), prevPixel); ""\n" - " vec2 norm = vec2(shift.y, -shift.x); ""\n" - " alpha *= clamp(1.0 - 2.0 * abs(dot(norm, coords) - dot(norm, floor(center) + 0.5)), 0.0, 1.0); ""\n" - " color = vec4(1.0, 1.0, 1.0, alpha); ""\n" - "} ""\n" - " ""\n" - "void getTextureColor(in vec4 texCoordsOffsetAndScale, in vec4 transitionPhase, in float texelSize, ""\n" - " in sampler2D sampler, in vec2 texCoords, out lowp vec4 resultColor) ""\n" - "{ ""\n" - " if (transitionPhase.x >= 0.0) ""\n" - " { ""\n" - // Animate transition between textures - " vec2 leftCoords = texCoords; ""\n" - " vec2 rightCoords; ""\n" - " lowp vec4 windColor = vec4(0.0); ""\n" - " float halfTexelSize = texelSize / 2.0; ""\n" - // Scale two times: to get into tile data without overlap (* 0.8 + 0.1) and to access quadrants (* 0.5) - " leftCoords = leftCoords * 0.4 + 0.05; ""\n" - " leftCoords.x = clamp(leftCoords.x, halfTexelSize, 0.5 - halfTexelSize); ""\n" - " leftCoords.y = clamp(leftCoords.y, halfTexelSize, 0.5 - halfTexelSize); ""\n" - " rightCoords = leftCoords + 0.5; ""\n" - " vec2 rightWind; ""\n" - " getWind(sampler, rightCoords, rightWind); ""\n" - // Scale two times (* 0.8 and * 0.5) and then divide to TileSize3D (/ 100) - " float factor = transitionPhase.y / (v2f_metersPerUnit * 250.0); ""\n" - " vec2 texShift = rightWind * texCoordsOffsetAndScale.zw * factor; ""\n" - " if (transitionPhase.z >= 0.0) ""\n" - " { ""\n" - // Animate wind particles - " float seed = floor(transitionPhase.z) / 10.0; ""\n" - " float shift = floor(texCoordsOffsetAndScale.y * 10.0) / 100.0; ""\n" - " shift += floor(texCoordsOffsetAndScale.x * 10.0) / 1000.0; ""\n" - " float phase = fract(transitionPhase.z); ""\n" - " vec2 tileSize = transitionPhase.w / texCoordsOffsetAndScale.zw; ""\n" - " rightCoords.x -= 0.5; ""\n" - " vec2 leftWind; ""\n" - " getWind(sampler, rightCoords, leftWind); ""\n" - " vec2 windVector = mix(leftWind, rightWind, transitionPhase.x); ""\n" - " vec2 windShift = windVector * texCoordsOffsetAndScale.zw * factor; ""\n" - " vec2 leftShift = windShift * phase; ""\n" - " vec2 rightShift = windShift * (1.0 - phase); ""\n" - " lowp vec4 tmpColor; ""\n" - " lowp vec4 leftColor; ""\n" - " lowp vec4 rightColor; ""\n" - " getParticleColor(leftCoords - leftShift, windVector, tileSize, fract(seed) + shift, leftColor); ""\n" - " getParticleColor(leftCoords - leftShift, windVector, tileSize, fract(seed + 0.1) + shift, tmpColor); ""\n" - " leftColor = mix(tmpColor, leftColor, clamp(phase + 0.5, 0.0, 1.0)); ""\n" - " getParticleColor(leftCoords + rightShift, windVector, tileSize, fract(seed + 0.1) + shift, rightColor);""\n" - " getParticleColor(leftCoords + rightShift, windVector, tileSize, fract(seed + 0.2) + shift, tmpColor); ""\n" - " rightColor = mix(rightColor, tmpColor, clamp(phase - 0.5, 0.0, 1.0)); ""\n" - " windColor = mix(leftColor, rightColor, phase); ""\n" - " } ""\n" - " rightCoords = leftCoords; ""\n" - " rightCoords.x += 0.5; ""\n" - " leftCoords -= texShift * transitionPhase.x; ""\n" - " leftCoords.x = clamp(leftCoords.x, halfTexelSize, 0.5 - halfTexelSize); ""\n" - " leftCoords.y = clamp(leftCoords.y, halfTexelSize, 0.5 - halfTexelSize); ""\n" - " rightCoords += texShift * (1.0 - transitionPhase.x); ""\n" - " rightCoords.x = clamp(rightCoords.x, 0.5 + halfTexelSize, 1.0 - halfTexelSize); ""\n" - " rightCoords.y = clamp(rightCoords.y, halfTexelSize, 0.5 - halfTexelSize); ""\n" - " lowp vec4 texColorLeft; ""\n" - " lowp vec4 texColorRight; ""\n" - " fromTexture(sampler, leftCoords, texColorLeft); ""\n" - " fromTexture(sampler, rightCoords, texColorRight); ""\n" - " lowp vec4 texColor = mix(texColorLeft, texColorRight, clamp(transitionPhase.x, 0.0, 1.0)); ""\n" - " resultColor = vec4(mix(texColor.rgb, windColor.rgb, windColor.a), texColor.a); ""\n" - " } ""\n" - " else ""\n" - " { ""\n" - " fromTexture(sampler, texCoords, resultColor); ""\n" - " } ""\n" - "} ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " lowp vec4 finalColor; ""\n" - " ""\n" - // Calculate color of accuracy circle - " lowp vec4 circle = param_fs_myLocationColor; ""\n" - " vec2 vMyToPos = v2f_position.xz - param_fs_myLocation.xy; ""\n" - " float dist = length(vMyToPos); ""\n" - " circle.a = dist > param_fs_myLocation.z ? 0.0 : circle.a * (dist + 0.05 > param_fs_myLocation.z ? 2.0 : 1.0); ""\n" - " ""\n" - // Calculate color of heading sector - " lowp vec4 sector = param_fs_myLocationColor; ""\n" - " float fdir = dot(vec2(sin(param_fs_myDirection.x), -cos(param_fs_myDirection.x)), vMyToPos / dist); ""\n" - " sector.a = dist >= param_fs_myDirection.y ? 0.0 : (fdir < 0.7071 ? 0.0 : 1.0 - dist / param_fs_myDirection.y); ""\n" - " ""\n" - // Calculate mist color - " lowp vec4 mistColor = param_fs_mistColor; ""\n" - " vec4 infrontPosition = v2f_position; ""\n" - " infrontPosition.xz = v2f_position.xz * param_fs_mistConfiguration.xy; ""\n" - " infrontPosition.xz = v2f_position.xz - (infrontPosition.x + infrontPosition.z) * param_fs_mistConfiguration.xy;""\n" - " float toFog = param_fs_mistConfiguration.z - distance(infrontPosition, param_fs_worldCameraPosition); ""\n" - " float expScale = (3.0 - param_fs_mistColor.w ) / param_fs_mistConfiguration.w; ""\n" - " float expOffset = 2.354 - param_fs_mistColor.w * 0.5; ""\n" - " mistColor.a = clamp(1.0 - 1.0 / exp(pow(max(0.0, expOffset - toFog * expScale), 2.0)), 0.0, 1.0); ""\n" - " ""\n" - // Mix colors of all layers. - // First layer is processed unconditionally, as well as its color is converted to premultiplied alpha. - " getTextureColor(param_fs_rasterTileLayer_0.texCoordsOffsetAndScale, param_fs_rasterTileLayer_0.transitionPhase,""\n" - " param_fs_rasterTileLayer_0.texelSize, param_fs_rasterTileLayer_0.sampler, ""\n" - " v2f_texCoordsPerLayer_0, finalColor); ""\n" - " addExtraAlpha(finalColor, param_fs_rasterTileLayer_0.opacityFactor, ""\n" - " param_fs_rasterTileLayer_0.isPremultipliedAlpha); ""\n" - " lowp float firstLayerColorFactor = param_fs_rasterTileLayer_0.isPremultipliedAlpha + ""\n" - " (1.0 - param_fs_rasterTileLayer_0.isPremultipliedAlpha) * finalColor.a; ""\n" - " finalColor *= vec4(firstLayerColorFactor, firstLayerColorFactor, firstLayerColorFactor, 1.0); ""\n" - " ""\n" - "%UnrolledPerRasterLayerProcessingCode% ""\n" - " ""\n" - "#if ELEVATION_VISUALIZATION_ENABLED ""\n" - " mixColors(finalColor, v2f_elevationColor * param_fs_lastBatch); ""\n" - "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" - " ""\n" -#if 0 - // NOTE: Useful for debugging mipmap levels - " { ""\n" - " vec4 mipmapDebugColor; ""\n" - " mipmapDebugColor.a = 1.0; ""\n" - " float value = v2f_mipmapLOD; ""\n" - //" float value = textureQueryLod(param_vs_rasterTileLayer[0].sampler, v2f_texCoordsPerLayer[0]).x; ""\n" - " mipmapDebugColor.r = clamp(value, 0.0, 1.0); ""\n" - " value -= 1.0; ""\n" - " mipmapDebugColor.g = clamp(value, 0.0, 1.0); ""\n" - " value -= 1.0; ""\n" - " mipmapDebugColor.b = clamp(value, 0.0, 1.0); ""\n" - " finalColor = mix(finalColor, mipmapDebugColor, 0.5); ""\n" - " } ""\n" -#endif - " ""\n" - " mixColors(finalColor, circle * param_fs_lastBatch); ""\n" - " mixColors(finalColor, sector * param_fs_lastBatch); ""\n" - " mixColors(finalColor, mistColor * param_fs_lastBatch); ""\n" - " lowp vec4 overColor = mix(param_fs_backgroundColor, finalColor, finalColor.a); ""\n" - " FRAGMENT_COLOR_OUTPUT = mix(overColor, finalColor, param_fs_blendingEnabled); ""\n" - "} ""\n"); - const auto& fragmentShader_perRasterLayer = QString::fromLatin1( - " { ""\n" - " lowp vec4 tc; ""\n" - " getTextureColor(param_fs_rasterTileLayer_%rasterLayerIndex%.texCoordsOffsetAndScale, ""\n" - " param_fs_rasterTileLayer_%rasterLayerIndex%.transitionPhase, ""\n" - " param_fs_rasterTileLayer_%rasterLayerIndex%.texelSize, ""\n" - " param_fs_rasterTileLayer_%rasterLayerIndex%.sampler, v2f_texCoordsPerLayer_%rasterLayerIndex%, tc); ""\n" - " addExtraAlpha(tc, param_fs_rasterTileLayer_%rasterLayerIndex%.opacityFactor, ""\n" - " param_fs_rasterTileLayer_%rasterLayerIndex%.isPremultipliedAlpha); ""\n" - " mixColors(finalColor, tc, param_fs_rasterTileLayer_%rasterLayerIndex%.isPremultipliedAlpha); ""\n" - " } ""\n"); - const auto& fragmentShader_perRasterLayerTexCoordsDeclaration = QString::fromLatin1( - "PARAM_INPUT vec2 v2f_texCoordsPerLayer_%rasterLayerIndex%; ""\n"); - const auto& fragmentShader_perRasterLayerParamsDeclaration = QString::fromLatin1( - "uniform FsRasterLayerTile param_fs_rasterTileLayer_%rasterLayerIndex%; ""\n"); - - // Compile vertex shader - auto preprocessedVertexShader = vertexShader; - QString preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsProcessingCode; - QString preprocessedVertexShader_UnrolledPerRasterLayerParamsDeclarationCode; - QString preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsDeclarationCode; - for (auto layerIndex = 0u; layerIndex < numberOfLayersInBatch; layerIndex++) - { - preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsProcessingCode += - detachedOf(vertexShader_perRasterLayerTexCoordsProcessing) - .replace("%rasterLayerIndex%", QString::number(layerIndex)); - - preprocessedVertexShader_UnrolledPerRasterLayerParamsDeclarationCode += - detachedOf(vertexShader_perRasterLayerParamsDeclaration) - .replace("%rasterLayerIndex%", QString::number(layerIndex)); - - preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsDeclarationCode += - detachedOf(vertexShader_perRasterLayerTexCoordsDeclaration) - .replace("%rasterLayerIndex%", QString::number(layerIndex)); - } - preprocessedVertexShader.replace("%UnrolledPerRasterLayerTexCoordsProcessingCode%", - preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsProcessingCode); - preprocessedVertexShader.replace("%UnrolledPerRasterLayerParamsDeclarationCode%", - preprocessedVertexShader_UnrolledPerRasterLayerParamsDeclarationCode); - preprocessedVertexShader.replace("%UnrolledPerRasterLayerTexCoordsDeclarationCode%", - preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsDeclarationCode); - QString preprocessedVertexShader_UnrolledPerElevationColorMapEntryCode; - for (auto colorMapEntryIndex = 1u; colorMapEntryIndex < ElevationConfiguration::MaxColorMapEntries; colorMapEntryIndex++) + auto variableLocations = QList< std::tuple >({ + { GlslVariableType::In, QStringLiteral("in_vs_vertexPosition"), 0 }, + { GlslVariableType::In, QStringLiteral("in_vs_vertexTexCoords"), 1 }, + }); + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + outRasterLayerTileProgram.id = 0; + if (!outRasterLayerTileProgram.binaryCache.isEmpty()) { - preprocessedVertexShader_UnrolledPerElevationColorMapEntryCode += - detachedOf(vertexShader_perElevationColorMapEntryCode) - .replace("%entryIndex%", QString::number(colorMapEntryIndex)) - .replace("%prevEntryIndex%", QString::number(colorMapEntryIndex - 1)); + outRasterLayerTileProgram.id = getGPUAPI()->linkProgram(0, nullptr, variableLocations, + outRasterLayerTileProgram.binaryCache, outRasterLayerTileProgram.cacheFormat, true, &variablesMap); } - preprocessedVertexShader.replace("%UnrolledPerElevationColorMapEntryCode%", - preprocessedVertexShader_UnrolledPerElevationColorMapEntryCode); - preprocessedVertexShader.replace("%HeixelsPerTileSide%", - QString::number(AtlasMapRenderer::HeixelsPerTileSide - 1)); - preprocessedVertexShader.replace("%MaxElevationColorMapEntriesCount%", - QString::number(ElevationConfiguration::MaxColorMapEntries)); - preprocessedVertexShader.replace("%SlopeAlgorithm_None%", - QString::number(static_cast(ElevationConfiguration::SlopeAlgorithm::None))); - preprocessedVertexShader.replace("%SlopeAlgorithm_ZevenbergenThorne%", - QString::number(static_cast(ElevationConfiguration::SlopeAlgorithm::ZevenbergenThorne))); - preprocessedVertexShader.replace("%SlopeAlgorithm_Horn%", - QString::number(static_cast(ElevationConfiguration::SlopeAlgorithm::Horn))); - preprocessedVertexShader.replace("%VisualizationStyle_None%", - QString::number(static_cast(ElevationConfiguration::VisualizationStyle::None))); - preprocessedVertexShader.replace("%VisualizationStyle_SlopeDegrees%", - QString::number(static_cast(ElevationConfiguration::VisualizationStyle::SlopeDegrees))); - preprocessedVertexShader.replace("%VisualizationStyle_SlopePercents%", - QString::number(static_cast(ElevationConfiguration::VisualizationStyle::SlopePercents))); - preprocessedVertexShader.replace("%VisualizationStyle_HillshadeTraditional%", - QString::number(static_cast(ElevationConfiguration::VisualizationStyle::HillshadeTraditional))); - preprocessedVertexShader.replace("%VisualizationStyle_HillshadeIgor%", - QString::number(static_cast(ElevationConfiguration::VisualizationStyle::HillshadeIgor))); - preprocessedVertexShader.replace("%VisualizationStyle_HillshadeCombined%", - QString::number(static_cast(ElevationConfiguration::VisualizationStyle::HillshadeCombined))); - preprocessedVertexShader.replace("%VisualizationStyle_HillshadeMultidirectional%", - QString::number(static_cast(ElevationConfiguration::VisualizationStyle::HillshadeMultidirectional))); - preprocessedVertexShader.replace("%ElevationVisualizationEnabled%", - QString::number(setupOptions.elevationVisualizationEnabled ? 1 : 0)); - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + if (!outRasterLayerTileProgram.id.isValid()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererMapLayersStage_OpenGL vertex shader for %d raster map layers", - numberOfLayersInBatch); - return false; - } + const auto& vertexShader = QString::fromLatin1( + // Definitions + "#define ELEVATION_VISUALIZATION_ENABLED %ElevationVisualizationEnabled% ""\n" + " ""\n" + // Input data + "INPUT vec2 in_vs_vertexPosition; ""\n" + "INPUT vec2 in_vs_vertexTexCoords; ""\n" + " ""\n" + // Output data to next shader stages + "%UnrolledPerRasterLayerTexCoordsDeclarationCode% ""\n" + "PARAM_OUTPUT float v2f_metersPerUnit; ""\n" + "#if TEXTURE_LOD_SUPPORTED ""\n" + " PARAM_OUTPUT float v2f_mipmapLOD; ""\n" + "#endif // TEXTURE_LOD_SUPPORTED ""\n" + "#if ELEVATION_VISUALIZATION_ENABLED ""\n" + " PARAM_OUTPUT lowp vec4 v2f_elevationColor; ""\n" + "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" + "PARAM_OUTPUT vec4 v2f_position; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + "uniform vec2 param_vs_targetInTilePosN; ""\n" + "uniform float param_vs_tileSize; ""\n" + "#if TEXTURE_LOD_SUPPORTED ""\n" + " uniform float param_vs_distanceFromCameraToTarget; ""\n" + " uniform float param_vs_cameraElevationAngleN; ""\n" + " uniform vec2 param_vs_groundCameraPosition; ""\n" + " uniform float param_vs_scaleToRetainProjectedSize; ""\n" + "#endif // TEXTURE_LOD_SUPPORTED ""\n" + "uniform vec4 param_vs_elevation_configuration; ""\n" + "#if ELEVATION_VISUALIZATION_ENABLED ""\n" + " uniform vec4 param_vs_elevation_hillshadeConfiguration; ""\n" + " uniform vec4 param_vs_elevation_colorMapKeys[%MaxElevationColorMapEntriesCount%]; ""\n" + " uniform vec4 param_vs_elevation_colorMapValues[%MaxElevationColorMapEntriesCount%]; ""\n" + "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" + " ""\n" + // Parameters: per-tile data + "uniform vec2 param_vs_tileCoordsOffset; ""\n" + "uniform vec4 param_vs_elevation_scale; ""\n" + "uniform highp sampler2D param_vs_elevation_dataSampler; ""\n" + " ""\n" + // Parameters: per-layer-in-tile data + "struct VsRasterLayerTile ""\n" + "{ ""\n" + " highp vec4 texCoordsOffsetAndScale; ""\n" + "}; ""\n" + "%UnrolledPerRasterLayerParamsDeclarationCode% ""\n" + "uniform VsRasterLayerTile param_vs_elevationLayer; ""\n" + "uniform highp vec4 param_vs_elevationLayerTexelSize; ""\n" + "uniform highp vec4 param_vs_elevationLayerDataPlace; ""\n" + " ""\n" + "float interpolatedHeight(in vec2 inTexCoords) ""\n" + "{ ""\n" + " vec2 heixelSize = param_vs_elevationLayerDataPlace.zw * 2.0; ""\n" + " vec2 texCoords = (inTexCoords - param_vs_elevationLayerDataPlace.zw) / heixelSize; ""\n" + " vec2 pixOffset = fract(texCoords); ""\n" + " texCoords = floor(texCoords) * heixelSize + param_vs_elevationLayerDataPlace.zw; ""\n" + " vec2 minCoords = param_vs_elevationLayerDataPlace.xy - heixelSize; ""\n" + " vec2 maxCoords = minCoords + heixelSize * (%HeixelsPerTileSide%.0 + 2.0); ""\n" + " float blHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" + " texCoords.x += heixelSize.x; ""\n" + " float brHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" + " texCoords.y += heixelSize.y; ""\n" + " float trHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" + " texCoords.x -= heixelSize.x; ""\n" + " float tlHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" + " float avbPixel = mix(blHeixel, brHeixel, pixOffset.x); ""\n" + " float avtPixel = mix(tlHeixel, trHeixel, pixOffset.x); ""\n" + " return mix(avbPixel, avtPixel, pixOffset.y); ""\n" + "} ""\n" + " ""\n" + "void calculateTextureCoordinates(in VsRasterLayerTile tileLayer, out vec2 outTexCoords) ""\n" + "{ ""\n" + " vec2 texCoords = in_vs_vertexTexCoords; ""\n" + " texCoords = texCoords * tileLayer.texCoordsOffsetAndScale.zw + tileLayer.texCoordsOffsetAndScale.xy; ""\n" + " outTexCoords = texCoords; ""\n" + "} ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec4 v = vec4(in_vs_vertexPosition.x, 0.0, in_vs_vertexPosition.y, 1.0); ""\n" + " ""\n" + // Scale and shift vertex to it's proper position + " v.xz *= param_vs_tileSize; ""\n" + " v.xz += param_vs_tileSize * (param_vs_tileCoordsOffset - param_vs_targetInTilePosN); ""\n" + " ""\n" + // Get meters per unit, which is needed at both shader stages + " v2f_metersPerUnit = mix(param_vs_elevation_scale.x, param_vs_elevation_scale.y, in_vs_vertexTexCoords.t); ""\n" + " ""\n" + // Process each tile layer texture coordinates (except elevation) + "%UnrolledPerRasterLayerTexCoordsProcessingCode% ""\n" + " ""\n" + // If elevation data is active, use it + "#if ELEVATION_VISUALIZATION_ENABLED ""\n" + " v2f_elevationColor = vec4(0.0, 0.0, 0.0, 0.0); ""\n" + "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" + " if (abs(param_vs_elevation_scale.w) > 0.0) ""\n" + " { ""\n" + " float slopeAlgorithm = param_vs_elevation_configuration.x; ""\n" + " ""\n" + // [0][0] - TL (0); [1][0] - T (1); [2][0] - TR (2) + // [0][1] - L (3); [1][1] - O (4); [2][1] - R (5) + // [0][2] - BL (6); [1][2] - B (7); [2][2] - BR (8) + " mat3 heightInMeters = mat3(0.0); ""\n" + " vec2 elevationTexCoordsO; ""\n" + " calculateTextureCoordinates( ""\n" + " param_vs_elevationLayer, ""\n" + " elevationTexCoordsO); ""\n" + " heightInMeters[1][1] = interpolatedHeight(elevationTexCoordsO); ""\n" + " ""\n" + "#if ELEVATION_VISUALIZATION_ENABLED ""\n" + " if (slopeAlgorithm > %SlopeAlgorithm_None%.0) ""\n" + " { ""\n" + " vec2 elevationTexCoordsT = elevationTexCoordsO; ""\n" + " elevationTexCoordsT.t -= param_vs_elevationLayerTexelSize.y; ""\n" + " heightInMeters[1][0] = interpolatedHeight(elevationTexCoordsT); ""\n" + " ""\n" + " vec2 elevationTexCoordsL = elevationTexCoordsO; ""\n" + " elevationTexCoordsL.s -= param_vs_elevationLayerTexelSize.x; ""\n" + " heightInMeters[0][1] = interpolatedHeight(elevationTexCoordsL); ""\n" + " ""\n" + " vec2 elevationTexCoordsB = elevationTexCoordsO; ""\n" + " elevationTexCoordsB.t += param_vs_elevationLayerTexelSize.y; ""\n" + " heightInMeters[1][2] = interpolatedHeight(elevationTexCoordsB); ""\n" + " ""\n" + " vec2 elevationTexCoordsR = elevationTexCoordsO; ""\n" + " elevationTexCoordsR.s += param_vs_elevationLayerTexelSize.x; ""\n" + " heightInMeters[2][1] = interpolatedHeight(elevationTexCoordsR); ""\n" + " ""\n" + " if (slopeAlgorithm > %SlopeAlgorithm_ZevenbergenThorne%.0) ""\n" + " { ""\n" + " vec2 elevationTexCoordsTL = elevationTexCoordsO; ""\n" + " elevationTexCoordsTL.s -= param_vs_elevationLayerTexelSize.x; ""\n" + " elevationTexCoordsTL.t -= param_vs_elevationLayerTexelSize.y; ""\n" + " heightInMeters[0][0] = interpolatedHeight(elevationTexCoordsTL); ""\n" + " ""\n" + " vec2 elevationTexCoordsTR = elevationTexCoordsO; ""\n" + " elevationTexCoordsTR.s += param_vs_elevationLayerTexelSize.x; ""\n" + " elevationTexCoordsTR.t -= param_vs_elevationLayerTexelSize.y; ""\n" + " heightInMeters[2][0] = interpolatedHeight(elevationTexCoordsTR); ""\n" + " ""\n" + " vec2 elevationTexCoordsBL = elevationTexCoordsO; ""\n" + " elevationTexCoordsBL.s -= param_vs_elevationLayerTexelSize.x; ""\n" + " elevationTexCoordsBL.t += param_vs_elevationLayerTexelSize.y; ""\n" + " heightInMeters[0][2] = interpolatedHeight(elevationTexCoordsBL); ""\n" + " ""\n" + " vec2 elevationTexCoordsBR = elevationTexCoordsO; ""\n" + " elevationTexCoordsBR.s += param_vs_elevationLayerTexelSize.x; ""\n" + " elevationTexCoordsBR.t += param_vs_elevationLayerTexelSize.y; ""\n" + " heightInMeters[2][2] = interpolatedHeight(elevationTexCoordsBR); ""\n" + " } ""\n" + " } ""\n" + "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" + " heightInMeters *= param_vs_elevation_scale.w * param_vs_elevation_scale.z; ""\n" + " ""\n" + "#if ELEVATION_VISUALIZATION_ENABLED ""\n" + " float visualizationStyle = param_vs_elevation_configuration.y; ""\n" + " if (visualizationStyle > %VisualizationStyle_None%.0) ""\n" + " { ""\n" + " const float M_PI = 3.1415926535897932384626433832795; ""\n" + " const float M_PI_2 = M_PI / 2.0; ""\n" + " const float M_2PI = 2.0 * M_PI; ""\n" + " const float M_3PI_2 = 3.0 * M_PI_2; ""\n" + " const float M_COS_225_D = -0.70710678118; ""\n" + " ""\n" + " float heixelInMeters = v2f_metersPerUnit * (param_vs_tileSize / %HeixelsPerTileSide%.0); ""\n" + " ""\n" + " vec2 slopeInMeters = vec2(0.0); ""\n" + " float slopeAlgorithmScale = 1.0; ""\n" + " if (abs(slopeAlgorithm - %SlopeAlgorithm_ZevenbergenThorne%.0) < 0.00001) ""\n" + " { ""\n" + " slopeInMeters.x = heightInMeters[0][1] - heightInMeters[2][1]; ""\n" + " slopeInMeters.y = heightInMeters[1][2] - heightInMeters[1][0]; ""\n" + " ""\n" + " slopeAlgorithmScale = 2.0; ""\n" + " } ""\n" + " else if (abs(slopeAlgorithm - %SlopeAlgorithm_Horn%.0) < 0.00001) ""\n" + " { ""\n" + " slopeInMeters.x = ( ""\n" + " heightInMeters[0][0] + heightInMeters[0][1] + heightInMeters[0][1] + heightInMeters[0][2] ""\n" + " ); ""\n" + " slopeInMeters.x -= ( ""\n" + " heightInMeters[2][0] + heightInMeters[2][1] + heightInMeters[2][1] + heightInMeters[2][2] ""\n" + " ); ""\n" + " ""\n" + " slopeInMeters.y = ( ""\n" + " heightInMeters[0][2] + heightInMeters[1][2] + heightInMeters[1][2] + heightInMeters[2][2] ""\n" + " ); ""\n" + " slopeInMeters.y -= ( ""\n" + " heightInMeters[0][0] + heightInMeters[1][0] + heightInMeters[1][0] + heightInMeters[2][0] ""\n" + " ); ""\n" + " ""\n" + " slopeAlgorithmScale = 8.0; ""\n" + " } ""\n" + " highp float slope_XX = slopeInMeters.x*slopeInMeters.x; ""\n" + " highp float slope_YY = slopeInMeters.y*slopeInMeters.y; ""\n" + " highp float slope_XXpYY = slope_XX + slope_YY; ""\n" + " ""\n" + " float zFactor = param_vs_elevation_configuration.w; ""\n" + " float zFactorN = zFactor / (slopeAlgorithmScale * heixelInMeters); ""\n" + " float zFactorNSq = zFactorN * zFactorN; ""\n" + " float sunZenith = param_vs_elevation_hillshadeConfiguration.x; ""\n" + " float zNCosZenith = zFactorN * cos(sunZenith); ""\n" + " ""\n" + " float colorMapKey_0 = param_vs_elevation_colorMapKeys[0].w; ""\n" + " float colorMapKey = colorMapKey_0 - 1.0; ""\n" + " if (abs(visualizationStyle - %VisualizationStyle_SlopeDegrees%.0) < 0.00001) ""\n" + " { ""\n" + " vec2 slopeN = slopeInMeters / (slopeAlgorithmScale * heixelInMeters); ""\n" + " float slopeN_XXpYY = slopeN.x*slopeN.x + slopeN.y*slopeN.y; ""\n" + " colorMapKey = degrees(atan(zFactor * sqrt(slopeN_XXpYY))); ""\n" + " colorMapKey = max(colorMapKey, colorMapKey_0); ""\n" + " } ""\n" + " else if (abs(visualizationStyle - %VisualizationStyle_SlopePercents%.0) < 0.00001) ""\n" + " { ""\n" + " vec2 slopeN = slopeInMeters / (slopeAlgorithmScale * heixelInMeters); ""\n" + " float slopeN_XXpYY = slopeN.x*slopeN.x + slopeN.y*slopeN.y; ""\n" + " colorMapKey = 100.0 * zFactor * sqrt(slopeN_XXpYY); ""\n" + " colorMapKey = max(colorMapKey, colorMapKey_0); ""\n" + " } ""\n" + " else if (abs(visualizationStyle - %VisualizationStyle_HillshadeMultidirectional%.0) < 0.00001) ""\n" + " { ""\n" + // NOTE: See http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf + " float sinZenith = sin(sunZenith); ""\n" + " ""\n" + " if (slope_XXpYY < 0.00001) ""\n" + " { ""\n" + " float value = 1.0 + 254.0 * sinZenith; ""\n" + " colorMapKey = max(value, colorMapKey_0); ""\n" + " } ""\n" + " else ""\n" + " { ""\n" + " highp float weight_225 = 0.5 * slope_XXpYY - slopeInMeters.x * slopeInMeters.y; ""\n" + " highp float weight_270 = slope_XX; ""\n" + " highp float weight_315 = slope_XXpYY - weight_225; ""\n" + " highp float weight_360 = slope_YY; ""\n" + " ""\n" + " float value_225 = sinZenith - (slopeInMeters.x - slopeInMeters.y) * M_COS_225_D * zNCosZenith; ""\n" + " float value_270 = sinZenith - slopeInMeters.x * zNCosZenith; ""\n" + " float value_315 = sinZenith + (slopeInMeters.x + slopeInMeters.y) * M_COS_225_D * zNCosZenith; ""\n" + " float value_360 = sinZenith - slopeInMeters.y * zNCosZenith; ""\n" + " ""\n" + " float value = weight_225 * max(value_225, 0.0); ""\n" + " value += weight_270 * max(value_270, 0.0); ""\n" + " value += weight_315 * max(value_315, 0.0); ""\n" + " value += weight_360 * max(value_360, 0.0); ""\n" + " value *= 127.0; ""\n" + " value /= slope_XXpYY; ""\n" + " value /= sqrt(1.0 + zFactorNSq * slope_XXpYY); ""\n" + " value = 1.0 + value; ""\n" + " colorMapKey = max(value, colorMapKey_0); ""\n" + " } ""\n" + " } ""\n" + " else ""\n" + " { ""\n" + " float sunAzimuth = M_PI - param_vs_elevation_hillshadeConfiguration.y; ""\n" + " ""\n" + " float zNCosZenithCosAzimuth = zNCosZenith * cos(sunAzimuth); ""\n" + " float zNCosZenithSinAzimuth = zNCosZenith * sin(sunAzimuth); ""\n" + " ""\n" + " if (abs(visualizationStyle - %VisualizationStyle_HillshadeTraditional%.0) < 0.00001) ""\n" + " { ""\n" + " float value = sin(sunZenith); ""\n" + " value -= slopeInMeters.y*zNCosZenithCosAzimuth - slopeInMeters.x*zNCosZenithSinAzimuth; ""\n" + " value = 254.0 * value / sqrt(1.0 + zFactorNSq * slope_XXpYY); ""\n" + " value = 1.0 + max(value, 0.0); ""\n" + " colorMapKey = max(value, colorMapKey_0); ""\n" + " } ""\n" + " else if (abs(visualizationStyle - %VisualizationStyle_HillshadeIgor%.0) < 0.00001) ""\n" + " { ""\n" + " float slopeAngle = atan(sqrt(slope_XXpYY) * zFactorN); ""\n" + " float aspect = atan(slopeInMeters.y, slopeInMeters.x); ""\n" + " float slopeStrength = slopeAngle / M_PI_2; ""\n" + " float aspectN = mod(M_2PI + aspect, M_2PI); ""\n" + " float wAzimuthN = mod(M_2PI + mod(M_3PI_2 - sunAzimuth, M_2PI), M_2PI); ""\n" + " float aspectDiffWAzimuth = mod(abs(aspectN - wAzimuthN), M_PI); ""\n" + " float value = 255.0 * (1.0 - slopeStrength * (1.0 - aspectDiffWAzimuth / M_PI)); ""\n" + " colorMapKey = max(value, colorMapKey_0); ""\n" + " } ""\n" + " else if (abs(visualizationStyle - %VisualizationStyle_HillshadeCombined%.0) < 0.00001) ""\n" + " { ""\n" + // NOTE: First part produces same result as HillshadeTraditional except for mapping to 1..255 scale (oblique shading) + " float value = sin(sunZenith); ""\n" + " value -= slopeInMeters.y*zNCosZenithCosAzimuth - slopeInMeters.x*zNCosZenithSinAzimuth; ""\n" + " value /= sqrt(1.0 + zFactorNSq * slope_XXpYY); ""\n" + " ""\n" + // NOTE: Combine with slope shading + // NOTE: Implemented differently that in GDAL, see source that https://trac.osgeo.org/gdal/ticket/4753 references + " vec2 slopeN = slopeInMeters / (slopeAlgorithmScale * heixelInMeters); ""\n" + " float slopeN_XXpYY = slopeN.x*slopeN.x + slopeN.y*slopeN.y; ""\n" + " value *= 1.0 - abs(atan(zFactor * sqrt(slopeN_XXpYY)) / M_PI); ""\n" + " ""\n" + " value = 1.0 + 254.0 * max(value, 0.0); ""\n" + " colorMapKey = max(value, colorMapKey_0); ""\n" + " } ""\n" + " } ""\n" + " ""\n" + " if (colorMapKey >= colorMapKey_0) ""\n" + " { ""\n" + " float colorMapEntryKeyA = colorMapKey_0; ""\n" + " float colorMapEntryKeyB = colorMapKey; ""\n" + " vec4 colorMapEntryValueA = vec4(0.0); ""\n" + " vec4 colorMapEntryValueB = vec4(0.0); ""\n" + " ""\n" + "%UnrolledPerElevationColorMapEntryCode% ""\n" + " ""\n" + " float interpolation = (colorMapKey - colorMapEntryKeyA) / (colorMapEntryKeyB - colorMapEntryKeyA); ""\n" + " v2f_elevationColor = mix( ""\n" + " colorMapEntryValueA, ""\n" + " colorMapEntryValueB, ""\n" + " clamp(interpolation, 0.0, 1.0)); ""\n" + " } ""\n" + " v2f_elevationColor.a *= param_vs_elevation_configuration.z; ""\n" + " } ""\n" + "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" + " v.y = heightInMeters[1][1] / v2f_metersPerUnit; ""\n" + " } ""\n" + " ""\n" + "#if TEXTURE_LOD_SUPPORTED ""\n" + // Calculate mipmap LOD + " vec2 groundVertex = v.xz; ""\n" + " vec2 groundCameraToVertex = groundVertex - param_vs_groundCameraPosition; ""\n" + " float mipmapK = log(1.0 + 10.0 * log2(1.0 + param_vs_cameraElevationAngleN)); ""\n" + " float mipmapBaseLevelEndDistance = mipmapK * param_vs_distanceFromCameraToTarget; ""\n" + " v2f_mipmapLOD = 1.0 + (length(groundCameraToVertex) - mipmapBaseLevelEndDistance) ""\n" + " / (param_vs_scaleToRetainProjectedSize * param_vs_tileSize); ""\n" + "#endif // TEXTURE_LOD_SUPPORTED ""\n" + " ""\n" + // Finally output processed modified vertex + " v2f_position = v; ""\n" + " v = param_vs_mPerspectiveProjectionView * v; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + "} ""\n"); + const auto& vertexShader_perRasterLayerTexCoordsDeclaration = QString::fromLatin1( + "PARAM_OUTPUT vec2 v2f_texCoordsPerLayer_%rasterLayerIndex%; ""\n"); + const auto& vertexShader_perRasterLayerParamsDeclaration = QString::fromLatin1( + "uniform VsRasterLayerTile param_vs_rasterTileLayer_%rasterLayerIndex%; ""\n"); + const auto& vertexShader_perRasterLayerTexCoordsProcessing = QString::fromLatin1( + " calculateTextureCoordinates( ""\n" + " param_vs_rasterTileLayer_%rasterLayerIndex%, ""\n" + " v2f_texCoordsPerLayer_%rasterLayerIndex%); ""\n" + " ""\n"); + const auto& vertexShader_perElevationColorMapEntryCode = QString::fromLatin1( + " float colorMapKey_%entryIndex% = param_vs_elevation_colorMapKeys[%entryIndex%].w; ""\n" + " if (colorMapKey_%entryIndex% > colorMapKey_%prevEntryIndex%) ""\n" + " { ""\n" + " if (colorMapKey >= colorMapKey_%prevEntryIndex%) ""\n" + " { ""\n" + " colorMapEntryKeyA = colorMapKey_%prevEntryIndex%; ""\n" + " colorMapEntryValueA = param_vs_elevation_colorMapValues[%prevEntryIndex%]; ""\n" + " colorMapEntryKeyB = colorMapKey_%entryIndex%; ""\n" + " colorMapEntryValueB = param_vs_elevation_colorMapValues[%entryIndex%]; ""\n" + " } ""\n" + " } ""\n" + " ""\n"); + + const auto& fragmentShader = QString::fromLatin1( + // Definitions + "#define ELEVATION_VISUALIZATION_ENABLED %ElevationVisualizationEnabled% ""\n" + " ""\n" + // Input data + "%UnrolledPerRasterLayerTexCoordsDeclarationCode% ""\n" + "PARAM_INPUT float v2f_metersPerUnit; ""\n" + "#if TEXTURE_LOD_SUPPORTED ""\n" + " PARAM_INPUT float v2f_mipmapLOD; ""\n" + "#endif // TEXTURE_LOD_SUPPORTED ""\n" + "#if ELEVATION_VISUALIZATION_ENABLED ""\n" + " PARAM_INPUT lowp vec4 v2f_elevationColor; ""\n" + "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" + "PARAM_INPUT vec4 v2f_position; ""\n" + " ""\n" + // Parameters: common data + "uniform lowp float param_fs_lastBatch; ""\n" + "uniform lowp float param_fs_blendingEnabled; ""\n" + "uniform lowp vec4 param_fs_backgroundColor; ""\n" + "uniform lowp vec4 param_fs_myLocationColor; ""\n" + "uniform vec4 param_fs_myLocation; ""\n" + "uniform vec2 param_fs_myDirection; ""\n" + "uniform vec4 param_fs_worldCameraPosition; ""\n" + "uniform vec4 param_fs_mistConfiguration; ""\n" + "uniform vec4 param_fs_mistColor; ""\n" + // Parameters: per-layer data + "struct FsRasterLayerTile ""\n" + "{ ""\n" + " lowp float isPremultipliedAlpha; ""\n" + " lowp float opacityFactor; ""\n" + " highp vec4 texCoordsOffsetAndScale; ""\n" + " highp vec4 transitionPhase; ""\n" + " highp float texelSize; ""\n" + " lowp sampler2D sampler; ""\n" + "}; ""\n" + "%UnrolledPerRasterLayerParamsDeclarationCode% ""\n" + " ""\n" + "void addExtraAlpha(inout lowp vec4 color, in lowp float alpha, in lowp float isPremultipliedAlpha) ""\n" + "{ ""\n" + " lowp float colorAlpha = 1.0 - isPremultipliedAlpha + isPremultipliedAlpha * alpha; ""\n" + " color *= vec4(colorAlpha, colorAlpha, colorAlpha, alpha); ""\n" + "} ""\n" + " ""\n" + "void mixColors(inout lowp vec4 destColor, in lowp vec4 srcColor) ""\n" + "{ ""\n" + " destColor = destColor * (1.0 - srcColor.a) + srcColor * vec4(srcColor.a, srcColor.a, srcColor.a, 1.0); ""\n" + "} ""\n" + " ""\n" + "void mixColors(inout lowp vec4 destColor, in lowp vec4 srcColor, in lowp float isPremultipliedAlpha) ""\n" + "{ ""\n" + " lowp float srcColorMultiplier = ""\n" + " isPremultipliedAlpha + (1.0 - isPremultipliedAlpha) * srcColor.a; ""\n" + " destColor *= 1.0 - srcColor.a; ""\n" + " destColor += srcColor * vec4(srcColorMultiplier, srcColorMultiplier, srcColorMultiplier, 1.0); ""\n" + "} ""\n" + " ""\n" + "void fromTexture(in lowp sampler2D sampler, in vec2 coords, out lowp vec4 destColor) ""\n" + "{ ""\n" + "#if TEXTURE_LOD_SUPPORTED ""\n" + " destColor = SAMPLE_TEXTURE_2D_LOD(sampler, coords, v2f_mipmapLOD); ""\n" + "#else // !TEXTURE_LOD_SUPPORTED ""\n" + " destColor = SAMPLE_TEXTURE_2D(sampler, coords); ""\n" + "#endif // TEXTURE_LOD_SUPPORTED ""\n" + "} ""\n" + " ""\n" + "void getWind(in lowp sampler2D sampler, in vec2 coords, out vec2 windVector) ""\n" + "{ ""\n" + " lowp vec4 windColor; ""\n" + " fromTexture(sampler, coords, windColor); ""\n" + " vec2 result = (windColor.rb + windColor.ga * 255.0 - 8.0) * 10.0; ""\n" + " windVector = vec2(result.x, -result.y); ""\n" + "} ""\n" + " ""\n" + "float getRandPixel(in vec2 pixCoords, in vec2 tileSize, in float normSeed) ""\n" + "{ ""\n" + " vec2 intCoords = floor(pixCoords); ""\n" + " float randValue = abs(fract(sin(dot(intCoords / tileSize + normSeed, vec2(12.9898, 78.233))) * 43758.5453)); ""\n" + " return randValue > 0.98 ? 1.0 : 0.0; ""\n" + "} ""\n" + " ""\n" + "void getParticleColor(in vec2 pixCoords, in vec2 windVector, in vec2 tileSize, in float seed, out lowp vec4 color) ""\n" + "{ ""\n" + " float normSeed = seed * 0.001; ""\n" + " float windMagnitude = length(windVector); ""\n" + " vec2 shift = windMagnitude >= 0.5 ? windVector / windMagnitude : vec2(0.0); ""\n" + // Rescale two times: to get out of tile data without overlap (- 0.1 / 0.8) and to access full texture (/ 0.5) + " vec2 coords = (pixCoords - 0.05) * tileSize / 0.4; ""\n" + " vec2 center = coords + shift; ""\n" + " float nextPixel = 0.5 * getRandPixel(center, tileSize, normSeed); ""\n" + " float prevPixel = getRandPixel(coords - shift, tileSize, normSeed); ""\n" + " center = prevPixel > 0.0 ? coords - shift : center; ""\n" + " float actPixel = 0.8 * getRandPixel(coords, tileSize, normSeed); ""\n" + " center = actPixel > 0.0 ? coords : center; ""\n" + " float alpha = max(max(actPixel, nextPixel), prevPixel); ""\n" + " vec2 norm = vec2(shift.y, -shift.x); ""\n" + " alpha *= clamp(1.0 - 2.0 * abs(dot(norm, coords) - dot(norm, floor(center) + 0.5)), 0.0, 1.0); ""\n" + " color = vec4(1.0, 1.0, 1.0, alpha); ""\n" + "} ""\n" + " ""\n" + "void getTextureColor(in vec4 texCoordsOffsetAndScale, in vec4 transitionPhase, in float texelSize, ""\n" + " in sampler2D sampler, in vec2 texCoords, out lowp vec4 resultColor) ""\n" + "{ ""\n" + " if (transitionPhase.x >= 0.0) ""\n" + " { ""\n" + // Animate transition between textures + " vec2 leftCoords = texCoords; ""\n" + " vec2 rightCoords; ""\n" + " lowp vec4 windColor = vec4(0.0); ""\n" + " float halfTexelSize = texelSize / 2.0; ""\n" + // Scale two times: to get into tile data without overlap (* 0.8 + 0.1) and to access quadrants (* 0.5) + " leftCoords = leftCoords * 0.4 + 0.05; ""\n" + " leftCoords.x = clamp(leftCoords.x, halfTexelSize, 0.5 - halfTexelSize); ""\n" + " leftCoords.y = clamp(leftCoords.y, halfTexelSize, 0.5 - halfTexelSize); ""\n" + " rightCoords = leftCoords + 0.5; ""\n" + " vec2 rightWind; ""\n" + " getWind(sampler, rightCoords, rightWind); ""\n" + // Scale two times (* 0.8 and * 0.5) and then divide to TileSize3D (/ 100) + " float factor = transitionPhase.y / (v2f_metersPerUnit * 250.0); ""\n" + " vec2 texShift = rightWind * texCoordsOffsetAndScale.zw * factor; ""\n" + " if (transitionPhase.z >= 0.0) ""\n" + " { ""\n" + // Animate wind particles + " float seed = floor(transitionPhase.z) / 10.0; ""\n" + " float shift = floor(texCoordsOffsetAndScale.y * 10.0) / 100.0; ""\n" + " shift += floor(texCoordsOffsetAndScale.x * 10.0) / 1000.0; ""\n" + " float phase = fract(transitionPhase.z); ""\n" + " vec2 tileSize = transitionPhase.w / texCoordsOffsetAndScale.zw; ""\n" + " rightCoords.x -= 0.5; ""\n" + " vec2 leftWind; ""\n" + " getWind(sampler, rightCoords, leftWind); ""\n" + " vec2 windVector = mix(leftWind, rightWind, transitionPhase.x); ""\n" + " vec2 windShift = windVector * texCoordsOffsetAndScale.zw * factor; ""\n" + " vec2 leftShift = windShift * phase; ""\n" + " vec2 rightShift = windShift * (1.0 - phase); ""\n" + " lowp vec4 tmpColor; ""\n" + " lowp vec4 leftColor; ""\n" + " lowp vec4 rightColor; ""\n" + " getParticleColor(leftCoords - leftShift, windVector, tileSize, fract(seed) + shift, leftColor); ""\n" + " getParticleColor(leftCoords - leftShift, windVector, tileSize, fract(seed + 0.1) + shift, tmpColor); ""\n" + " leftColor = mix(tmpColor, leftColor, clamp(phase + 0.5, 0.0, 1.0)); ""\n" + " getParticleColor(leftCoords + rightShift, windVector, tileSize, fract(seed + 0.1) + shift, rightColor);""\n" + " getParticleColor(leftCoords + rightShift, windVector, tileSize, fract(seed + 0.2) + shift, tmpColor); ""\n" + " rightColor = mix(rightColor, tmpColor, clamp(phase - 0.5, 0.0, 1.0)); ""\n" + " windColor = mix(leftColor, rightColor, phase); ""\n" + " } ""\n" + " rightCoords = leftCoords; ""\n" + " rightCoords.x += 0.5; ""\n" + " leftCoords -= texShift * transitionPhase.x; ""\n" + " leftCoords.x = clamp(leftCoords.x, halfTexelSize, 0.5 - halfTexelSize); ""\n" + " leftCoords.y = clamp(leftCoords.y, halfTexelSize, 0.5 - halfTexelSize); ""\n" + " rightCoords += texShift * (1.0 - transitionPhase.x); ""\n" + " rightCoords.x = clamp(rightCoords.x, 0.5 + halfTexelSize, 1.0 - halfTexelSize); ""\n" + " rightCoords.y = clamp(rightCoords.y, halfTexelSize, 0.5 - halfTexelSize); ""\n" + " lowp vec4 texColorLeft; ""\n" + " lowp vec4 texColorRight; ""\n" + " fromTexture(sampler, leftCoords, texColorLeft); ""\n" + " fromTexture(sampler, rightCoords, texColorRight); ""\n" + " lowp vec4 texColor = mix(texColorLeft, texColorRight, clamp(transitionPhase.x, 0.0, 1.0)); ""\n" + " resultColor = vec4(mix(texColor.rgb, windColor.rgb, windColor.a), texColor.a); ""\n" + " } ""\n" + " else ""\n" + " { ""\n" + " fromTexture(sampler, texCoords, resultColor); ""\n" + " } ""\n" + "} ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " lowp vec4 finalColor; ""\n" + " ""\n" + // Calculate color of accuracy circle + " lowp vec4 circle = param_fs_myLocationColor; ""\n" + " vec2 vMyToPos = v2f_position.xz - param_fs_myLocation.xy; ""\n" + " float dist = length(vMyToPos); ""\n" + " circle.a = dist > param_fs_myLocation.z ? 0.0 : circle.a * (dist + 0.05 > param_fs_myLocation.z ? 2.0 : 1.0); ""\n" + " ""\n" + // Calculate color of heading sector + " lowp vec4 sector = param_fs_myLocationColor; ""\n" + " float fdir = dot(vec2(sin(param_fs_myDirection.x), -cos(param_fs_myDirection.x)), vMyToPos / dist); ""\n" + " sector.a = dist >= param_fs_myDirection.y ? 0.0 : (fdir < 0.7071 ? 0.0 : 1.0 - dist / param_fs_myDirection.y); ""\n" + " ""\n" + // Calculate mist color + " lowp vec4 mistColor = param_fs_mistColor; ""\n" + " vec4 infrontPosition = v2f_position; ""\n" + " infrontPosition.xz = v2f_position.xz * param_fs_mistConfiguration.xy; ""\n" + " infrontPosition.xz = v2f_position.xz - (infrontPosition.x + infrontPosition.z) * param_fs_mistConfiguration.xy;""\n" + " float toFog = param_fs_mistConfiguration.z - distance(infrontPosition, param_fs_worldCameraPosition); ""\n" + " float expScale = (3.0 - param_fs_mistColor.w ) / param_fs_mistConfiguration.w; ""\n" + " float expOffset = 2.354 - param_fs_mistColor.w * 0.5; ""\n" + " mistColor.a = clamp(1.0 - 1.0 / exp(pow(max(0.0, expOffset - toFog * expScale), 2.0)), 0.0, 1.0); ""\n" + " ""\n" + // Mix colors of all layers. + // First layer is processed unconditionally, as well as its color is converted to premultiplied alpha. + " getTextureColor(param_fs_rasterTileLayer_0.texCoordsOffsetAndScale, param_fs_rasterTileLayer_0.transitionPhase,""\n" + " param_fs_rasterTileLayer_0.texelSize, param_fs_rasterTileLayer_0.sampler, ""\n" + " v2f_texCoordsPerLayer_0, finalColor); ""\n" + " addExtraAlpha(finalColor, param_fs_rasterTileLayer_0.opacityFactor, ""\n" + " param_fs_rasterTileLayer_0.isPremultipliedAlpha); ""\n" + " lowp float firstLayerColorFactor = param_fs_rasterTileLayer_0.isPremultipliedAlpha + ""\n" + " (1.0 - param_fs_rasterTileLayer_0.isPremultipliedAlpha) * finalColor.a; ""\n" + " finalColor *= vec4(firstLayerColorFactor, firstLayerColorFactor, firstLayerColorFactor, 1.0); ""\n" + " ""\n" + "%UnrolledPerRasterLayerProcessingCode% ""\n" + " ""\n" + "#if ELEVATION_VISUALIZATION_ENABLED ""\n" + " mixColors(finalColor, v2f_elevationColor * param_fs_lastBatch); ""\n" + "#endif // ELEVATION_VISUALIZATION_ENABLED ""\n" + " ""\n" +#if 0 + // NOTE: Useful for debugging mipmap levels + " { ""\n" + " vec4 mipmapDebugColor; ""\n" + " mipmapDebugColor.a = 1.0; ""\n" + " float value = v2f_mipmapLOD; ""\n" + //" float value = textureQueryLod(param_vs_rasterTileLayer[0].sampler, v2f_texCoordsPerLayer[0]).x; ""\n" + " mipmapDebugColor.r = clamp(value, 0.0, 1.0); ""\n" + " value -= 1.0; ""\n" + " mipmapDebugColor.g = clamp(value, 0.0, 1.0); ""\n" + " value -= 1.0; ""\n" + " mipmapDebugColor.b = clamp(value, 0.0, 1.0); ""\n" + " finalColor = mix(finalColor, mipmapDebugColor, 0.5); ""\n" + " } ""\n" +#endif + " ""\n" + " mixColors(finalColor, circle * param_fs_lastBatch); ""\n" + " mixColors(finalColor, sector * param_fs_lastBatch); ""\n" + " mixColors(finalColor, mistColor * param_fs_lastBatch); ""\n" + " lowp vec4 overColor = mix(param_fs_backgroundColor, finalColor, finalColor.a); ""\n" + " FRAGMENT_COLOR_OUTPUT = mix(overColor, finalColor, param_fs_blendingEnabled); ""\n" + "} ""\n"); + const auto& fragmentShader_perRasterLayer = QString::fromLatin1( + " { ""\n" + " lowp vec4 tc; ""\n" + " getTextureColor(param_fs_rasterTileLayer_%rasterLayerIndex%.texCoordsOffsetAndScale, ""\n" + " param_fs_rasterTileLayer_%rasterLayerIndex%.transitionPhase, ""\n" + " param_fs_rasterTileLayer_%rasterLayerIndex%.texelSize, ""\n" + " param_fs_rasterTileLayer_%rasterLayerIndex%.sampler, v2f_texCoordsPerLayer_%rasterLayerIndex%, tc); ""\n" + " addExtraAlpha(tc, param_fs_rasterTileLayer_%rasterLayerIndex%.opacityFactor, ""\n" + " param_fs_rasterTileLayer_%rasterLayerIndex%.isPremultipliedAlpha); ""\n" + " mixColors(finalColor, tc, param_fs_rasterTileLayer_%rasterLayerIndex%.isPremultipliedAlpha); ""\n" + " } ""\n"); + const auto& fragmentShader_perRasterLayerTexCoordsDeclaration = QString::fromLatin1( + "PARAM_INPUT vec2 v2f_texCoordsPerLayer_%rasterLayerIndex%; ""\n"); + const auto& fragmentShader_perRasterLayerParamsDeclaration = QString::fromLatin1( + "uniform FsRasterLayerTile param_fs_rasterTileLayer_%rasterLayerIndex%; ""\n"); + + // Compile vertex shader + auto preprocessedVertexShader = vertexShader; + QString preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsProcessingCode; + QString preprocessedVertexShader_UnrolledPerRasterLayerParamsDeclarationCode; + QString preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsDeclarationCode; + for (auto layerIndex = 0u; layerIndex < numberOfLayersInBatch; layerIndex++) + { + preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsProcessingCode += + detachedOf(vertexShader_perRasterLayerTexCoordsProcessing) + .replace("%rasterLayerIndex%", QString::number(layerIndex)); - // Compile fragment shader - auto preprocessedFragmentShader = fragmentShader; - QString preprocessedFragmentShader_UnrolledPerRasterLayerTexCoordsDeclarationCode; - QString preprocessedFragmentShader_UnrolledPerRasterLayerParamsDeclarationCode; - QString preprocessedFragmentShader_UnrolledPerRasterLayerProcessingCode; - for (auto layerIndex = 0u; layerIndex < numberOfLayersInBatch; layerIndex++) - { - preprocessedFragmentShader_UnrolledPerRasterLayerTexCoordsDeclarationCode += - detachedOf(fragmentShader_perRasterLayerTexCoordsDeclaration) - .replace("%rasterLayerIndex%", QString::number(layerIndex)); + preprocessedVertexShader_UnrolledPerRasterLayerParamsDeclarationCode += + detachedOf(vertexShader_perRasterLayerParamsDeclaration) + .replace("%rasterLayerIndex%", QString::number(layerIndex)); - preprocessedFragmentShader_UnrolledPerRasterLayerParamsDeclarationCode += - detachedOf(fragmentShader_perRasterLayerParamsDeclaration) - .replace("%rasterLayerIndex%", QString::number(layerIndex)); + preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsDeclarationCode += + detachedOf(vertexShader_perRasterLayerTexCoordsDeclaration) + .replace("%rasterLayerIndex%", QString::number(layerIndex)); + } + preprocessedVertexShader.replace("%UnrolledPerRasterLayerTexCoordsProcessingCode%", + preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsProcessingCode); + preprocessedVertexShader.replace("%UnrolledPerRasterLayerParamsDeclarationCode%", + preprocessedVertexShader_UnrolledPerRasterLayerParamsDeclarationCode); + preprocessedVertexShader.replace("%UnrolledPerRasterLayerTexCoordsDeclarationCode%", + preprocessedVertexShader_UnrolledPerRasterLayerTexCoordsDeclarationCode); + QString preprocessedVertexShader_UnrolledPerElevationColorMapEntryCode; + for (auto colorMapEntryIndex = 1u; colorMapEntryIndex < ElevationConfiguration::MaxColorMapEntries; colorMapEntryIndex++) + { + preprocessedVertexShader_UnrolledPerElevationColorMapEntryCode += + detachedOf(vertexShader_perElevationColorMapEntryCode) + .replace("%entryIndex%", QString::number(colorMapEntryIndex)) + .replace("%prevEntryIndex%", QString::number(colorMapEntryIndex - 1)); + } + preprocessedVertexShader.replace("%UnrolledPerElevationColorMapEntryCode%", + preprocessedVertexShader_UnrolledPerElevationColorMapEntryCode); + preprocessedVertexShader.replace("%HeixelsPerTileSide%", + QString::number(AtlasMapRenderer::HeixelsPerTileSide - 1)); + preprocessedVertexShader.replace("%MaxElevationColorMapEntriesCount%", + QString::number(ElevationConfiguration::MaxColorMapEntries)); + preprocessedVertexShader.replace("%SlopeAlgorithm_None%", + QString::number(static_cast(ElevationConfiguration::SlopeAlgorithm::None))); + preprocessedVertexShader.replace("%SlopeAlgorithm_ZevenbergenThorne%", + QString::number(static_cast(ElevationConfiguration::SlopeAlgorithm::ZevenbergenThorne))); + preprocessedVertexShader.replace("%SlopeAlgorithm_Horn%", + QString::number(static_cast(ElevationConfiguration::SlopeAlgorithm::Horn))); + preprocessedVertexShader.replace("%VisualizationStyle_None%", + QString::number(static_cast(ElevationConfiguration::VisualizationStyle::None))); + preprocessedVertexShader.replace("%VisualizationStyle_SlopeDegrees%", + QString::number(static_cast(ElevationConfiguration::VisualizationStyle::SlopeDegrees))); + preprocessedVertexShader.replace("%VisualizationStyle_SlopePercents%", + QString::number(static_cast(ElevationConfiguration::VisualizationStyle::SlopePercents))); + preprocessedVertexShader.replace("%VisualizationStyle_HillshadeTraditional%", + QString::number(static_cast(ElevationConfiguration::VisualizationStyle::HillshadeTraditional))); + preprocessedVertexShader.replace("%VisualizationStyle_HillshadeIgor%", + QString::number(static_cast(ElevationConfiguration::VisualizationStyle::HillshadeIgor))); + preprocessedVertexShader.replace("%VisualizationStyle_HillshadeCombined%", + QString::number(static_cast(ElevationConfiguration::VisualizationStyle::HillshadeCombined))); + preprocessedVertexShader.replace("%VisualizationStyle_HillshadeMultidirectional%", + QString::number(static_cast(ElevationConfiguration::VisualizationStyle::HillshadeMultidirectional))); + preprocessedVertexShader.replace("%ElevationVisualizationEnabled%", + QString::number(setupOptions.elevationVisualizationEnabled ? 1 : 0)); + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererMapLayersStage_OpenGL vertex shader for %d raster map layers", + numberOfLayersInBatch); + return false; + } - if (layerIndex > 0) + // Compile fragment shader + auto preprocessedFragmentShader = fragmentShader; + QString preprocessedFragmentShader_UnrolledPerRasterLayerTexCoordsDeclarationCode; + QString preprocessedFragmentShader_UnrolledPerRasterLayerParamsDeclarationCode; + QString preprocessedFragmentShader_UnrolledPerRasterLayerProcessingCode; + for (auto layerIndex = 0u; layerIndex < numberOfLayersInBatch; layerIndex++) { - preprocessedFragmentShader_UnrolledPerRasterLayerProcessingCode += - detachedOf(fragmentShader_perRasterLayer).replace("%rasterLayerIndex%", QString::number(layerIndex)); + preprocessedFragmentShader_UnrolledPerRasterLayerTexCoordsDeclarationCode += + detachedOf(fragmentShader_perRasterLayerTexCoordsDeclaration) + .replace("%rasterLayerIndex%", QString::number(layerIndex)); + + preprocessedFragmentShader_UnrolledPerRasterLayerParamsDeclarationCode += + detachedOf(fragmentShader_perRasterLayerParamsDeclaration) + .replace("%rasterLayerIndex%", QString::number(layerIndex)); + + if (layerIndex > 0) + { + preprocessedFragmentShader_UnrolledPerRasterLayerProcessingCode += + detachedOf(fragmentShader_perRasterLayer).replace("%rasterLayerIndex%", QString::number(layerIndex)); + } } - } - preprocessedFragmentShader.replace( - "%UnrolledPerRasterLayerTexCoordsDeclarationCode%", - preprocessedFragmentShader_UnrolledPerRasterLayerTexCoordsDeclarationCode); - preprocessedFragmentShader.replace( - "%UnrolledPerRasterLayerParamsDeclarationCode%", - preprocessedFragmentShader_UnrolledPerRasterLayerParamsDeclarationCode); - preprocessedFragmentShader.replace( - "%UnrolledPerRasterLayerProcessingCode%", - preprocessedFragmentShader_UnrolledPerRasterLayerProcessingCode); - preprocessedFragmentShader.replace("%ElevationVisualizationEnabled%", - QString::number(setupOptions.elevationVisualizationEnabled ? 1 : 0)); - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; + preprocessedFragmentShader.replace( + "%UnrolledPerRasterLayerTexCoordsDeclarationCode%", + preprocessedFragmentShader_UnrolledPerRasterLayerTexCoordsDeclarationCode); + preprocessedFragmentShader.replace( + "%UnrolledPerRasterLayerParamsDeclarationCode%", + preprocessedFragmentShader_UnrolledPerRasterLayerParamsDeclarationCode); + preprocessedFragmentShader.replace( + "%UnrolledPerRasterLayerProcessingCode%", + preprocessedFragmentShader_UnrolledPerRasterLayerProcessingCode); + preprocessedFragmentShader.replace("%ElevationVisualizationEnabled%", + QString::number(setupOptions.elevationVisualizationEnabled ? 1 : 0)); + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererMapLayersStage_OpenGL fragment shader for %d raster map layers", - numberOfLayersInBatch); - return false; + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererMapLayersStage_OpenGL fragment shader for %d raster map layers", + numberOfLayersInBatch); + return false; + } + GLuint shaders[] = { vsId, fsId }; + outRasterLayerTileProgram.id = getGPUAPI()->linkProgram(2, shaders, variableLocations, + outRasterLayerTileProgram.binaryCache, outRasterLayerTileProgram.cacheFormat, true, &variablesMap); } - - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - auto variableLocations = QList< std::tuple >({ - { GlslVariableType::In, QStringLiteral("in_vs_vertexPosition"), 0 }, - { GlslVariableType::In, QStringLiteral("in_vs_vertexTexCoords"), 1 }, - }); - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - outRasterLayerTileProgram.id = getGPUAPI()->linkProgram(2, shaders, variableLocations, true, &variablesMap); if (!outRasterLayerTileProgram.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -1044,6 +1073,10 @@ bool OsmAnd::AtlasMapRendererMapLayersStage_OpenGL::initializeRasterLayersProgra outRasterLayerTileProgram.vs.param.mPerspectiveProjectionView, "param_vs_mPerspectiveProjectionView", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation( + outRasterLayerTileProgram.vs.param.resultScale, + "param_vs_resultScale", + GlslVariableType::Uniform); ok = ok && lookup->lookupLocation( outRasterLayerTileProgram.vs.param.targetInTilePosN, "param_vs_targetInTilePosN", @@ -1281,154 +1314,6 @@ bool OsmAnd::AtlasMapRendererMapLayersStage_OpenGL::renderRasterLayersBatch( haveElevation = true; } -/* - QList> elevationResources; - ZoomLevel elevationZoom = zoomLevel; - auto baseTileSize = static_cast(AtlasMapRenderer::TileSize3D) * - static_cast(1ull << currentState.zoomLevel - zoomLevel); - auto tileSize = baseTileSize; - const auto tileIdN = Utilities::normalizeTileId(batch->tileId, zoomLevel); - const auto upperMetersPerUnit = Utilities::getMetersPerTileUnit( - zoomLevel, - tileIdN.y, - baseTileSize); - const auto lowerMetersPerUnit = Utilities::getMetersPerTileUnit( - zoomLevel, - tileIdN.y + 1, - baseTileSize); - float zScaleFactor = 0.0f; - float dataScaleFactor = 0.0f; - if (withElevation && currentState.elevationDataProvider) - { - auto elevationResource = captureElevationDataResource(tileIdN, zoomLevel); - if (elevationResource) - { - configureElevationData(elevationResource, program, - tileIdN, - zoomLevel, - PointF(0.0f, 0.0f), - PointF(1.0f, 1.0f), - tileSize, - elevationDataSamplerIndex); - } - else - { - const auto maxMissingDataZoomShift = currentState.elevationDataProvider->getMaxMissingDataZoomShift(); - const auto maxUnderZoomShift = currentState.elevationDataProvider->getMaxMissingDataUnderZoomShift(); - const auto minZoom = currentState.elevationDataProvider->getMinZoom(); - const auto maxZoom = currentState.elevationDataProvider->getMaxZoom(); - for (int absZoomShift = 1; absZoomShift <= maxMissingDataZoomShift; absZoomShift++) - { - // Look for underscaled first. Only full match is accepted. - // Don't replace tiles of absent zoom levels by the unserscaled ones - const auto underscaledZoom = static_cast(zoomLevel) + absZoomShift; - if (underscaledZoom >= minZoom && underscaledZoom <= maxZoom && absZoomShift <= maxUnderZoomShift && - zoomLevel >= minZoom) - { - const auto underscaledTileIdsN = Utilities::getTileIdsUnderscaledByZoomShift( - tileIdN, - absZoomShift); - const auto subtilesCount = underscaledTileIdsN.size(); - bool atLeastOnePresent = false; - QVector< std::shared_ptr > gpuResources(subtilesCount); - auto pUnderscaledTileIdN = underscaledTileIdsN.constData(); - auto pGpuResource = gpuResources.data(); - for (auto tileIdx = 0; tileIdx < subtilesCount; tileIdx++) - { - const auto& underscaledTileId = *(pUnderscaledTileIdN++); - auto& gpuResource = *(pGpuResource++); - gpuResource = captureElevationDataResource( - underscaledTileId, - static_cast(underscaledZoom)); - if (gpuResource) - atLeastOnePresent = true; - } - - if (atLeastOnePresent) - { - const auto subtilesPerSide = (1u << absZoomShift); - const PointF texCoordsScale(subtilesPerSide, subtilesPerSide); - auto pGpuResource = gpuResources.constData(); - pUnderscaledTileIdN = underscaledTileIdsN.constData(); - for (auto subtileIdx = 0; subtileIdx < subtilesCount; subtileIdx++) - { - const auto& underscaledTileId = *(pUnderscaledTileIdN++); - const auto& gpuResource = *(pGpuResource++); - if (gpuResource) - { - uint16_t xSubtile; - uint16_t ySubtile; - Utilities::decodeMortonCode(subtileIdx, xSubtile, ySubtile); - elevationResources.push_back(Ref::New( - gpuResource, - underscaledTileId, - PointF(-xSubtile, -ySubtile), - texCoordsScale)); - } - else - { - // Stub texture requires no texture offset or scaling - elevationResources.push_back(Ref::New( - nullptr, - underscaledTileId)); - } - } - if (subtilesCount > subtilesPerTile) - { - subtileRasterFactor = subtilesCount / subtilesPerTile; - subtilesPerTile = subtilesCount; - } - else if (subtilesCount < subtilesPerTile) - subtileElevationFactor = subtilesPerTile / subtilesCount; - elevationZoom = static_cast(underscaledZoom); - tileSize = static_cast(AtlasMapRenderer::TileSize3D) * - (elevationZoom > currentState.zoomLevel ? - 1.0 / static_cast(1ull << elevationZoom - currentState.zoomLevel) : - static_cast(1ull << currentState.zoomLevel - elevationZoom)); - break; - } - } - - // If underscaled was not found, look for overscaled (surely, if such zoom level exists at all) - const auto overscaledZoom = static_cast(zoomLevel) - absZoomShift; - if (overscaledZoom >= minZoom && overscaledZoom <= maxZoom) - { - PointF texCoordsOffset; - PointF texCoordsScale; - const auto overscaledTileIdN = Utilities::getTileIdOverscaledByZoomShift( - tileIdN, - absZoomShift, - &texCoordsOffset, - &texCoordsScale); - elevationZoom = static_cast(overscaledZoom); - if (elevationResource = captureElevationDataResource( - overscaledTileIdN, - elevationZoom)) - { - tileSize = static_cast(AtlasMapRenderer::TileSize3D) * - static_cast(1ull << currentState.zoomLevel - elevationZoom); - configureElevationData(elevationResource, program, - overscaledTileIdN, - elevationZoom, - texCoordsOffset, - texCoordsScale, - tileSize, - elevationDataSamplerIndex); - break; - } - } - } - } - if (!elevationResource && elevationResources.isEmpty()) - cancelElevation(program, elevationDataSamplerIndex); - else - { - zScaleFactor = currentState.elevationConfiguration.zScaleFactor; - dataScaleFactor = currentState.elevationConfiguration.dataScaleFactor; - haveElevation = true; - } - } -*/ // Per-tile elevation data configuration glUniform4f( program.vs.param.elevation_scale, @@ -1687,6 +1572,14 @@ bool OsmAnd::AtlasMapRendererMapLayersStage_OpenGL::activateRasterLayersProgram( glUniformMatrix4fv(program.vs.param.mPerspectiveProjectionView, 1, GL_FALSE, glm::value_ptr(internalState.mPerspectiveProjectionView)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(program.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + // Set center offset PointF offsetInTileN; Utilities::getTileId(currentState.target31, zoomLevel, &offsetInTileN); @@ -1934,9 +1827,8 @@ bool OsmAnd::AtlasMapRendererMapLayersStage_OpenGL::releaseRasterLayers(bool gpu glDeleteProgram(rasterLayerTileProgram.id); GL_CHECK_RESULT; } - rasterLayerTileProgram = RasterLayerTileProgram(); + rasterLayerTileProgram.id = 0; } - _rasterLayerTilePrograms.clear(); return true; } diff --git a/src/Map/OpenGL/AtlasMapRendererMapLayersStage_OpenGL.h b/src/Map/OpenGL/AtlasMapRendererMapLayersStage_OpenGL.h index f1f14e967..7614de068 100644 --- a/src/Map/OpenGL/AtlasMapRendererMapLayersStage_OpenGL.h +++ b/src/Map/OpenGL/AtlasMapRendererMapLayersStage_OpenGL.h @@ -126,6 +126,8 @@ namespace OsmAnd struct RasterLayerTileProgram { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -138,7 +140,7 @@ namespace OsmAnd struct { // Common data GLlocation mPerspectiveProjectionView; - GLlocation mapScale; + GLlocation resultScale; GLlocation targetInTilePosN; GLlocation tileSize; GLlocation distanceFromCameraToTarget; diff --git a/src/Map/OpenGL/AtlasMapRendererSkyStage_OpenGL.cpp b/src/Map/OpenGL/AtlasMapRendererSkyStage_OpenGL.cpp index 30a12dd38..37e5fec1c 100644 --- a/src/Map/OpenGL/AtlasMapRendererSkyStage_OpenGL.cpp +++ b/src/Map/OpenGL/AtlasMapRendererSkyStage_OpenGL.cpp @@ -30,86 +30,91 @@ bool OsmAnd::AtlasMapRendererSkyStage_OpenGL::initialize() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - " ""\n" - // Output data to next shader stages - "PARAM_OUTPUT vec2 v2f_position; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mProjection; ""\n" - "uniform vec4 param_vs_planeSize; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec4 v; ""\n" - " v.xy = in_vs_vertexPosition * param_vs_planeSize.xy; ""\n" - " v.z = param_vs_planeSize.z; ""\n" - " v.w = 1.0; ""\n" - " v.y += param_vs_planeSize.w; ""\n" - " ""\n" - " v2f_position = in_vs_vertexPosition; ""\n" - " v = param_vs_mProjection * v; ""\n" - " v.y--; ""\n" - " gl_Position = v; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _program.id = 0; + if (!_program.binaryCache.isEmpty()) + _program.id = gpuAPI->linkProgram(0, nullptr, _program.binaryCache, _program.cacheFormat, true, &variablesMap); + if (!_program.id.isValid()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSkyStage_OpenGL vertex shader"); - return false; - } + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; ""\n" + " ""\n" + // Output data to next shader stages + "PARAM_OUTPUT vec2 v2f_position; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mProjection; ""\n" + "uniform vec4 param_vs_planeSize; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec4 v; ""\n" + " v.xy = in_vs_vertexPosition * param_vs_planeSize.xy; ""\n" + " v.z = param_vs_planeSize.z; ""\n" + " v.w = 1.0; ""\n" + " v.y += param_vs_planeSize.w; ""\n" + " ""\n" + " v2f_position = in_vs_vertexPosition; ""\n" + " v = param_vs_mProjection * v; ""\n" + " v.y--; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSkyStage_OpenGL vertex shader"); + return false; + } - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Input data - "PARAM_INPUT vec2 v2f_position; ""\n" - " ""\n" - // Parameters: common data - "uniform vec4 param_fs_skySize; ""\n" - "uniform lowp vec4 param_fs_skyColor; ""\n" - "uniform lowp vec4 param_fs_fogColor; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " lowp vec4 color; ""\n" - " float position; ""\n" - " float skyHeight = param_fs_skySize.y / (1.0 - param_fs_skySize.z); ""\n" - " bool sky = v2f_position.y > param_fs_skySize.z; ""\n" - " position = (v2f_position.y - (sky ? param_fs_skySize.z : 0.0)) * skyHeight; ""\n" - " float low = 1.0 / pow(1.1 + position, 4.0); ""\n" - " float high = 1.0 - low; ""\n" - " color.r = (0.7647059 * low + high * exp(1.0 - position / 3.0) * 0.3101728) * param_fs_skyColor.r; ""\n" - " color.g = (0.8039216 * low + high * exp(1.0 - position / 5.0) * 0.3390262) * param_fs_skyColor.g; ""\n" - " color.b = (0.9019608 * low + high * exp(1.0 - position / 12.0) * 0.3678794) * param_fs_skyColor.b; ""\n" - " color.a = 1.0; ""\n" - " FRAGMENT_COLOR_OUTPUT = sky ? color : param_fs_fogColor; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Input data + "PARAM_INPUT vec2 v2f_position; ""\n" + " ""\n" + // Parameters: common data + "uniform vec4 param_fs_skySize; ""\n" + "uniform lowp vec4 param_fs_skyColor; ""\n" + "uniform lowp vec4 param_fs_fogColor; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " lowp vec4 color; ""\n" + " float position; ""\n" + " float skyHeight = param_fs_skySize.y / (1.0 - param_fs_skySize.z); ""\n" + " bool sky = v2f_position.y > param_fs_skySize.z; ""\n" + " position = (v2f_position.y - (sky ? param_fs_skySize.z : 0.0)) * skyHeight; ""\n" + " float low = 1.0 / pow(1.1 + position, 4.0); ""\n" + " float high = 1.0 - low; ""\n" + " color.r = (0.7647059 * low + high * exp(1.0 - position / 3.0) * 0.3101728) * param_fs_skyColor.r; ""\n" + " color.g = (0.8039216 * low + high * exp(1.0 - position / 5.0) * 0.3390262) * param_fs_skyColor.g; ""\n" + " color.b = (0.9019608 * low + high * exp(1.0 - position / 12.0) * 0.3678794) * param_fs_skyColor.b; ""\n" + " color.a = 1.0; ""\n" + " FRAGMENT_COLOR_OUTPUT = sky ? color : param_fs_fogColor; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + QString preprocessedFragmentShader_UnrolledPerLayerProcessingCode; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSkyStage_OpenGL fragment shader"); - return false; + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSkyStage_OpenGL fragment shader"); + return false; + } + const GLuint shaders[] = { vsId, fsId }; + _program.id = gpuAPI->linkProgram(2, shaders, _program.binaryCache, _program.cacheFormat, true, &variablesMap); } - - // Link everything into program object - const GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _program.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); if (!_program.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -122,6 +127,7 @@ bool OsmAnd::AtlasMapRendererSkyStage_OpenGL::initialize() ok = ok && lookup->lookupLocation(_program.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_program.vs.param.mProjection, "param_vs_mProjection", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_program.vs.param.planeSize, "param_vs_planeSize", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_program.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_program.fs.param.skySize, "param_fs_skySize", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_program.fs.param.skyColor, "param_fs_skyColor", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_program.fs.param.fogColor, "param_fs_fogColor", GlslVariableType::Uniform); @@ -235,6 +241,14 @@ bool OsmAnd::AtlasMapRendererSkyStage_OpenGL::render(IMapRenderer_Metrics::Metri internalState.skyShift); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_program.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + // Set parameters of the sky glUniform4f(_program.fs.param.skySize, 0.0f, // TODO: put sky width (in kilometers) here for the spherified horizon @@ -315,7 +329,7 @@ bool OsmAnd::AtlasMapRendererSkyStage_OpenGL::release(bool gpuContextLost) glDeleteProgram(_program.id); GL_CHECK_RESULT; } - _program = Program(); + _program.id = 0; } return true; diff --git a/src/Map/OpenGL/AtlasMapRendererSkyStage_OpenGL.h b/src/Map/OpenGL/AtlasMapRendererSkyStage_OpenGL.h index 797a1b20d..016c38214 100644 --- a/src/Map/OpenGL/AtlasMapRendererSkyStage_OpenGL.h +++ b/src/Map/OpenGL/AtlasMapRendererSkyStage_OpenGL.h @@ -26,6 +26,8 @@ namespace OsmAnd struct Program { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -38,6 +40,7 @@ namespace OsmAnd // Common data GLlocation mProjection; GLlocation planeSize; + GLlocation resultScale; } param; } vs; diff --git a/src/Map/OpenGL/AtlasMapRendererSymbolsStageModel3D_OpenGL.cpp b/src/Map/OpenGL/AtlasMapRendererSymbolsStageModel3D_OpenGL.cpp index e5f07ec50..c4bcc4ee5 100644 --- a/src/Map/OpenGL/AtlasMapRendererSymbolsStageModel3D_OpenGL.cpp +++ b/src/Map/OpenGL/AtlasMapRendererSymbolsStageModel3D_OpenGL.cpp @@ -42,84 +42,90 @@ bool OsmAnd::AtlasMapRendererSymbolsStageModel3D_OpenGL::initialize() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec3 in_vs_vertexPosition; ""\n" - "INPUT vec3 in_vs_vertexNormal; ""\n" - "INPUT vec4 in_vs_vertexColor; ""\n" - " ""\n" - // Output data to next shader stages - "PARAM_OUTPUT vec3 v2f_pointPosition; ""\n" - "PARAM_OUTPUT vec3 v2f_pointNormal; ""\n" - "PARAM_OUTPUT vec4 v2f_pointColor; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" - " ""\n" - // Parameters: per-model data - "uniform mat4 param_vs_mModel; ""\n" - "uniform vec4 param_vs_mainColor; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec4 v = vec4(in_vs_vertexPosition, 1.0); ""\n" - " v = param_vs_mModel * v; ""\n" - " v2f_pointPosition = v.xyz; ""\n" - " mat3 nModel = mat3(param_vs_mModel[0].xyz, param_vs_mModel[1].xyz, param_vs_mModel[2].xyz); ""\n" - " v2f_pointNormal = nModel * in_vs_vertexNormal; ""\n" - " v2f_pointColor.argb = in_vs_vertexColor.x > -1.0 ? in_vs_vertexColor.xyzw : param_vs_mainColor.argb; ""\n" - " gl_Position = param_vs_mPerspectiveProjectionView * v; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _program.id = 0; + if (!_program.binaryCache.isEmpty()) + _program.id = gpuAPI->linkProgram(0, nullptr, _program.binaryCache, _program.cacheFormat, true, &variablesMap); + if (!_program.id.isValid()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStageModel3D_OpenGL vertex shader"); - return false; - } + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec3 in_vs_vertexPosition; ""\n" + "INPUT vec3 in_vs_vertexNormal; ""\n" + "INPUT vec4 in_vs_vertexColor; ""\n" + " ""\n" + // Output data to next shader stages + "PARAM_OUTPUT vec3 v2f_pointPosition; ""\n" + "PARAM_OUTPUT vec3 v2f_pointNormal; ""\n" + "PARAM_OUTPUT vec4 v2f_pointColor; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + " ""\n" + // Parameters: per-model data + "uniform mat4 param_vs_mModel; ""\n" + "uniform vec4 param_vs_mainColor; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec4 v = vec4(in_vs_vertexPosition, 1.0); ""\n" + " v = param_vs_mModel * v; ""\n" + " v2f_pointPosition = v.xyz; ""\n" + " mat3 nModel = mat3(param_vs_mModel[0].xyz, param_vs_mModel[1].xyz, param_vs_mModel[2].xyz); ""\n" + " v2f_pointNormal = nModel * in_vs_vertexNormal; ""\n" + " v2f_pointColor.argb = in_vs_vertexColor.x > -1.0 ? in_vs_vertexColor.xyzw : param_vs_mainColor.argb; ""\n" + " v = param_vs_mPerspectiveProjectionView * v; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStageModel3D_OpenGL vertex shader"); + return false; + } - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Input data - "PARAM_INPUT vec3 v2f_pointPosition; ""\n" - "PARAM_INPUT vec3 v2f_pointNormal; ""\n" - "PARAM_INPUT vec4 v2f_pointColor; ""\n" - " ""\n" - // Parameters: common data - "uniform vec3 param_fs_cameraPosition; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec3 v = normalize(param_fs_cameraPosition - v2f_pointPosition); ""\n" - " vec3 n = normalize(v2f_pointNormal); ""\n" - " vec3 l = normalize(vec3(1.0, -1.0, 1.0)); ""\n" - " vec3 r = reflect(l, n); ""\n" - " float h = pow((clamp(dot(r, v), 0.5, 1.0) - 0.5) * 2.0, 6.0) / 2.0; ""\n" - " float d = clamp(dot(r, n), 0.0, 1.0) + 0.5; ""\n" - " FRAGMENT_COLOR_OUTPUT = vec4(mix(v2f_pointColor.rgb * d, vec3(1.0), h), v2f_pointColor.a); ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Input data + "PARAM_INPUT vec3 v2f_pointPosition; ""\n" + "PARAM_INPUT vec3 v2f_pointNormal; ""\n" + "PARAM_INPUT vec4 v2f_pointColor; ""\n" + " ""\n" + // Parameters: common data + "uniform vec3 param_fs_cameraPosition; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec3 v = normalize(param_fs_cameraPosition - v2f_pointPosition); ""\n" + " vec3 n = normalize(v2f_pointNormal); ""\n" + " vec3 l = normalize(vec3(1.0, -1.0, 1.0)); ""\n" + " vec3 r = reflect(l, n); ""\n" + " float h = pow((clamp(dot(r, v), 0.5, 1.0) - 0.5) * 2.0, 6.0) / 2.0; ""\n" + " float d = clamp(dot(r, n), 0.0, 1.0) + 0.5; ""\n" + " FRAGMENT_COLOR_OUTPUT = vec4(mix(v2f_pointColor.rgb * d, vec3(1.0), h), v2f_pointColor.a); ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStageModel3D_OpenGL fragment shader"); - return false; + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStageModel3D_OpenGL fragment shader"); + return false; + } + const GLuint shaders[] = { vsId, fsId }; + _program.id = gpuAPI->linkProgram(2, shaders, _program.binaryCache, _program.cacheFormat, true, &variablesMap); } - - // Link everything into program object - const GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _program.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); if (!_program.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -132,10 +138,11 @@ bool OsmAnd::AtlasMapRendererSymbolsStageModel3D_OpenGL::initialize() ok = ok && lookup->lookupLocation(_program.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_program.vs.in.vertexNormal, "in_vs_vertexNormal", GlslVariableType::In); ok = ok && lookup->lookupLocation(_program.vs.in.vertexColor, "in_vs_vertexColor", GlslVariableType::In); - ok = ok && lookup->lookupLocation(_program.vs.params.mainColor, "param_vs_mainColor", GlslVariableType::Uniform); - ok = ok && lookup->lookupLocation(_program.vs.params.mPerspectiveProjectionView, "param_vs_mPerspectiveProjectionView", GlslVariableType::Uniform); - ok = ok && lookup->lookupLocation(_program.vs.params.mModel, "param_vs_mModel", GlslVariableType::Uniform); - ok = ok && lookup->lookupLocation(_program.fs.params.cameraPosition, "param_fs_cameraPosition", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_program.vs.param.mainColor, "param_vs_mainColor", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_program.vs.param.mPerspectiveProjectionView, "param_vs_mPerspectiveProjectionView", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_program.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_program.vs.param.mModel, "param_vs_mModel", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_program.fs.param.cameraPosition, "param_fs_cameraPosition", GlslVariableType::Uniform); if (!ok) { glDeleteProgram(_program.id); @@ -169,17 +176,24 @@ bool OsmAnd::AtlasMapRendererSymbolsStageModel3D_OpenGL::render( GL_CHECK_RESULT; // Set perspective projection-view matrix - glUniformMatrix4fv(_program.vs.params.mPerspectiveProjectionView, 1, GL_FALSE, glm::value_ptr(internalState.mPerspectiveProjectionView)); + glUniformMatrix4fv(_program.vs.param.mPerspectiveProjectionView, 1, GL_FALSE, glm::value_ptr(internalState.mPerspectiveProjectionView)); + GL_CHECK_RESULT; + + // Scale the result + glUniform4f(_program.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); GL_CHECK_RESULT; // Set camera position - glUniform3f(_program.fs.params.cameraPosition, + glUniform3f(_program.fs.param.cameraPosition, internalState.worldCameraPosition.x, internalState.worldCameraPosition.y, internalState.worldCameraPosition.z); GL_CHECK_RESULT; - // Just in case un-use any possibly used VAO gpuAPI->unuseVAO(); @@ -213,14 +227,14 @@ bool OsmAnd::AtlasMapRendererSymbolsStageModel3D_OpenGL::render( // Model matrix glUniformMatrix4fv( - _program.vs.params.mModel, + _program.vs.param.mModel, 1, GL_FALSE, glm::value_ptr(renderable->mModel)); GL_CHECK_RESULT; // Set main color - glUniform4f(_program.vs.params.mainColor, + glUniform4f(_program.vs.param.mainColor, symbol->mainColor.r, symbol->mainColor.g, symbol->mainColor.b, @@ -329,7 +343,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStageModel3D_OpenGL::release(const bool gpuC glDeleteProgram(_program.id); GL_CHECK_RESULT; } - _program = Model3DProgram(); + _program.id = 0; } return true; diff --git a/src/Map/OpenGL/AtlasMapRendererSymbolsStageModel3D_OpenGL.h b/src/Map/OpenGL/AtlasMapRendererSymbolsStageModel3D_OpenGL.h index 627ac5f3d..bf123b64d 100644 --- a/src/Map/OpenGL/AtlasMapRendererSymbolsStageModel3D_OpenGL.h +++ b/src/Map/OpenGL/AtlasMapRendererSymbolsStageModel3D_OpenGL.h @@ -17,6 +17,8 @@ namespace OsmAnd struct Model3DProgram { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; // Vertex data struct @@ -38,7 +40,8 @@ namespace OsmAnd // Common data GLlocation mPerspectiveProjectionView; - } params; + GLlocation resultScale; + } param; } vs; // Vertex data struct @@ -48,7 +51,7 @@ namespace OsmAnd { // Common data GLlocation cameraPosition; - } params; + } param; } fs; } _program; diff --git a/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.cpp b/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.cpp index 4c941bf02..c77ecedfc 100644 --- a/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.cpp +++ b/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.cpp @@ -320,145 +320,155 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeBillboardRaster() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - "INPUT vec2 in_vs_vertexTexCoords; ""\n" - " ""\n" - // Output data to next shader stages - "PARAM_OUTPUT vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" - "uniform mat4 param_vs_mOrthographicProjection; ""\n" - "uniform vec4 param_vs_viewport; // x, y, width, height ""\n" - "uniform highp ivec4 param_vs_target31; // x, y, zoom, tileSize31 ""\n" - " ""\n" - // Parameters: per-symbol data - "uniform highp ivec2 param_vs_position31; ""\n" - "uniform ivec2 param_vs_symbolSize; ""\n" - "uniform float param_vs_distanceFromCamera; ""\n" - "uniform ivec2 param_vs_onScreenOffset; ""\n" - "uniform float param_vs_elevationInWorld; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - // Calculate position of symbol in world coordinate system. - " highp ivec2 offsetFromTarget31 = param_vs_position31 - param_vs_target31.xy; ""\n" - " highp ivec2 correctOverlap; ""\n" - " correctOverlap.x = offsetFromTarget31.x >= 1073741824 ? -1 : (offsetFromTarget31.x < -1073741824 ? 1 : 0); ""\n" - " correctOverlap.y = offsetFromTarget31.y >= 1073741824 ? -1 : (offsetFromTarget31.y < -1073741824 ? 1 : 0); ""\n" - " offsetFromTarget31 += correctOverlap * 2147483647 + correctOverlap; ""\n" - " highp vec2 offsetFromTarget = vec2(0.0); ""\n" - "#if INTEGER_OPERATIONS_SUPPORTED ""\n" - " { ""\n" - " highp ivec2 offsetFromTargetT = offsetFromTarget31 / param_vs_target31.w; ""\n" - " highp vec2 offsetFromTargetF = vec2(offsetFromTarget31 - offsetFromTargetT * param_vs_target31.w); ""\n" - " offsetFromTarget = vec2(offsetFromTargetT) + offsetFromTargetF / float(param_vs_target31.w); ""\n" - " } ""\n" - "#else // !INTEGER_OPERATIONS_SUPPORTED ""\n" - " offsetFromTarget = vec2(offsetFromTarget31); ""\n" - " offsetFromTarget /= float(param_vs_target31.w); ""\n" - "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" - " vec4 symbolInWorld; ""\n" - " symbolInWorld.xz = offsetFromTarget * %TileSize3D%.0; ""\n" - " symbolInWorld.y = param_vs_elevationInWorld; ""\n" - " symbolInWorld.w = 1.0; ""\n" - " ""\n" - // Project position of symbol from world coordinate system to clipping space and to normalized device coordinates ([-1 .. 1]) - " vec4 symbolInClipping = param_vs_mPerspectiveProjectionView * symbolInWorld; ""\n" - " vec3 symbolInNDC = symbolInClipping.xyz / symbolInClipping.w; ""\n" - " ""\n" - // Using viewport size, get real screen coordinates and correct depth to be [0 .. 1] - " vec3 symbolOnScreen; ""\n" - " symbolOnScreen.xy = symbolInNDC.xy * 0.5 + 0.5; ""\n" - " symbolOnScreen.x = symbolOnScreen.x * param_vs_viewport.z + param_vs_viewport.x; ""\n" - " symbolOnScreen.y = symbolOnScreen.y * param_vs_viewport.w + param_vs_viewport.y; ""\n" - " symbolOnScreen.z = (1.0 + symbolOnScreen.z) * 0.5; ""\n" - " ""\n" - // Add on-screen offset - " symbolOnScreen.xy += vec2(param_vs_onScreenOffset); ""\n" - " ""\n" - // symbolOnScreen.xy now contains correct coordinates in viewport, - // which can be used in orthographic projection (if it was configured to match viewport). - // - // To provide pixel-perfect rendering of billboard raster symbols: - // symbolOnScreen.(x|y) has to be rounded and +0.5 in case param_vs_symbolSize.(x|y) is even - // symbolOnScreen.(x|y) has to be rounded in case param_vs_symbolSize.(x|y) is odd - " symbolOnScreen = floor(symbolOnScreen); ""\n" - "#if INTEGER_OPERATIONS_SUPPORTED ""\n" - " symbolOnScreen.x += float(1 - param_vs_symbolSize.x & 1) * 0.5; ""\n" - " symbolOnScreen.y += float(1 - param_vs_symbolSize.y & 1) * 0.5; ""\n" - "#else // !INTEGER_OPERATIONS_SUPPORTED ""\n" - " symbolOnScreen.xy += (vec2(1.0) - mod(vec2(param_vs_symbolSize), vec2(2.0))) * 0.5; ""\n" - "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" - " ""\n" - // So it's possible to calculate current vertex location: - // Initially, get location of current vertex in screen coordinates - " vec2 vertexOnScreen = in_vs_vertexPosition * vec2(param_vs_symbolSize) + symbolOnScreen.xy; ""\n" - " ""\n" - // To provide pixel-perfect result, vertexOnScreen needs to be rounded - " vertexOnScreen = floor(vertexOnScreen + vec2(0.5, 0.5)); ""\n" - " ""\n" - // There's no need to perform unprojection into orthographic world space, just multiply these coordinates by - // orthographic projection matrix (View and Model being identity), yet use Z from perspective NDC - " vec4 vertex; ""\n" - " vertex.xy = vertexOnScreen.xy; ""\n" - " vertex.z = 0.0; ""\n" - " vertex.w = 1.0; ""\n" - " gl_Position = param_vs_mOrthographicProjection * vertex; ""\n" - " gl_Position.z = symbolInNDC.z * gl_Position.w; ""\n" - " ""\n" - // Texture coordinates are simply forwarded from input - " v2f_texCoords = in_vs_vertexTexCoords; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - preprocessedVertexShader.replace("%TileSize3D%", QString::number(AtlasMapRenderer::TileSize3D)); - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _billboardRasterProgram.id = 0; + if (!_billboardRasterProgram.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); - return false; + _billboardRasterProgram.id = gpuAPI->linkProgram(0, nullptr, + _billboardRasterProgram.binaryCache, _billboardRasterProgram.cacheFormat, true, &variablesMap); } + if (!_billboardRasterProgram.id.isValid()) + { + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; ""\n" + "INPUT vec2 in_vs_vertexTexCoords; ""\n" + " ""\n" + // Output data to next shader stages + "PARAM_OUTPUT vec2 v2f_texCoords; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" + "uniform mat4 param_vs_mOrthographicProjection; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + "uniform vec4 param_vs_viewport; // x, y, width, height ""\n" + "uniform highp ivec4 param_vs_target31; // x, y, zoom, tileSize31 ""\n" + " ""\n" + // Parameters: per-symbol data + "uniform highp ivec2 param_vs_position31; ""\n" + "uniform ivec2 param_vs_symbolSize; ""\n" + "uniform float param_vs_distanceFromCamera; ""\n" + "uniform ivec2 param_vs_onScreenOffset; ""\n" + "uniform float param_vs_elevationInWorld; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + // Calculate position of symbol in world coordinate system. + " highp ivec2 offsetFromTarget31 = param_vs_position31 - param_vs_target31.xy; ""\n" + " highp ivec2 correctOverlap; ""\n" + " correctOverlap.x = offsetFromTarget31.x >= 1073741824 ? -1 : (offsetFromTarget31.x < -1073741824 ? 1 : 0); ""\n" + " correctOverlap.y = offsetFromTarget31.y >= 1073741824 ? -1 : (offsetFromTarget31.y < -1073741824 ? 1 : 0); ""\n" + " offsetFromTarget31 += correctOverlap * 2147483647 + correctOverlap; ""\n" + " highp vec2 offsetFromTarget = vec2(0.0); ""\n" + "#if INTEGER_OPERATIONS_SUPPORTED ""\n" + " { ""\n" + " highp ivec2 offsetFromTargetT = offsetFromTarget31 / param_vs_target31.w; ""\n" + " highp vec2 offsetFromTargetF = vec2(offsetFromTarget31 - offsetFromTargetT * param_vs_target31.w); ""\n" + " offsetFromTarget = vec2(offsetFromTargetT) + offsetFromTargetF / float(param_vs_target31.w); ""\n" + " } ""\n" + "#else // !INTEGER_OPERATIONS_SUPPORTED ""\n" + " offsetFromTarget = vec2(offsetFromTarget31); ""\n" + " offsetFromTarget /= float(param_vs_target31.w); ""\n" + "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" + " vec4 symbolInWorld; ""\n" + " symbolInWorld.xz = offsetFromTarget * %TileSize3D%.0; ""\n" + " symbolInWorld.y = param_vs_elevationInWorld; ""\n" + " symbolInWorld.w = 1.0; ""\n" + " ""\n" + // Project position of symbol from world coordinate system to clipping space and to normalized device coordinates ([-1 .. 1]) + " vec4 symbolInClipping = param_vs_mPerspectiveProjectionView * symbolInWorld; ""\n" + " vec3 symbolInNDC = symbolInClipping.xyz / symbolInClipping.w; ""\n" + " ""\n" + // Using viewport size, get real screen coordinates and correct depth to be [0 .. 1] + " vec3 symbolOnScreen; ""\n" + " symbolOnScreen.xy = symbolInNDC.xy * 0.5 + 0.5; ""\n" + " symbolOnScreen.x = symbolOnScreen.x * param_vs_viewport.z + param_vs_viewport.x; ""\n" + " symbolOnScreen.y = symbolOnScreen.y * param_vs_viewport.w + param_vs_viewport.y; ""\n" + " symbolOnScreen.z = (1.0 + symbolOnScreen.z) * 0.5; ""\n" + " ""\n" + // Add on-screen offset + " symbolOnScreen.xy += vec2(param_vs_onScreenOffset); ""\n" + " ""\n" + // symbolOnScreen.xy now contains correct coordinates in viewport, + // which can be used in orthographic projection (if it was configured to match viewport). + // + // To provide pixel-perfect rendering of billboard raster symbols: + // symbolOnScreen.(x|y) has to be rounded and +0.5 in case param_vs_symbolSize.(x|y) is even + // symbolOnScreen.(x|y) has to be rounded in case param_vs_symbolSize.(x|y) is odd + " symbolOnScreen = floor(symbolOnScreen); ""\n" + "#if INTEGER_OPERATIONS_SUPPORTED ""\n" + " symbolOnScreen.x += float(1 - param_vs_symbolSize.x & 1) * 0.5; ""\n" + " symbolOnScreen.y += float(1 - param_vs_symbolSize.y & 1) * 0.5; ""\n" + "#else // !INTEGER_OPERATIONS_SUPPORTED ""\n" + " symbolOnScreen.xy += (vec2(1.0) - mod(vec2(param_vs_symbolSize), vec2(2.0))) * 0.5; ""\n" + "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" + " ""\n" + // So it's possible to calculate current vertex location: + // Initially, get location of current vertex in screen coordinates + " vec2 vertexOnScreen = in_vs_vertexPosition * vec2(param_vs_symbolSize) + symbolOnScreen.xy; ""\n" + " ""\n" + // To provide pixel-perfect result, vertexOnScreen needs to be rounded + " vertexOnScreen = floor(vertexOnScreen + vec2(0.5, 0.5)); ""\n" + " ""\n" + // There's no need to perform unprojection into orthographic world space, just multiply these coordinates by + // orthographic projection matrix (View and Model being identity), yet use Z from perspective NDC + " vec4 vertex; ""\n" + " vertex.xy = vertexOnScreen.xy; ""\n" + " vertex.z = 0.0; ""\n" + " vertex.w = 1.0; ""\n" + " vertex = param_vs_mOrthographicProjection * vertex; ""\n" + " vertex.z = symbolInNDC.z * vertex.w; ""\n" + " gl_Position = vertex * param_vs_resultScale; ""\n" + " ""\n" + // Texture coordinates are simply forwarded from input + " v2f_texCoords = in_vs_vertexTexCoords; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + preprocessedVertexShader.replace("%TileSize3D%", QString::number(AtlasMapRenderer::TileSize3D)); + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); + return false; + } - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Input data - "PARAM_INPUT vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters: common data - // Parameters: per-symbol data - "uniform lowp sampler2D param_fs_sampler; ""\n" - "uniform lowp vec4 param_fs_modulationColor; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = SAMPLE_TEXTURE_2D( ""\n" - " param_fs_sampler, ""\n" - " v2f_texCoords) * param_fs_modulationColor; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Input data + "PARAM_INPUT vec2 v2f_texCoords; ""\n" + " ""\n" + // Parameters: common data + // Parameters: per-symbol data + "uniform lowp sampler2D param_fs_sampler; ""\n" + "uniform lowp vec4 param_fs_modulationColor; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = SAMPLE_TEXTURE_2D( ""\n" + " param_fs_sampler, ""\n" + " v2f_texCoords) * param_fs_modulationColor; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); - return false; + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _billboardRasterProgram.id = gpuAPI->linkProgram(2, shaders, + _billboardRasterProgram.binaryCache, _billboardRasterProgram.cacheFormat, true, &variablesMap); } - - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _billboardRasterProgram.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); if (!_billboardRasterProgram.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -474,6 +484,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeBillboardRaster() && lookup->lookupLocation( _billboardRasterProgram.vs.param.mPerspectiveProjectionView, "param_vs_mPerspectiveProjectionView", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_billboardRasterProgram.vs.param.mOrthographicProjection, "param_vs_mOrthographicProjection", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_billboardRasterProgram.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_billboardRasterProgram.vs.param.viewport, "param_vs_viewport", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_billboardRasterProgram.vs.param.target31, "param_vs_target31", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_billboardRasterProgram.vs.param.position31, "param_vs_position31", GlslVariableType::Uniform); @@ -588,6 +599,14 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::renderBillboardRasterSymbol( glUniformMatrix4fv(_billboardRasterProgram.vs.param.mOrthographicProjection, 1, GL_FALSE, glm::value_ptr(internalState.mOrthographicProjection)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_billboardRasterProgram.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + // Set viewport glUniform4fv(_billboardRasterProgram.vs.param.viewport, 1, glm::value_ptr(internalState.glmViewport)); GL_CHECK_RESULT; @@ -755,7 +774,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::releaseBillboardRaster(bool gp glDeleteProgram(_billboardRasterProgram.id); GL_CHECK_RESULT; } - _billboardRasterProgram = BillboardRasterSymbolProgram(); + _billboardRasterProgram.id = 0; } return true; @@ -780,7 +799,8 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnPath2D() GL_CHECK_PRESENT(glVertexAttribPointer); const auto alreadyOccupiedUniforms = - 4 /*param_vs_mPerspectiveProjectionView*/ + + 4 /*param_vs_mOrthographicProjection*/ + + 1 /*param_vs_resultScale*/ + 1 /*param_vs_glyphHeight*/ + 1 /*param_vs_zDistanceFromCamera*/ + 1 /*param_vs_currentOffset*/; @@ -927,123 +947,133 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnPath2DProgram(cons GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - "INPUT float in_vs_glyphIndex; ""\n" - "INPUT vec2 in_vs_vertexTexCoords; ""\n" - " ""\n" - // Output data to next shader stages - "PARAM_OUTPUT vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mOrthographicProjection; ""\n" - " ""\n" - // Parameters: per-symbol data - "uniform float param_vs_glyphHeight; ""\n" - "uniform float param_vs_distanceFromCamera; ""\n" - "uniform vec2 param_vs_currentOffset; ""\n" - " ""\n" - // Parameters: per-glyph data - "struct Glyph ""\n" - "{ ""\n" - " vec2 anchorPoint; ""\n" - " float width; ""\n" - " float angle; ""\n" - " float widthOfPreviousN; ""\n" - " float widthN; ""\n" - "}; ""\n" - "uniform Glyph param_vs_glyphs[%MaxGlyphsPerDrawCall%]; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " Glyph glyph = param_vs_glyphs[int(in_vs_glyphIndex)]; ""\n" - " vec2 anchorPoint = glyph.anchorPoint + param_vs_currentOffset; ""\n" - " float cos_a = cos(glyph.angle); ""\n" - " float sin_a = sin(glyph.angle); ""\n" - " ""\n" - // Pixel-perfect rendering is available when angle is 0, 90, 180 or 270 degrees, what will produce - // cos_a 0, 1 or -1 - //" if (abs(cos_a - int(cos_a)) < 0.0001) ""\n" - //" { ""\n" - //" anchorPoint.x = floor(anchorPoint.x) + mod(glyph.width, 2.0) * 0.5; ""\n" - //" anchorPoint.y = floor(anchorPoint.y) + mod(param_vs_glyphHeight, 2.0) * 0.5; ""\n" - //" } ""\n" - " ""\n" - // Get on-screen glyph point offset - " vec2 glyphPoint; ""\n" - " glyphPoint.x = in_vs_vertexPosition.x * glyph.width; ""\n" - " glyphPoint.y = in_vs_vertexPosition.y * param_vs_glyphHeight; ""\n" - " ""\n" - // Get on-screen vertex coordinates - " vec4 vertexOnScreen; ""\n" - " vertexOnScreen.x = anchorPoint.x + (glyphPoint.x*cos_a - glyphPoint.y*sin_a); ""\n" - " vertexOnScreen.y = anchorPoint.y + (glyphPoint.x*sin_a + glyphPoint.y*cos_a); ""\n" - " vertexOnScreen.z = -param_vs_distanceFromCamera; ""\n" - " vertexOnScreen.w = 1.0; ""\n" - " ""\n" - // Pixel-perfect rendering is available when angle is 0, 90, 180 or 270 degrees, what will produce - // cos_a 0, 1 or -1 - //" if (abs(cos_a - int(cos_a)) < 0.0001) ""\n" - //" { ""\n" - //" vertexOnScreen.x = floor(vertexOnScreen.x + 0.5); ""\n" - //" vertexOnScreen.y = floor(vertexOnScreen.y + 0.5); ""\n" - //" } ""\n" - " ""\n" - // Project vertex - " gl_Position = param_vs_mOrthographicProjection * vertexOnScreen; ""\n" - " ""\n" - // Prepare texture coordinates - " v2f_texCoords.s = glyph.widthOfPreviousN + in_vs_vertexTexCoords.s*glyph.widthN; ""\n" - " v2f_texCoords.t = in_vs_vertexTexCoords.t; // Height is compatible as-is ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - preprocessedVertexShader.replace("%MaxGlyphsPerDrawCall%", QString::number(maxGlyphsPerDrawCall)); - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _onPath2dProgram.id = 0; + if (!_onPath2dProgram.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); - return false; + _onPath2dProgram.id = gpuAPI->linkProgram(0, nullptr, + _onPath2dProgram.binaryCache, _onPath2dProgram.cacheFormat, true, &variablesMap); } + if (!_onPath2dProgram.id.isValid()) + { + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; ""\n" + "INPUT float in_vs_glyphIndex; ""\n" + "INPUT vec2 in_vs_vertexTexCoords; ""\n" + " ""\n" + // Output data to next shader stages + "PARAM_OUTPUT vec2 v2f_texCoords; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mOrthographicProjection; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + " ""\n" + // Parameters: per-symbol data + "uniform float param_vs_glyphHeight; ""\n" + "uniform float param_vs_distanceFromCamera; ""\n" + "uniform vec2 param_vs_currentOffset; ""\n" + " ""\n" + // Parameters: per-glyph data + "struct Glyph ""\n" + "{ ""\n" + " vec2 anchorPoint; ""\n" + " float width; ""\n" + " float angle; ""\n" + " float widthOfPreviousN; ""\n" + " float widthN; ""\n" + "}; ""\n" + "uniform Glyph param_vs_glyphs[%MaxGlyphsPerDrawCall%]; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " Glyph glyph = param_vs_glyphs[int(in_vs_glyphIndex)]; ""\n" + " vec2 anchorPoint = glyph.anchorPoint + param_vs_currentOffset; ""\n" + " float cos_a = cos(glyph.angle); ""\n" + " float sin_a = sin(glyph.angle); ""\n" + " ""\n" + // Pixel-perfect rendering is available when angle is 0, 90, 180 or 270 degrees, what will produce + // cos_a 0, 1 or -1 + //" if (abs(cos_a - int(cos_a)) < 0.0001) ""\n" + //" { ""\n" + //" anchorPoint.x = floor(anchorPoint.x) + mod(glyph.width, 2.0) * 0.5; ""\n" + //" anchorPoint.y = floor(anchorPoint.y) + mod(param_vs_glyphHeight, 2.0) * 0.5; ""\n" + //" } ""\n" + " ""\n" + // Get on-screen glyph point offset + " vec2 glyphPoint; ""\n" + " glyphPoint.x = in_vs_vertexPosition.x * glyph.width; ""\n" + " glyphPoint.y = in_vs_vertexPosition.y * param_vs_glyphHeight; ""\n" + " ""\n" + // Get on-screen vertex coordinates + " vec4 vertexOnScreen; ""\n" + " vertexOnScreen.x = anchorPoint.x + (glyphPoint.x*cos_a - glyphPoint.y*sin_a); ""\n" + " vertexOnScreen.y = anchorPoint.y + (glyphPoint.x*sin_a + glyphPoint.y*cos_a); ""\n" + " vertexOnScreen.z = -param_vs_distanceFromCamera; ""\n" + " vertexOnScreen.w = 1.0; ""\n" + " ""\n" + // Pixel-perfect rendering is available when angle is 0, 90, 180 or 270 degrees, what will produce + // cos_a 0, 1 or -1 + //" if (abs(cos_a - int(cos_a)) < 0.0001) ""\n" + //" { ""\n" + //" vertexOnScreen.x = floor(vertexOnScreen.x + 0.5); ""\n" + //" vertexOnScreen.y = floor(vertexOnScreen.y + 0.5); ""\n" + //" } ""\n" + " ""\n" + // Project vertex + " vertexOnScreen = param_vs_mOrthographicProjection * vertexOnScreen; ""\n" + " gl_Position = vertexOnScreen * param_vs_resultScale; ""\n" + " ""\n" + // Prepare texture coordinates + " v2f_texCoords.s = glyph.widthOfPreviousN + in_vs_vertexTexCoords.s*glyph.widthN; ""\n" + " v2f_texCoords.t = in_vs_vertexTexCoords.t; // Height is compatible as-is ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + preprocessedVertexShader.replace("%MaxGlyphsPerDrawCall%", QString::number(maxGlyphsPerDrawCall)); + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); + return false; + } - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Input data - "PARAM_INPUT vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters: common data - // Parameters: per-symbol data - "uniform lowp sampler2D param_fs_sampler; ""\n" - "uniform lowp vec4 param_fs_modulationColor; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = SAMPLE_TEXTURE_2D( ""\n" - " param_fs_sampler, ""\n" - " v2f_texCoords) * param_fs_modulationColor; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Input data + "PARAM_INPUT vec2 v2f_texCoords; ""\n" + " ""\n" + // Parameters: common data + // Parameters: per-symbol data + "uniform lowp sampler2D param_fs_sampler; ""\n" + "uniform lowp vec4 param_fs_modulationColor; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = SAMPLE_TEXTURE_2D( ""\n" + " param_fs_sampler, ""\n" + " v2f_texCoords) * param_fs_modulationColor; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); - return false; + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _onPath2dProgram.id = gpuAPI->linkProgram(2, shaders, + _onPath2dProgram.binaryCache, _onPath2dProgram.cacheFormat, true, &variablesMap); } - - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _onPath2dProgram.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); if (!_onPath2dProgram.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -1057,6 +1087,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnPath2DProgram(cons ok = ok && lookup->lookupLocation(_onPath2dProgram.vs.in.glyphIndex, "in_vs_glyphIndex", GlslVariableType::In); ok = ok && lookup->lookupLocation(_onPath2dProgram.vs.in.vertexTexCoords, "in_vs_vertexTexCoords", GlslVariableType::In); ok = ok && lookup->lookupLocation(_onPath2dProgram.vs.param.mOrthographicProjection, "param_vs_mOrthographicProjection", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_onPath2dProgram.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onPath2dProgram.vs.param.glyphHeight, "param_vs_glyphHeight", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onPath2dProgram.vs.param.distanceFromCamera, "param_vs_distanceFromCamera", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onPath2dProgram.vs.param.currentOffset, "param_vs_currentOffset", GlslVariableType::Uniform); @@ -1100,6 +1131,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnPath3D() const auto alreadyOccupiedUniforms = 4 /*param_vs_mPerspectiveProjectionView*/ + + 1 /*param_vs_resultScale*/ + 1 /*param_vs_glyphHeight*/ + 1 /*param_vs_zDistanceFromCamera*/ + 1 /*param_vs_currentOffset*/; @@ -1246,124 +1278,134 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnPath3DProgram(cons GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - "INPUT float in_vs_glyphIndex; ""\n" - "INPUT vec2 in_vs_vertexTexCoords; ""\n" - " ""\n" - // Output data to next shader stages - "PARAM_OUTPUT vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" - " ""\n" - // Parameters: per-symbol data - "uniform float param_vs_glyphHeight; ""\n" - "uniform float param_vs_zDistanceFromCamera; ""\n" - "uniform vec2 param_vs_currentOffset; ""\n" - " ""\n" - // Parameters: per-glyph data - "struct Glyph ""\n" - "{ ""\n" - " vec3 anchorPoint; ""\n" - " vec4 angle; ""\n" - " float width; ""\n" - " float widthOfPreviousN; ""\n" - " float widthN; ""\n" - "}; ""\n" - "uniform Glyph param_vs_glyphs[%MaxGlyphsPerDrawCall%]; ""\n" - " ""\n" - "vec3 rotateY(vec3 p, float angle) ""\n" - "{ ""\n" - " float sn = sin(angle); ""\n" - " float cs = cos(angle); ""\n" - " float x = p.x * cs - p.z * sn; ""\n" - " float z = p.x * sn + p.z * cs; ""\n" - " return vec3(x, p.y, z); ""\n" - "} ""\n" - " ""\n" - "vec3 rotateXZ(vec3 p, float angle, vec2 u) ""\n" - "{ ""\n" - " float sn = sin(angle); ""\n" - " float cs = cos(angle); ""\n" - " float csr = 1.0 - cs; ""\n" - " float x = p.x * (cs + u.x * u.x * csr) - p.y * u.y * sn + p.z * u.x * u.y * csr; ""\n" - " float y = p.x * u.y * sn + p.y * cs - p.z * u.x * sn; ""\n" - " float z = p.x * u.x * u.y * csr + p.y * u.x * sn + p.z * (cs + u.y * u.y * csr); ""\n" - " return vec3(x, y, z); ""\n" - "} ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " Glyph glyph = param_vs_glyphs[int(in_vs_glyphIndex)]; ""\n" - " ""\n" - // Get on-screen vertex coordinates - " vec3 p; ""\n" - " p.x = in_vs_vertexPosition.x * glyph.width; ""\n" - " p.y = 0.0; ""\n" - " p.z = in_vs_vertexPosition.y * param_vs_glyphHeight; ""\n" - " p = rotateY(p, glyph.angle.y); ""\n" - " p = rotateXZ(p, glyph.angle.x, glyph.angle.zw); ""\n" - " vec4 v; ""\n" - " v.x = glyph.anchorPoint.x + p.x - param_vs_currentOffset.x; ""\n" - " v.y = glyph.anchorPoint.y + p.y; ""\n" - " v.z = glyph.anchorPoint.z + p.z - param_vs_currentOffset.y; ""\n" - " v.w = 1.0; ""\n" - " gl_Position = param_vs_mPerspectiveProjectionView * v; ""\n" - " gl_Position.z = param_vs_zDistanceFromCamera; ""\n" - " ""\n" - // Prepare texture coordinates - " v2f_texCoords.s = glyph.widthOfPreviousN + in_vs_vertexTexCoords.s*glyph.widthN; ""\n" - " v2f_texCoords.t = in_vs_vertexTexCoords.t; // Height is compatible as-is ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - preprocessedVertexShader.replace("%MaxGlyphsPerDrawCall%", QString::number(maxGlyphsPerDrawCall)); - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _onPath3dProgram.id = 0; + if (!_onPath3dProgram.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); - return false; + _onPath3dProgram.id = gpuAPI->linkProgram(0, nullptr, + _onPath3dProgram.binaryCache, _onPath3dProgram.cacheFormat, true, &variablesMap); } + if (!_onPath3dProgram.id.isValid()) + { + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; ""\n" + "INPUT float in_vs_glyphIndex; ""\n" + "INPUT vec2 in_vs_vertexTexCoords; ""\n" + " ""\n" + // Output data to next shader stages + "PARAM_OUTPUT vec2 v2f_texCoords; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + " ""\n" + // Parameters: per-symbol data + "uniform float param_vs_glyphHeight; ""\n" + "uniform float param_vs_zDistanceFromCamera; ""\n" + "uniform vec2 param_vs_currentOffset; ""\n" + " ""\n" + // Parameters: per-glyph data + "struct Glyph ""\n" + "{ ""\n" + " vec3 anchorPoint; ""\n" + " vec4 angle; ""\n" + " float width; ""\n" + " float widthOfPreviousN; ""\n" + " float widthN; ""\n" + "}; ""\n" + "uniform Glyph param_vs_glyphs[%MaxGlyphsPerDrawCall%]; ""\n" + " ""\n" + "vec3 rotateY(vec3 p, float angle) ""\n" + "{ ""\n" + " float sn = sin(angle); ""\n" + " float cs = cos(angle); ""\n" + " float x = p.x * cs - p.z * sn; ""\n" + " float z = p.x * sn + p.z * cs; ""\n" + " return vec3(x, p.y, z); ""\n" + "} ""\n" + " ""\n" + "vec3 rotateXZ(vec3 p, float angle, vec2 u) ""\n" + "{ ""\n" + " float sn = sin(angle); ""\n" + " float cs = cos(angle); ""\n" + " float csr = 1.0 - cs; ""\n" + " float x = p.x * (cs + u.x * u.x * csr) - p.y * u.y * sn + p.z * u.x * u.y * csr; ""\n" + " float y = p.x * u.y * sn + p.y * cs - p.z * u.x * sn; ""\n" + " float z = p.x * u.x * u.y * csr + p.y * u.x * sn + p.z * (cs + u.y * u.y * csr); ""\n" + " return vec3(x, y, z); ""\n" + "} ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " Glyph glyph = param_vs_glyphs[int(in_vs_glyphIndex)]; ""\n" + " ""\n" + // Get on-screen vertex coordinates + " vec3 p; ""\n" + " p.x = in_vs_vertexPosition.x * glyph.width; ""\n" + " p.y = 0.0; ""\n" + " p.z = in_vs_vertexPosition.y * param_vs_glyphHeight; ""\n" + " p = rotateY(p, glyph.angle.y); ""\n" + " p = rotateXZ(p, glyph.angle.x, glyph.angle.zw); ""\n" + " vec4 v; ""\n" + " v.x = glyph.anchorPoint.x + p.x - param_vs_currentOffset.x; ""\n" + " v.y = glyph.anchorPoint.y + p.y; ""\n" + " v.z = glyph.anchorPoint.z + p.z - param_vs_currentOffset.y; ""\n" + " v.w = 1.0; ""\n" + " v = param_vs_mPerspectiveProjectionView * v; ""\n" + " v.z = param_vs_zDistanceFromCamera; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + " ""\n" + // Prepare texture coordinates + " v2f_texCoords.s = glyph.widthOfPreviousN + in_vs_vertexTexCoords.s*glyph.widthN; ""\n" + " v2f_texCoords.t = in_vs_vertexTexCoords.t; // Height is compatible as-is ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + preprocessedVertexShader.replace("%MaxGlyphsPerDrawCall%", QString::number(maxGlyphsPerDrawCall)); + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); + return false; + } - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Input data - "PARAM_INPUT vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters: common data - // Parameters: per-symbol data - "uniform lowp sampler2D param_fs_sampler; ""\n" - "uniform lowp vec4 param_fs_modulationColor; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = SAMPLE_TEXTURE_2D( ""\n" - " param_fs_sampler, ""\n" - " v2f_texCoords) * param_fs_modulationColor; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Input data + "PARAM_INPUT vec2 v2f_texCoords; ""\n" + " ""\n" + // Parameters: common data + // Parameters: per-symbol data + "uniform lowp sampler2D param_fs_sampler; ""\n" + "uniform lowp vec4 param_fs_modulationColor; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = SAMPLE_TEXTURE_2D( ""\n" + " param_fs_sampler, ""\n" + " v2f_texCoords) * param_fs_modulationColor; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); - return false; + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _onPath3dProgram.id = gpuAPI->linkProgram(2, shaders, + _onPath3dProgram.binaryCache, _onPath3dProgram.cacheFormat, true, &variablesMap); } - - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _onPath3dProgram.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); if (!_onPath3dProgram.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -1377,6 +1419,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnPath3DProgram(cons ok = ok && lookup->lookupLocation(_onPath3dProgram.vs.in.glyphIndex, "in_vs_glyphIndex", GlslVariableType::In); ok = ok && lookup->lookupLocation(_onPath3dProgram.vs.in.vertexTexCoords, "in_vs_vertexTexCoords", GlslVariableType::In); ok = ok && lookup->lookupLocation(_onPath3dProgram.vs.param.mPerspectiveProjectionView, "param_vs_mPerspectiveProjectionView", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_onPath3dProgram.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onPath3dProgram.vs.param.glyphHeight, "param_vs_glyphHeight", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onPath3dProgram.vs.param.zDistanceFromCamera, "param_vs_zDistanceFromCamera", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onPath3dProgram.vs.param.currentOffset, "param_vs_currentOffset", GlslVariableType::Uniform); @@ -1435,6 +1478,14 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::renderOnPath2dSymbol( glUniformMatrix4fv(_onPath2dProgram.vs.param.mOrthographicProjection, 1, GL_FALSE, glm::value_ptr(internalState.mOrthographicProjection)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_onPath2dProgram.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + // Activate texture block for symbol textures glActiveTexture(GL_TEXTURE0 + 0); GL_CHECK_RESULT; @@ -1611,6 +1662,14 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::renderOnPath3dSymbol( glUniformMatrix4fv(_onPath3dProgram.vs.param.mPerspectiveProjectionView, 1, GL_FALSE, glm::value_ptr(internalState.mPerspectiveProjectionView)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_onPath3dProgram.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + // Activate texture block for symbol textures glActiveTexture(GL_TEXTURE0 + 0); GL_CHECK_RESULT; @@ -1805,7 +1864,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::releaseOnPath2D(bool gpuContex glDeleteProgram(_onPath2dProgram.id); GL_CHECK_RESULT; } - _onPath2dProgram = OnPathSymbol2dProgram(); + _onPath2dProgram.id = 0; } return true; @@ -1850,7 +1909,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::releaseOnPath3D(bool gpuContex glDeleteProgram(_onPath3dProgram.id); GL_CHECK_RESULT; } - _onPath3dProgram = OnPathSymbol3dProgram(); + _onPath3dProgram.id = 0; } return true; @@ -1868,88 +1927,98 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnSurfaceRaster() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - "INPUT vec2 in_vs_vertexTexCoords; ""\n" - " ""\n" - // Output data to next shader stages - "PARAM_OUTPUT vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters: common data - "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" - " ""\n" - // Parameters: per-symbol data - "uniform vec2 param_vs_symbolOffsetFromTarget; ""\n" - "uniform float param_vs_direction; ""\n" - "uniform vec2 param_vs_symbolSize; ""\n" - "uniform float param_vs_elevationInWorld; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - // Get vertex coordinates in world - " float cos_a = cos(param_vs_direction); ""\n" - " float sin_a = sin(param_vs_direction); ""\n" - " vec2 p; ""\n" - " p.x = in_vs_vertexPosition.x * param_vs_symbolSize.x; ""\n" - " p.y = in_vs_vertexPosition.y * param_vs_symbolSize.y; ""\n" - " vec4 v; ""\n" - " v.x = param_vs_symbolOffsetFromTarget.x * %TileSize3D%.0 + (p.x*cos_a - p.y*sin_a); ""\n" - " v.y = param_vs_elevationInWorld; ""\n" - " v.z = param_vs_symbolOffsetFromTarget.y * %TileSize3D%.0 + (p.x*sin_a + p.y*cos_a); ""\n" - " v.w = 1.0; ""\n" - " gl_Position = param_vs_mPerspectiveProjectionView * v; ""\n" - " ""\n" - // Prepare texture coordinates - " v2f_texCoords = in_vs_vertexTexCoords; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - preprocessedVertexShader.replace("%TileSize3D%", QString::number(AtlasMapRenderer::TileSize3D)); - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _onSurfaceRasterProgram.id = 0; + if (!_onSurfaceRasterProgram.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); - return false; + _onSurfaceRasterProgram.id = gpuAPI->linkProgram(0, nullptr, + _onSurfaceRasterProgram.binaryCache, _onSurfaceRasterProgram.cacheFormat, true, &variablesMap); } + if (!_onSurfaceRasterProgram.id.isValid()) + { + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec2 in_vs_vertexPosition; ""\n" + "INPUT vec2 in_vs_vertexTexCoords; ""\n" + " ""\n" + // Output data to next shader stages + "PARAM_OUTPUT vec2 v2f_texCoords; ""\n" + " ""\n" + // Parameters: common data + "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + " ""\n" + // Parameters: per-symbol data + "uniform vec2 param_vs_symbolOffsetFromTarget; ""\n" + "uniform float param_vs_direction; ""\n" + "uniform vec2 param_vs_symbolSize; ""\n" + "uniform float param_vs_elevationInWorld; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + // Get vertex coordinates in world + " float cos_a = cos(param_vs_direction); ""\n" + " float sin_a = sin(param_vs_direction); ""\n" + " vec2 p; ""\n" + " p.x = in_vs_vertexPosition.x * param_vs_symbolSize.x; ""\n" + " p.y = in_vs_vertexPosition.y * param_vs_symbolSize.y; ""\n" + " vec4 v; ""\n" + " v.x = param_vs_symbolOffsetFromTarget.x * %TileSize3D%.0 + (p.x*cos_a - p.y*sin_a); ""\n" + " v.y = param_vs_elevationInWorld; ""\n" + " v.z = param_vs_symbolOffsetFromTarget.y * %TileSize3D%.0 + (p.x*sin_a + p.y*cos_a); ""\n" + " v.w = 1.0; ""\n" + " v = param_vs_mPerspectiveProjectionView * v; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + " ""\n" + // Prepare texture coordinates + " v2f_texCoords = in_vs_vertexTexCoords; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + preprocessedVertexShader.replace("%TileSize3D%", QString::number(AtlasMapRenderer::TileSize3D)); + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); + return false; + } - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Input data - "PARAM_INPUT vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters: common data - // Parameters: per-symbol data - "uniform lowp sampler2D param_fs_sampler; ""\n" - "uniform lowp vec4 param_fs_modulationColor; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = SAMPLE_TEXTURE_2D( ""\n" - " param_fs_sampler, ""\n" - " v2f_texCoords) * param_fs_modulationColor; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Input data + "PARAM_INPUT vec2 v2f_texCoords; ""\n" + " ""\n" + // Parameters: common data + // Parameters: per-symbol data + "uniform lowp sampler2D param_fs_sampler; ""\n" + "uniform lowp vec4 param_fs_modulationColor; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = SAMPLE_TEXTURE_2D( ""\n" + " param_fs_sampler, ""\n" + " v2f_texCoords) * param_fs_modulationColor; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); - return false; + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _onSurfaceRasterProgram.id = gpuAPI->linkProgram(2, shaders, + _onSurfaceRasterProgram.binaryCache, _onSurfaceRasterProgram.cacheFormat, true, &variablesMap); } - - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _onSurfaceRasterProgram.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); if (!_onSurfaceRasterProgram.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -1962,6 +2031,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnSurfaceRaster() ok = ok && lookup->lookupLocation(_onSurfaceRasterProgram.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_onSurfaceRasterProgram.vs.in.vertexTexCoords, "in_vs_vertexTexCoords", GlslVariableType::In); ok = ok && lookup->lookupLocation(_onSurfaceRasterProgram.vs.param.mPerspectiveProjectionView, "param_vs_mPerspectiveProjectionView", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_onSurfaceRasterProgram.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onSurfaceRasterProgram.vs.param.symbolOffsetFromTarget, "param_vs_symbolOffsetFromTarget", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onSurfaceRasterProgram.vs.param.direction, "param_vs_direction", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onSurfaceRasterProgram.vs.param.symbolSize, "param_vs_symbolSize", GlslVariableType::Uniform); @@ -2072,6 +2142,14 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::renderOnSurfaceRasterSymbol( glUniformMatrix4fv(_onSurfaceRasterProgram.vs.param.mPerspectiveProjectionView, 1, GL_FALSE, glm::value_ptr(internalState.mPerspectiveProjectionView)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_onSurfaceRasterProgram.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + // Activate texture block for symbol textures glActiveTexture(GL_TEXTURE0 + 0); GL_CHECK_RESULT; @@ -2219,7 +2297,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::releaseOnSurfaceRaster(bool gp glDeleteProgram(_onSurfaceRasterProgram.id); GL_CHECK_RESULT; } - _onSurfaceRasterProgram = OnSurfaceRasterProgram(); + _onSurfaceRasterProgram.id = 0; } return true; @@ -2232,132 +2310,142 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnSurfaceVector() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT vec4 in_vs_vertexPosition; ""\n" - "INPUT vec4 in_vs_vertexColor; ""\n" - " ""\n" - // Output data to next shader stages - "PARAM_OUTPUT vec4 v2f_color; ""\n" - "PARAM_OUTPUT float v2f_distance; ""\n" - " ""\n" - // Parameters: common data - "uniform vec4 param_vs_elevation_scale; ""\n" - "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" - "uniform mat4 param_vs_mModel; ""\n" - "uniform lowp vec4 param_vs_modulationColor; ""\n" - "uniform vec2 param_vs_tileId; ""\n" - "uniform vec4 param_vs_lookupOffsetAndScale; ""\n" - "uniform vec4 param_vs_cameraPositionAndZfar; ""\n" - "uniform float param_vs_elevationInMeters; ""\n" - "uniform float param_vs_elevationFactor; ""\n" - "uniform highp vec2 param_vs_offsetInTile; ""\n" - "uniform highp sampler2D param_vs_elevation_dataSampler; ""\n" - "uniform highp vec4 param_vs_texCoordsOffsetAndScale; ""\n" - "uniform highp vec4 param_vs_elevationLayerDataPlace; ""\n" - " ""\n" - "float interpolatedHeight(in vec2 inTexCoords) ""\n" - "{ ""\n" - " vec2 heixelSize = param_vs_elevationLayerDataPlace.zw * 2.0; ""\n" - " vec2 texCoords = (inTexCoords - param_vs_elevationLayerDataPlace.zw) / heixelSize; ""\n" - " vec2 pixOffset = fract(texCoords); ""\n" - " texCoords = floor(texCoords) * heixelSize + param_vs_elevationLayerDataPlace.zw; ""\n" - " vec2 minCoords = param_vs_elevationLayerDataPlace.xy - heixelSize; ""\n" - " vec2 maxCoords = minCoords + heixelSize * (%HeixelsPerTileSide%.0 + 2.0); ""\n" - " float blHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" - " texCoords.x += heixelSize.x; ""\n" - " float brHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" - " texCoords.y += heixelSize.y; ""\n" - " float trHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" - " texCoords.x -= heixelSize.x; ""\n" - " float tlHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" - " float avbPixel = mix(blHeixel, brHeixel, pixOffset.x); ""\n" - " float avtPixel = mix(tlHeixel, trHeixel, pixOffset.x); ""\n" - " return mix(avbPixel, avtPixel, pixOffset.y); ""\n" - "} ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - // Get vertex coordinates in world - " vec4 v; ""\n" - " v.x = in_vs_vertexPosition.x; ""\n" - " v.y = 0.0; ""\n" - " v.z = in_vs_vertexPosition.z; ""\n" - " v.w = 1.0; ""\n" - " bool isElevated = param_vs_elevationInMeters > -12000000.0; ""\n" - " bool withHeight = in_vs_vertexPosition.y > -12000000.0; ""\n" - " bool withSurface = abs(param_vs_elevation_scale.w) > 0.0; ""\n" - " vec2 vertexTexCoords = v.xz * param_vs_lookupOffsetAndScale.z + param_vs_lookupOffsetAndScale.xy; ""\n" - " v = param_vs_mModel * v; ""\n" - " vertexTexCoords -= param_vs_tileId; ""\n" - " vec2 elevationTexCoords = vertexTexCoords * param_vs_texCoordsOffsetAndScale.zw; ""\n" - " elevationTexCoords += param_vs_texCoordsOffsetAndScale.xy; ""\n" - " float surfaceInMeters = withSurface ? interpolatedHeight(elevationTexCoords) : 0.0; ""\n" - " float tileOffset = withSurface ? vertexTexCoords.t : param_vs_offsetInTile.y; ""\n" - " float metersPerUnit = mix(param_vs_elevation_scale.x, param_vs_elevation_scale.y, tileOffset); ""\n" - " v.y = surfaceInMeters * param_vs_elevation_scale.w * param_vs_elevation_scale.z / metersPerUnit; ""\n" - " float elevation = withHeight ? in_vs_vertexPosition.y : surfaceInMeters; ""\n" - " elevation = ((isElevated ? param_vs_elevationInMeters : elevation) - surfaceInMeters) / metersPerUnit; ""\n" - " v.y += elevation * param_vs_elevationFactor; ""\n" - " float dist = distance(param_vs_cameraPositionAndZfar.xyz, v.xyz); ""\n" - " float extraZfar = 2.0 * dist / param_vs_cameraPositionAndZfar.w; ""\n" - " float extraCam = dist / length(param_vs_cameraPositionAndZfar.xyz); ""\n" - " v.y += min(extraZfar, extraCam) + 0.1; ""\n" - " gl_Position = param_vs_mPerspectiveProjectionView * v; ""\n" - " ""\n" - // Prepare distance - " v2f_distance = in_vs_vertexPosition.w; ""\n" - // Prepare color - " v2f_color.argb = in_vs_vertexColor.xyzw * param_vs_modulationColor.argb; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - preprocessedVertexShader.replace("%TileSize3D%", QString::number(AtlasMapRenderer::TileSize3D)); - preprocessedVertexShader.replace("%HeixelsPerTileSide%", - QString::number(AtlasMapRenderer::HeixelsPerTileSide - 1)); - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _onSurfaceVectorProgram.id = 0; + if (!_onSurfaceVectorProgram.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); - return false; + _onSurfaceVectorProgram.id = gpuAPI->linkProgram(0, nullptr, + _onSurfaceVectorProgram.binaryCache, _onSurfaceVectorProgram.cacheFormat, true, &variablesMap); } + if (!_onSurfaceVectorProgram.id.isValid()) + { + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT vec4 in_vs_vertexPosition; ""\n" + "INPUT vec4 in_vs_vertexColor; ""\n" + " ""\n" + // Output data to next shader stages + "PARAM_OUTPUT vec4 v2f_color; ""\n" + "PARAM_OUTPUT float v2f_distance; ""\n" + " ""\n" + // Parameters: common data + "uniform vec4 param_vs_elevation_scale; ""\n" + "uniform mat4 param_vs_mPerspectiveProjectionView; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + "uniform mat4 param_vs_mModel; ""\n" + "uniform lowp vec4 param_vs_modulationColor; ""\n" + "uniform vec2 param_vs_tileId; ""\n" + "uniform vec4 param_vs_lookupOffsetAndScale; ""\n" + "uniform vec4 param_vs_cameraPositionAndZfar; ""\n" + "uniform float param_vs_elevationInMeters; ""\n" + "uniform float param_vs_elevationFactor; ""\n" + "uniform highp vec2 param_vs_offsetInTile; ""\n" + "uniform highp sampler2D param_vs_elevation_dataSampler; ""\n" + "uniform highp vec4 param_vs_texCoordsOffsetAndScale; ""\n" + "uniform highp vec4 param_vs_elevationLayerDataPlace; ""\n" + " ""\n" + "float interpolatedHeight(in vec2 inTexCoords) ""\n" + "{ ""\n" + " vec2 heixelSize = param_vs_elevationLayerDataPlace.zw * 2.0; ""\n" + " vec2 texCoords = (inTexCoords - param_vs_elevationLayerDataPlace.zw) / heixelSize; ""\n" + " vec2 pixOffset = fract(texCoords); ""\n" + " texCoords = floor(texCoords) * heixelSize + param_vs_elevationLayerDataPlace.zw; ""\n" + " vec2 minCoords = param_vs_elevationLayerDataPlace.xy - heixelSize; ""\n" + " vec2 maxCoords = minCoords + heixelSize * (%HeixelsPerTileSide%.0 + 2.0); ""\n" + " float blHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" + " texCoords.x += heixelSize.x; ""\n" + " float brHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" + " texCoords.y += heixelSize.y; ""\n" + " float trHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" + " texCoords.x -= heixelSize.x; ""\n" + " float tlHeixel = SAMPLE_TEXTURE_2D(param_vs_elevation_dataSampler, clamp(texCoords, minCoords, maxCoords)).r; ""\n" + " float avbPixel = mix(blHeixel, brHeixel, pixOffset.x); ""\n" + " float avtPixel = mix(tlHeixel, trHeixel, pixOffset.x); ""\n" + " return mix(avbPixel, avtPixel, pixOffset.y); ""\n" + "} ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + // Get vertex coordinates in world + " vec4 v; ""\n" + " v.x = in_vs_vertexPosition.x; ""\n" + " v.y = 0.0; ""\n" + " v.z = in_vs_vertexPosition.z; ""\n" + " v.w = 1.0; ""\n" + " bool isElevated = param_vs_elevationInMeters > -12000000.0; ""\n" + " bool withHeight = in_vs_vertexPosition.y > -12000000.0; ""\n" + " bool withSurface = abs(param_vs_elevation_scale.w) > 0.0; ""\n" + " vec2 vertexTexCoords = v.xz * param_vs_lookupOffsetAndScale.z + param_vs_lookupOffsetAndScale.xy; ""\n" + " v = param_vs_mModel * v; ""\n" + " vertexTexCoords -= param_vs_tileId; ""\n" + " vec2 elevationTexCoords = vertexTexCoords * param_vs_texCoordsOffsetAndScale.zw; ""\n" + " elevationTexCoords += param_vs_texCoordsOffsetAndScale.xy; ""\n" + " float surfaceInMeters = withSurface ? interpolatedHeight(elevationTexCoords) : 0.0; ""\n" + " float tileOffset = withSurface ? vertexTexCoords.t : param_vs_offsetInTile.y; ""\n" + " float metersPerUnit = mix(param_vs_elevation_scale.x, param_vs_elevation_scale.y, tileOffset); ""\n" + " v.y = surfaceInMeters * param_vs_elevation_scale.w * param_vs_elevation_scale.z / metersPerUnit; ""\n" + " float elevation = withHeight ? in_vs_vertexPosition.y : surfaceInMeters; ""\n" + " elevation = ((isElevated ? param_vs_elevationInMeters : elevation) - surfaceInMeters) / metersPerUnit; ""\n" + " v.y += elevation * param_vs_elevationFactor; ""\n" + " float dist = distance(param_vs_cameraPositionAndZfar.xyz, v.xyz); ""\n" + " float extraZfar = 2.0 * dist / param_vs_cameraPositionAndZfar.w; ""\n" + " float extraCam = dist / length(param_vs_cameraPositionAndZfar.xyz); ""\n" + " v.y += min(extraZfar, extraCam) + 0.1; ""\n" + " v = param_vs_mPerspectiveProjectionView * v; ""\n" + " gl_Position = v * param_vs_resultScale; ""\n" + " ""\n" + // Prepare distance + " v2f_distance = in_vs_vertexPosition.w; ""\n" + // Prepare color + " v2f_color.argb = in_vs_vertexColor.xyzw * param_vs_modulationColor.argb; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + preprocessedVertexShader.replace("%TileSize3D%", QString::number(AtlasMapRenderer::TileSize3D)); + preprocessedVertexShader.replace("%HeixelsPerTileSide%", + QString::number(AtlasMapRenderer::HeixelsPerTileSide - 1)); + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); + return false; + } - // Compile fragment shader - const QString fragmentShader = QLatin1String( - // Input data - "PARAM_INPUT vec4 v2f_color; ""\n" - "PARAM_INPUT float v2f_distance; ""\n" - " ""\n" - // Parameters: common data - "uniform float param_fs_startingDistance; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " vec4 outColor = v2f_color; ""\n" - " outColor.a = v2f_distance < param_fs_startingDistance ? 0.0 : outColor.a; ""\n" - " FRAGMENT_COLOR_OUTPUT = outColor; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile fragment shader + const QString fragmentShader = QLatin1String( + // Input data + "PARAM_INPUT vec4 v2f_color; ""\n" + "PARAM_INPUT float v2f_distance; ""\n" + " ""\n" + // Parameters: common data + "uniform float param_fs_startingDistance; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + " vec4 outColor = v2f_color; ""\n" + " outColor.a = v2f_distance < param_fs_startingDistance ? 0.0 : outColor.a; ""\n" + " FRAGMENT_COLOR_OUTPUT = outColor; ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); - return false; + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _onSurfaceVectorProgram.id = gpuAPI->linkProgram(2, shaders, + _onSurfaceVectorProgram.binaryCache, _onSurfaceVectorProgram.cacheFormat, true, &variablesMap); } - - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _onSurfaceVectorProgram.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); if (!_onSurfaceVectorProgram.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -2370,6 +2458,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeOnSurfaceVector() ok = ok && lookup->lookupLocation(_onSurfaceVectorProgram.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); ok = ok && lookup->lookupLocation(_onSurfaceVectorProgram.vs.in.vertexColor, "in_vs_vertexColor", GlslVariableType::In); ok = ok && lookup->lookupLocation(_onSurfaceVectorProgram.vs.param.mPerspectiveProjectionView, "param_vs_mPerspectiveProjectionView", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_onSurfaceVectorProgram.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onSurfaceVectorProgram.vs.param.mModel, "param_vs_mModel", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onSurfaceVectorProgram.vs.param.modulationColor, "param_vs_modulationColor", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_onSurfaceVectorProgram.vs.param.tileId, "param_vs_tileId", GlslVariableType::Uniform); @@ -2419,6 +2508,14 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::renderOnSurfaceVectorSymbol( glUniformMatrix4fv(_onSurfaceVectorProgram.vs.param.mPerspectiveProjectionView, 1, GL_FALSE, glm::value_ptr(internalState.mPerspectiveProjectionView)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_onSurfaceVectorProgram.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + // Just in case un-use any possibly used VAO gpuAPI->unuseVAO(); @@ -2855,7 +2952,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::releaseOnSurfaceVector(bool gp glDeleteProgram(_onSurfaceVectorProgram.id); GL_CHECK_RESULT; } - _onSurfaceVectorProgram = OnSurfaceVectorProgram(); + _onSurfaceVectorProgram.id = 0; } return true; @@ -2874,64 +2971,74 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeVisibilityCheck() GL_CHECK_PRESENT(glDeleteShader); GL_CHECK_PRESENT(glDeleteProgram); - // Compile vertex shader - const QString vertexShader = QLatin1String( - // Input data - "INPUT lowp float in_vs_vertexPosition; ""\n" - // Parameters: common data - "uniform vec3 param_vs_firstPointPosition; ""\n" - "uniform vec3 param_vs_secondPointPosition; ""\n" - "uniform vec3 param_vs_thirdPointPosition; ""\n" - "uniform vec3 param_vs_fourthPointPosition; ""\n" - "uniform vec4 param_vs_cameraInWorld; ""\n" - "uniform mat4 param_vs_mModelViewProjection; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - // Get vertex coordinates in world - " vec3 v = in_vs_vertexPosition < 2.5 ? param_vs_thirdPointPosition : param_vs_fourthPointPosition; ""\n" - " v = in_vs_vertexPosition < 1.5 ? param_vs_secondPointPosition : v; ""\n" - " v = in_vs_vertexPosition < 0.5 ? param_vs_firstPointPosition : v; ""\n" - " v.y += 0.2; ""\n" - " v += normalize(param_vs_cameraInWorld.xyz - v) * 0.3; ""\n" - " gl_PointSize = param_vs_cameraInWorld.w; ""\n" - " gl_Position = param_vs_mModelViewProjection * vec4(v.x, v.y, v.z, 1.0); ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - gpuAPI->preprocessVertexShader(preprocessedVertexShader); - gpuAPI->optimizeVertexShader(preprocessedVertexShader); - const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) + QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; + _visibilityCheckProgram.id = 0; + if (!_visibilityCheckProgram.binaryCache.isEmpty()) { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); - return false; + _visibilityCheckProgram.id = gpuAPI->linkProgram(0, nullptr, + _visibilityCheckProgram.binaryCache, _visibilityCheckProgram.cacheFormat, true, &variablesMap); } - - // Compile fragment shader - const QString fragmentShader = QLatin1String( - "void main() ""\n" - "{ ""\n" - " FRAGMENT_COLOR_OUTPUT = vec4(0.0, 0.0, 0.0, 0.0); ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); - gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) + if (!_visibilityCheckProgram.id.isValid()) { - glDeleteShader(vsId); - GL_CHECK_RESULT; + // Compile vertex shader + const QString vertexShader = QLatin1String( + // Input data + "INPUT lowp float in_vs_vertexPosition; ""\n" + // Parameters: common data + "uniform vec3 param_vs_firstPointPosition; ""\n" + "uniform vec3 param_vs_secondPointPosition; ""\n" + "uniform vec3 param_vs_thirdPointPosition; ""\n" + "uniform vec3 param_vs_fourthPointPosition; ""\n" + "uniform vec4 param_vs_cameraInWorld; ""\n" + "uniform mat4 param_vs_mModelViewProjection; ""\n" + "uniform vec4 param_vs_resultScale; ""\n" + " ""\n" + "void main() ""\n" + "{ ""\n" + // Get vertex coordinates in world + " vec3 v = in_vs_vertexPosition < 2.5 ? param_vs_thirdPointPosition : param_vs_fourthPointPosition; ""\n" + " v = in_vs_vertexPosition < 1.5 ? param_vs_secondPointPosition : v; ""\n" + " v = in_vs_vertexPosition < 0.5 ? param_vs_firstPointPosition : v; ""\n" + " v.y += 0.2; ""\n" + " v += normalize(param_vs_cameraInWorld.xyz - v) * 0.3; ""\n" + " gl_PointSize = param_vs_cameraInWorld.w; ""\n" + " vec4 vertex = param_vs_mModelViewProjection * vec4(v.x, v.y, v.z, 1.0); ""\n" + " gl_Position = vertex * param_vs_resultScale; ""\n" + "} ""\n"); + auto preprocessedVertexShader = vertexShader; + gpuAPI->preprocessVertexShader(preprocessedVertexShader); + gpuAPI->optimizeVertexShader(preprocessedVertexShader); + const auto vsId = gpuAPI->compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); + if (vsId == 0) + { + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL vertex shader"); + return false; + } - LogPrintf(LogSeverityLevel::Error, - "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); - return false; - } + // Compile fragment shader + const QString fragmentShader = QLatin1String( + "void main() ""\n" + "{ ""\n" + " FRAGMENT_COLOR_OUTPUT = vec4(0.0, 0.0, 0.0, 0.0); ""\n" + "} ""\n"); + auto preprocessedFragmentShader = fragmentShader; + gpuAPI->preprocessFragmentShader(preprocessedFragmentShader); + gpuAPI->optimizeFragmentShader(preprocessedFragmentShader); + const auto fsId = gpuAPI->compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); + if (fsId == 0) + { + glDeleteShader(vsId); + GL_CHECK_RESULT; - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _visibilityCheckProgram.id = gpuAPI->linkProgram(2, shaders, true, &variablesMap); + LogPrintf(LogSeverityLevel::Error, + "Failed to compile AtlasMapRendererSymbolsStage_OpenGL fragment shader"); + return false; + } + GLuint shaders[] = { vsId, fsId }; + _visibilityCheckProgram.id = gpuAPI->linkProgram(2, shaders, + _visibilityCheckProgram.binaryCache, _visibilityCheckProgram.cacheFormat, true, &variablesMap); + } if (!_visibilityCheckProgram.id.isValid()) { LogPrintf(LogSeverityLevel::Error, @@ -2948,6 +3055,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::initializeVisibilityCheck() ok = ok && lookup->lookupLocation(_visibilityCheckProgram.vs.param.fourthPointPosition, "param_vs_fourthPointPosition", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_visibilityCheckProgram.vs.param.cameraInWorld, "param_vs_cameraInWorld", GlslVariableType::Uniform); ok = ok && lookup->lookupLocation(_visibilityCheckProgram.vs.param.mModelViewProjection, "param_vs_mModelViewProjection", GlslVariableType::Uniform); + ok = ok && lookup->lookupLocation(_visibilityCheckProgram.vs.param.resultScale, "param_vs_resultScale", GlslVariableType::Uniform); if (!ok) { glDeleteProgram(_visibilityCheckProgram.id); @@ -3025,7 +3133,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::releaseVisibilityCheck(bool gp glDeleteProgram(_visibilityCheckProgram.id); GL_CHECK_RESULT; } - _visibilityCheckProgram = VisibilityCheckProgram(); + _visibilityCheckProgram.id = 0; } return true; @@ -3112,6 +3220,14 @@ int OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::startTerrainVisibilityFiltering glm::value_ptr(internalState.mPerspectiveProjectionView)); GL_CHECK_RESULT; + // Scale the result + glUniform4f(_visibilityCheckProgram.vs.param.resultScale, + 1.0f, + currentState.flip ? -1.0f : 1.0f, + 1.0f, + 1.0f); + GL_CHECK_RESULT; + // Change depth test function to perform > depth test glDepthFunc(GL_GREATER); GL_CHECK_RESULT; diff --git a/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.h b/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.h index fbe2f5630..1dd345359 100644 --- a/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.h +++ b/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.h @@ -32,6 +32,8 @@ namespace OsmAnd GLname _billboardRasterSymbolIBO; struct BillboardRasterSymbolProgram { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -45,6 +47,7 @@ namespace OsmAnd // Common data GLlocation mPerspectiveProjectionView; GLlocation mOrthographicProjection; + GLlocation resultScale; GLlocation viewport; GLlocation target31; @@ -93,6 +96,8 @@ namespace OsmAnd GLname _onPathSymbol2dIBO; struct OnPathSymbol2dProgram { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -106,6 +111,7 @@ namespace OsmAnd struct { // Common data GLlocation mOrthographicProjection; + GLlocation resultScale; // Per-symbol data GLlocation glyphHeight; @@ -141,6 +147,8 @@ namespace OsmAnd GLname _onPathSymbol3dIBO; struct OnPathSymbol3dProgram { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -154,6 +162,7 @@ namespace OsmAnd struct { // Common data GLlocation mPerspectiveProjectionView; + GLlocation resultScale; // Per-symbol data GLlocation glyphHeight; @@ -193,6 +202,8 @@ namespace OsmAnd GLname _onSurfaceRasterSymbolIBO; struct OnSurfaceRasterProgram { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -205,6 +216,7 @@ namespace OsmAnd struct { // Common data GLlocation mPerspectiveProjectionView; + GLlocation resultScale; // Per-symbol data GLlocation symbolOffsetFromTarget; @@ -233,6 +245,8 @@ namespace OsmAnd struct OnSurfaceVectorProgram { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -245,6 +259,7 @@ namespace OsmAnd struct { // Common data GLlocation mPerspectiveProjectionView; + GLlocation resultScale; GLlocation mModel; GLlocation modulationColor; GLlocation tileId; @@ -287,6 +302,8 @@ namespace OsmAnd GLname _visibilityCheckVBO; struct VisibilityCheckProgram { GLname id; + QByteArray binaryCache; + GLenum cacheFormat; struct { // Input data @@ -303,6 +320,7 @@ namespace OsmAnd GLlocation fourthPointPosition; GLlocation cameraInWorld; GLlocation mModelViewProjection; + GLlocation resultScale; } param; } vs; diff --git a/src/Map/OpenGL/AtlasMapRenderer_OpenGL.cpp b/src/Map/OpenGL/AtlasMapRenderer_OpenGL.cpp index e4f4eba38..898ddeb74 100644 --- a/src/Map/OpenGL/AtlasMapRenderer_OpenGL.cpp +++ b/src/Map/OpenGL/AtlasMapRenderer_OpenGL.cpp @@ -68,8 +68,6 @@ OsmAnd::AtlasMapRenderer_OpenGL::AtlasMapRenderer_OpenGL(GPUAPI_OpenGL* const gp gpuAPI_, std::unique_ptr(new AtlasMapRendererConfiguration()), std::unique_ptr(new MapRendererDebugSettings())) - , terrainDepthBuffer(_terrainDepthBuffer) - , terrainDepthBufferSize(_terrainDepthBufferSize) { } @@ -77,7 +75,7 @@ OsmAnd::AtlasMapRenderer_OpenGL::~AtlasMapRenderer_OpenGL() { } -bool OsmAnd::AtlasMapRenderer_OpenGL::doInitializeRendering() +bool OsmAnd::AtlasMapRenderer_OpenGL::doInitializeRendering(bool reinitialize) { GL_CHECK_PRESENT(glClearColor); @@ -85,16 +83,13 @@ bool OsmAnd::AtlasMapRenderer_OpenGL::doInitializeRendering() bool ok; - ok = AtlasMapRenderer::doInitializeRendering(); + ok = AtlasMapRenderer::doInitializeRendering(reinitialize); if (!ok) return false; glClearColor(0.0f, 0.0f, 0.0f, 0.0f); GL_CHECK_RESULT; - gpuAPI->glClearDepth_wrapper(1.0f); - GL_CHECK_RESULT; - return true; } @@ -147,11 +142,6 @@ bool OsmAnd::AtlasMapRenderer_OpenGL::doRenderFrame(IMapRenderer_Metrics::Metric glDisable(GL_DEPTH_TEST); GL_CHECK_RESULT; -// // Turn off writing to the color buffer for the sky (depth only) -// glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); -// GL_CHECK_RESULT; - - // Render the sky (depth only) -- ??? // Render the sky if (!currentDebugSettings->disableSkyStage) { @@ -182,7 +172,7 @@ bool OsmAnd::AtlasMapRenderer_OpenGL::doRenderFrame(IMapRenderer_Metrics::Metric glEnable(GL_CULL_FACE); GL_CHECK_RESULT; - glCullFace(GL_FRONT); + glCullFace(currentState.flip ? GL_BACK : GL_FRONT); GL_CHECK_RESULT; Stopwatch mapLayersStageStopwatch(metric != nullptr); @@ -196,19 +186,6 @@ bool OsmAnd::AtlasMapRenderer_OpenGL::doRenderFrame(IMapRenderer_Metrics::Metric GL_CHECK_RESULT; } - /* Disable depth buffer reading - // Capture terrain depth buffer - if (_terrainDepthBuffer.size() > 0 && _terrainDepthBufferSize == currentState.windowSize) - { - Stopwatch terrainDepthBufferCaptureStopwatch(metric != nullptr); - - gpuAPI->readFramebufferDepth(0, 0, _terrainDepthBufferSize.x, _terrainDepthBufferSize.y, _terrainDepthBuffer); - GL_CHECK_RESULT; - - if (metric) - metric->elapsedTimeForTerrainDepthBufferCapture = terrainDepthBufferCaptureStopwatch.elapsed(); - } - */ // Turn on blending since now objects with transparency are going to be rendered glEnable(GL_BLEND); GL_CHECK_RESULT; @@ -269,21 +246,8 @@ bool OsmAnd::AtlasMapRenderer_OpenGL::doReleaseRendering(bool gpuContextLost) bool OsmAnd::AtlasMapRenderer_OpenGL::handleStateChange(const MapRendererState& state, MapRendererStateChanges mask) { - const auto gpuAPI = getGPUAPI(); - bool ok = AtlasMapRenderer::handleStateChange(state, mask); -/* Disable depth buffer reading - if (mask.isSet(MapRendererStateChange::WindowSize)) - { - const auto depthBufferSize = state.windowSize.x * state.windowSize.y * gpuAPI->framebufferDepthBytes; - if (depthBufferSize != _terrainDepthBuffer.size()) - { - _terrainDepthBuffer.resize(depthBufferSize); - _terrainDepthBufferSize = state.windowSize; - } - } -*/ return ok; } diff --git a/src/Map/OpenGL/AtlasMapRenderer_OpenGL.h b/src/Map/OpenGL/AtlasMapRenderer_OpenGL.h index ef52f101a..6b4c29cdf 100644 --- a/src/Map/OpenGL/AtlasMapRenderer_OpenGL.h +++ b/src/Map/OpenGL/AtlasMapRenderer_OpenGL.h @@ -40,8 +40,6 @@ namespace OsmAnd const static float _minimumVisualZoom; const static float _maximumVisualZoom; const static double _minimumElevationAngle; - std::vector _terrainDepthBuffer; - PointI _terrainDepthBufferSize; void updateFrustum(InternalState* internalState, const MapRendererState& state) const; void computeTileset(const TileId targetTileId, const PointF targetInTileOffsetN, @@ -99,7 +97,7 @@ namespace OsmAnd void onValidateResourcesOfType(MapRendererResourceType type) override; // Customization points: - bool doInitializeRendering() override; + bool doInitializeRendering(bool reinitialize) override; bool doRenderFrame(IMapRenderer_Metrics::Metric_renderFrame* metric) override; bool doReleaseRendering(bool gpuContextLost) override; bool handleStateChange(const MapRendererState& state, MapRendererStateChanges mask) override; @@ -159,9 +157,6 @@ namespace OsmAnd AtlasMapRenderer_OpenGL(GPUAPI_OpenGL* gpuAPI); virtual ~AtlasMapRenderer_OpenGL(); - const std::vector& terrainDepthBuffer; - const PointI& terrainDepthBufferSize; - float getTileSizeOnScreenInPixels() const override; bool getLocationFromScreenPoint(const PointI& screenPoint, PointI& location31) const override; diff --git a/src/Map/OpenGL/GPUAPI_OpenGL.cpp b/src/Map/OpenGL/GPUAPI_OpenGL.cpp index c399aeaf8..41fb9e568 100644 --- a/src/Map/OpenGL/GPUAPI_OpenGL.cpp +++ b/src/Map/OpenGL/GPUAPI_OpenGL.cpp @@ -59,8 +59,6 @@ OsmAnd::GPUAPI_OpenGL::GPUAPI_OpenGL() , _maxVaryingFloats(32) , _maxVaryingVectors(15) , _maxVertexAttribs(16) - , _framebufferDepthBits(0) - , _framebufferDepthBytes(0) , glVersion(_glVersion) , glslVersion(_glslVersion) , extensions(_extensions) @@ -84,8 +82,6 @@ OsmAnd::GPUAPI_OpenGL::GPUAPI_OpenGL() , maxVaryingFloats(_maxVaryingFloats) , maxVaryingVectors(_maxVaryingVectors) , maxVertexAttribs(_maxVertexAttribs) - , framebufferDepthBits(_framebufferDepthBits) - , framebufferDepthBytes(_framebufferDepthBytes) { } @@ -192,16 +188,20 @@ GLuint OsmAnd::GPUAPI_OpenGL::compileShader(GLenum shaderType, const char* sourc GLuint OsmAnd::GPUAPI_OpenGL::linkProgram( GLuint shadersCount, const GLuint* shaders, + QByteArray& binaryCache, + GLenum& cacheFormat, const bool autoReleaseShaders /*= true*/, QHash* outVariablesMap /*= nullptr*/) { - return linkProgram(shadersCount, shaders, {}, autoReleaseShaders, outVariablesMap); + return linkProgram(shadersCount, shaders, {}, binaryCache, cacheFormat, autoReleaseShaders, outVariablesMap); } GLuint OsmAnd::GPUAPI_OpenGL::linkProgram( GLuint shadersCount, const GLuint* shaders, const QList< std::tuple >& variableLocations, + QByteArray& binaryCache, + GLenum& cacheFormat, const bool autoReleaseShaders /*= true*/, QHash* outVariablesMap /*= nullptr*/) { @@ -227,12 +227,6 @@ GLuint OsmAnd::GPUAPI_OpenGL::linkProgram( return program; } - for (auto shaderIdx = 0u; shaderIdx < shadersCount; shaderIdx++) - { - glAttachShader(program, shaders[shaderIdx]); - GL_CHECK_RESULT; - } - for (const auto& variableLocationsEntry : variableLocations) { const auto variableType = std::get<0>(variableLocationsEntry); @@ -246,26 +240,40 @@ GLuint OsmAnd::GPUAPI_OpenGL::linkProgram( } } - glLinkProgram(program); - const auto linkingResult = GL_GET_AND_CHECK_RESULT; - - for (auto shaderIdx = 0u; shaderIdx < shadersCount; shaderIdx++) + GLenum linkingResult; + if (_isSupported_program_binary && !binaryCache.isEmpty()) + linkingResult = linkProgramBinary(program, binaryCache, cacheFormat); + else { - glDetachShader(program, shaders[shaderIdx]); - GL_CHECK_RESULT; + for (auto shaderIdx = 0u; shaderIdx < shadersCount; shaderIdx++) + { + glAttachShader(program, shaders[shaderIdx]); + GL_CHECK_RESULT; + } - if (autoReleaseShaders) + glLinkProgram(program); + linkingResult = GL_GET_AND_CHECK_RESULT; + + for (auto shaderIdx = 0u; shaderIdx < shadersCount; shaderIdx++) { - glDeleteShader(shaders[shaderIdx]); + glDetachShader(program, shaders[shaderIdx]); GL_CHECK_RESULT; + + if (autoReleaseShaders) + { + glDeleteShader(shaders[shaderIdx]); + GL_CHECK_RESULT; + } } } GLint linkSuccessful; glGetProgramiv(program, GL_LINK_STATUS, &linkSuccessful); GL_CHECK_RESULT; - if (linkSuccessful == GL_FALSE) + if (linkingResult != GL_NO_ERROR || linkSuccessful == GL_FALSE) { + if (!binaryCache.isEmpty()) + binaryCache.clear(); GLint logBufferSize = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logBufferSize); GL_CHECK_RESULT; @@ -316,6 +324,9 @@ GLuint OsmAnd::GPUAPI_OpenGL::linkProgram( return 0; } + if (_isSupported_program_binary && binaryCache.isEmpty()) + keepProgramBinary(program, binaryCache, cacheFormat); + if (outVariablesMap) outVariablesMap->clear(); @@ -414,6 +425,34 @@ GLuint OsmAnd::GPUAPI_OpenGL::linkProgram( return program; } +GLenum OsmAnd::GPUAPI_OpenGL::linkProgramBinary(GLuint program, QByteArray& binaryCache, GLenum cacheFormat) +{ + GL_CHECK_PRESENT(glProgramBinary); + + glProgramBinary(program, cacheFormat, binaryCache.constData(), binaryCache.size()); + GLenum result = GL_GET_AND_CHECK_RESULT; + + return result; +} + +void OsmAnd::GPUAPI_OpenGL::keepProgramBinary(GLuint program, QByteArray& binaryCache, GLenum& cacheFormat) +{ + GL_CHECK_PRESENT(glGetProgramiv); + GL_CHECK_PRESENT(glGetProgramBinary); + + GLsizei binaryLength = 0; + glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binaryLength); + GL_CHECK_RESULT; + + if (binaryLength > 0) + { + binaryCache.resize(binaryLength); + glGetProgramBinary(program, binaryLength, NULL, &cacheFormat, binaryCache.data()); + if (GL_GET_AND_CHECK_RESULT != GL_NO_ERROR) + binaryCache.clear(); + } +} + QString OsmAnd::GPUAPI_OpenGL::decodeGlslVariableDataType(const GLenum dataType) { struct GlslDataTypeEntry diff --git a/src/Map/OpenGL/GPUAPI_OpenGL.h b/src/Map/OpenGL/GPUAPI_OpenGL.h index 948f2eb9f..9e451c3a7 100644 --- a/src/Map/OpenGL/GPUAPI_OpenGL.h +++ b/src/Map/OpenGL/GPUAPI_OpenGL.h @@ -306,15 +306,13 @@ namespace OsmAnd bool _isSupported_texture_rg; bool _isSupported_vertex_array_object; bool _isSupported_integerOperations; + bool _isSupported_program_binary; GLint _maxVertexUniformVectors; GLint _maxFragmentUniformVectors; GLint _maxVaryingFloats; GLint _maxVaryingVectors; GLint _maxVertexAttribs; - GLint _framebufferDepthBits; - GLint _framebufferDepthBytes; - bool releaseResourceInGPU(const ResourceInGPU::Type type, const RefInGPU& refInGPU) override; virtual TextureFormat getTextureFormat(const SkColorType colorType) const; @@ -361,23 +359,26 @@ namespace OsmAnd const GLint& maxVaryingVectors; const GLint& maxVertexAttribs; - const GLint& framebufferDepthBits; - const GLint& framebufferDepthBytes; - virtual GLenum validateResult(const char* const function, const char* const file, const int line) = 0; virtual GLuint compileShader(GLenum shaderType, const char* source); virtual GLuint linkProgram( GLuint shadersCount, const GLuint* shaders, + QByteArray& binaryCache, + GLenum& cacheFormat, const bool autoReleaseShaders = true, QHash* outVariablesMap = nullptr); virtual GLuint linkProgram( GLuint shadersCount, const GLuint* shaders, const QList< std::tuple >& variableLocations, + QByteArray& binaryCache, + GLenum& cacheFormat, const bool autoReleaseShaders = true, QHash* outVariablesMap = nullptr); + virtual GLenum linkProgramBinary(GLuint program, QByteArray& binaryCache, GLenum cacheFormat); + virtual void keepProgramBinary(GLuint program, QByteArray& binaryCache, GLenum& cacheFormat); virtual TextureFormat getTextureFormat(const std::shared_ptr< const IMapTiledDataProvider::Data >& tile, const SkColorType colorType = SkColorType::kRGBA_8888_SkColorType); @@ -450,11 +451,6 @@ namespace OsmAnd virtual void popDebugGroupMarker() = 0; virtual void setObjectLabel(ObjectType type, GLuint name, const QString& label) = 0; - - virtual void glClearDepth_wrapper(const float depth) = 0; - - virtual void readFramebufferDepth(GLint x, GLint y, GLsizei width, GLsizei height, std::vector& outData) = 0; - virtual bool pickFramebufferDepthValue(const std::vector& data, GLint x, GLint y, GLsizei width, GLsizei height, GLfloat& outValue) = 0; }; } diff --git a/src/Map/OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.cpp b/src/Map/OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.cpp index 34a73b24d..1ee383ffb 100644 --- a/src/Map/OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.cpp +++ b/src/Map/OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.cpp @@ -41,8 +41,7 @@ OsmAnd::GPUAPI_OpenGL2plus::GPUAPI_OpenGL2plus() , _isSupported_EXT_debug_marker(false) , _isSupported_EXT_debug_label(false) , _isSupported_ARB_sync(false) - , _framebufferDepthDataFormat(0) - , _framebufferDepthDataType(0) + , _isSupported_ARB_get_program_binary(false) , isSupported_GREMEDY_string_marker(_isSupported_GREMEDY_string_marker) , isSupported_ARB_sampler_objects(_isSupported_ARB_sampler_objects) , isSupported_samplerObjects(_isSupported_samplerObjects) @@ -56,8 +55,7 @@ OsmAnd::GPUAPI_OpenGL2plus::GPUAPI_OpenGL2plus() , isSupported_EXT_debug_marker(_isSupported_EXT_debug_marker) , isSupported_EXT_debug_label(_isSupported_EXT_debug_label) , isSupported_ARB_sync(_isSupported_ARB_sync) - , framebufferDepthDataFormat(_framebufferDepthDataFormat) - , framebufferDepthDataType(_framebufferDepthDataType) + , isSupported_ARB_get_program_binary(_isSupported_ARB_get_program_binary) { } @@ -236,6 +234,7 @@ bool OsmAnd::GPUAPI_OpenGL2plus::initialize() _isSupported_EXT_debug_label = extensions.contains("GL_EXT_debug_label"); _isSupported_debug_label = (glVersion >= 43 || _isSupported_EXT_debug_label); _isSupported_ARB_sync = extensions.contains("GL_ARB_sync"); + _isSupported_ARB_get_program_binary = extensions.contains("GL_ARB_get_program_binary"); _isSupported_sync = (glVersion >= 32 || _isSupported_ARB_sync); // http://www.opengl.org/sdk/docs/man/html/glGenSamplers.xhtml are supported only if OpenGL 3.3+ or GL_ARB_sampler_objects is available _isSupported_ARB_sampler_objects = extensions.contains(QLatin1String("GL_ARB_sampler_objects")); @@ -263,6 +262,16 @@ bool OsmAnd::GPUAPI_OpenGL2plus::initialize() _isSupported_EXT_gpu_shader4 = extensions.contains(QStringLiteral("GL_EXT_gpu_shader4")); _isSupported_integerOperations = (glslVersion >= 130) || _isSupported_EXT_gpu_shader4; + if (glVersion >= 41 || _isSupported_ARB_get_program_binary) + { + GLint numProgramBinaryFormats = 0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numProgramBinaryFormats); + GL_CHECK_RESULT; + _isSupported_program_binary = (numProgramBinaryFormats > 0); + } + else + _isSupported_program_binary = false; + GLint compressedFormatsLength = 0; glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &compressedFormatsLength); GL_CHECK_RESULT; @@ -401,76 +410,6 @@ bool OsmAnd::GPUAPI_OpenGL2plus::elementIsVisible(int queryIndex) return queryResult == 0; } -bool OsmAnd::GPUAPI_OpenGL2plus::attachToRenderTarget() -{ - if (isAttachedToRenderTarget()) - return false; - - glGetIntegerv(GL_DEPTH_BITS, &_framebufferDepthBits); - GL_CHECK_RESULT; - if (_framebufferDepthBits == 24) - _framebufferDepthBytes = 4; - else - _framebufferDepthBytes = _framebufferDepthBits / 8; - LogPrintf(LogSeverityLevel::Info, "OpenGL render target depth buffer: %d bits (%d bytes)", _framebufferDepthBits, _framebufferDepthBytes); - - GLint framebufferStencilBits; - glGetIntegerv(GL_STENCIL_BITS, &framebufferStencilBits); - GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Info, "OpenGL render target stencil buffer: %d bits", framebufferStencilBits); - - if (_framebufferDepthBits == 32) - { - _framebufferDepthDataFormat = GL_DEPTH_COMPONENT; - _framebufferDepthDataType = GL_UNSIGNED_INT; - LogPrintf(LogSeverityLevel::Info, "OpenGL render target depth buffer: DEPTH_COMPONENT/UNSIGNED_INT"); - } - else if (_framebufferDepthBits == 24) - { - if (framebufferStencilBits == 8) - { - _framebufferDepthDataFormat = GL_DEPTH_STENCIL; - if (glVersion >= 30) - { - _framebufferDepthDataType = GL_UNSIGNED_INT_24_8; - LogPrintf(LogSeverityLevel::Info, "OpenGL render target depth buffer: DEPTH_STENCIL/UNSIGNED_INT_24_8"); - } - else - { - _framebufferDepthDataType = GL_UNSIGNED_INT; - LogPrintf(LogSeverityLevel::Info, "OpenGL render target depth buffer: DEPTH_STENCIL/UNSIGNED_INT"); - } - } - else - { - _framebufferDepthDataFormat = GL_DEPTH_COMPONENT; - _framebufferDepthDataType = GL_UNSIGNED_INT; - LogPrintf(LogSeverityLevel::Info, "OpenGL render target depth buffer: DEPTH_COMPONENT/UNSIGNED_INT"); - } - } - else if (_framebufferDepthBits == 16) - { - _framebufferDepthDataFormat = GL_DEPTH_COMPONENT; - _framebufferDepthDataType = GL_UNSIGNED_SHORT; - LogPrintf(LogSeverityLevel::Info, "OpenGL render target depth buffer: DEPTH_COMPONENT/UNSIGNED_SHORT"); - } - - return GPUAPI_OpenGL::attachToRenderTarget(); -} - -bool OsmAnd::GPUAPI_OpenGL2plus::detachFromRenderTarget(bool gpuContextLost) -{ - if (!isAttachedToRenderTarget()) - return false; - - _framebufferDepthBits = 0; - _framebufferDepthBytes = 0; - _framebufferDepthDataFormat = 0; - _framebufferDepthDataType = 0; - - return GPUAPI_OpenGL::detachFromRenderTarget(gpuContextLost); -} - bool OsmAnd::GPUAPI_OpenGL2plus::release(bool gpuContextLost) { if (!gpuContextLost && _pointVisibilityCheckQueries.size() > 0) @@ -1130,52 +1069,3 @@ void OsmAnd::GPUAPI_OpenGL2plus::setObjectLabel(ObjectType type_, GLuint name, c GL_CHECK_RESULT; } } - -void OsmAnd::GPUAPI_OpenGL2plus::glClearDepth_wrapper(float depth) -{ - GL_CHECK_PRESENT(glClearDepth); - - glClearDepth(depth); -} - -void OsmAnd::GPUAPI_OpenGL2plus::readFramebufferDepth(GLint x, GLint y, GLsizei width, GLsizei height, std::vector& outData) -{ - GL_CHECK_PRESENT(glReadPixels); - - glReadPixels(x, y, width, height, _framebufferDepthDataFormat, _framebufferDepthDataType, outData.data()); - GL_CHECK_RESULT; -} - -bool OsmAnd::GPUAPI_OpenGL2plus::pickFramebufferDepthValue( - const std::vector& data, GLint x, GLint y, GLsizei width, GLsizei height, GLfloat& outValue) -{ - if (x < 0 || x >= width || y < 0 || y >= height) - return false; - - double value; - const auto pValue = data.data() + (y * width + x) * _framebufferDepthBytes; - if ((_framebufferDepthDataFormat == GL_DEPTH_COMPONENT || _framebufferDepthDataFormat == GL_DEPTH_STENCIL) && _framebufferDepthDataType == GL_UNSIGNED_INT) - { - value = static_cast(*reinterpret_cast(pValue)) / std::numeric_limits::max(); - } - else if (_framebufferDepthDataFormat == GL_DEPTH_STENCIL && _framebufferDepthDataType == GL_UNSIGNED_INT_24_8) - { - value = static_cast(*reinterpret_cast(pValue) >> 8) / (std::numeric_limits::max() >> 8); - } - else if (_framebufferDepthDataFormat == GL_DEPTH_COMPONENT && _framebufferDepthDataType == GL_UNSIGNED_SHORT) - { - value = static_cast(*reinterpret_cast(pValue)) / std::numeric_limits::max(); - } - else - { - LogPrintf(LogSeverityLevel::Error, - "Unsupported combination of framebuffer depth data format (0x%04x) and type (0x%04x)", - _framebufferDepthDataFormat, - _framebufferDepthDataType); - assert(false); - return false; - } - - outValue = static_cast(value); - return true; -} diff --git a/src/Map/OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.h b/src/Map/OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.h index c09245a99..bcaf1d62c 100644 --- a/src/Map/OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.h +++ b/src/Map/OpenGL/OpenGL2plus/GPUAPI_OpenGL2plus.h @@ -36,9 +36,7 @@ namespace OsmAnd bool _isSupported_EXT_debug_marker; bool _isSupported_EXT_debug_label; bool _isSupported_ARB_sync; - - GLenum _framebufferDepthDataFormat; - GLenum _framebufferDepthDataType; + bool _isSupported_ARB_get_program_binary; QVector _pointVisibilityCheckQueries; std::array< GLuint, SamplerTypesCount > _textureSamplers; @@ -69,8 +67,6 @@ namespace OsmAnd bool initialize() override; int checkElementVisibility(int queryIndex, float pointSize) override; bool elementIsVisible(int queryIndex) override; - bool attachToRenderTarget() override; - bool detachFromRenderTarget(bool gpuContextLost) override; bool release(bool gpuContextLost) override; const bool& isSupported_GREMEDY_string_marker; @@ -86,9 +82,7 @@ namespace OsmAnd const bool& isSupported_EXT_debug_marker; const bool& isSupported_EXT_debug_label; const bool& isSupported_ARB_sync; - - const GLenum& framebufferDepthDataFormat; - const GLenum& framebufferDepthDataType; + const bool& isSupported_ARB_get_program_binary; GLenum validateResult(const char* function, const char* file, int line) override; @@ -111,11 +105,6 @@ namespace OsmAnd void popDebugGroupMarker() override; void setObjectLabel(ObjectType type, GLuint name, const QString& label) override; - - void glClearDepth_wrapper(float depth) override; - - void readFramebufferDepth(GLint x, GLint y, GLsizei width, GLsizei height, std::vector& outData) override; - bool pickFramebufferDepthValue(const std::vector& data, GLint x, GLint y, GLsizei width, GLsizei height, GLfloat& outValue) override; }; } diff --git a/src/Map/OpenGL/OpenGLES2plus/GPUAPI_OpenGLES2plus.cpp b/src/Map/OpenGL/OpenGLES2plus/GPUAPI_OpenGLES2plus.cpp index 57ab08909..c75b4e6ae 100644 --- a/src/Map/OpenGL/OpenGLES2plus/GPUAPI_OpenGLES2plus.cpp +++ b/src/Map/OpenGL/OpenGLES2plus/GPUAPI_OpenGLES2plus.cpp @@ -88,14 +88,7 @@ OsmAnd::GPUAPI_OpenGLES2plus::PFNGLLABELOBJECTEXTPROC OsmAnd::GPUAPI_OpenGLES2pl #endif //!OSMAND_TARGET_OS_ios OsmAnd::GPUAPI_OpenGLES2plus::GPUAPI_OpenGLES2plus() - : _depthTexture(0) - , _depthTextureBits(0) - , _depthTextureBytes(0) - , _depthFramebuffer(0) - , _depthFramebufferColorRenderbuffer(0) - , _depthFramebufferColorRenderbufferFormat(0) - , _depthFramebufferColorRenderbufferType(0) - , _isSupported_EXT_unpack_subimage(false) + : _isSupported_EXT_unpack_subimage(false) , _isSupported_EXT_texture_storage(false) , _isSupported_APPLE_texture_max_level(false) , _isSupported_OES_vertex_array_object(false) @@ -110,6 +103,7 @@ OsmAnd::GPUAPI_OpenGLES2plus::GPUAPI_OpenGLES2plus() , _isSupported_EXT_debug_label(false) , _isSupported_APPLE_sync(false) , _isSupported_samplerObjects(false) + , _isSupported_OES_get_program_binary(false) , isSupported_EXT_unpack_subimage(_isSupported_EXT_unpack_subimage) , isSupported_EXT_texture_storage(_isSupported_EXT_texture_storage) , isSupported_APPLE_texture_max_level(_isSupported_APPLE_texture_max_level) @@ -125,6 +119,7 @@ OsmAnd::GPUAPI_OpenGLES2plus::GPUAPI_OpenGLES2plus() , isSupported_EXT_debug_label(_isSupported_EXT_debug_label) , isSupported_APPLE_sync(_isSupported_APPLE_sync) , isSupported_samplerObjects(_isSupported_samplerObjects) + , isSupported_OES_get_program_binary(_isSupported_OES_get_program_binary) , supportedVertexShaderPrecisionFormats(_supportedVertexShaderPrecisionFormats) , supportedFragmentShaderPrecisionFormats(_supportedFragmentShaderPrecisionFormats) { @@ -284,6 +279,7 @@ bool OsmAnd::GPUAPI_OpenGLES2plus::initialize() _isSupported_debug_label = _isSupported_EXT_debug_label; _isSupported_APPLE_sync = extensions.contains("GL_APPLE_sync"); _isSupported_sync = (glVersion >= 30 || _isSupported_APPLE_sync); + _isSupported_OES_get_program_binary = extensions.contains("GL_OES_get_program_binary"); if (isShaderPrecisionFormatSupported(GL_VERTEX_SHADER, GL_LOW_FLOAT)) _supportedVertexShaderPrecisionFormats.insert(GL_LOW_FLOAT); @@ -429,6 +425,16 @@ bool OsmAnd::GPUAPI_OpenGLES2plus::initialize() _isSupported_texture_storage = (glVersion >= 30) || _isSupported_EXT_texture_storage; _isSupported_integerOperations = (glslVersion >= 130); + if (glVersion >= 30 || _isSupported_OES_get_program_binary) + { + GLint numProgramBinaryFormats = 0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numProgramBinaryFormats); + GL_CHECK_RESULT; + _isSupported_program_binary = (numProgramBinaryFormats > 0); + } + else + _isSupported_program_binary = false; + glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); GL_CHECK_RESULT; @@ -562,560 +568,6 @@ bool OsmAnd::GPUAPI_OpenGLES2plus::elementIsVisible(int queryIndex) return queryResult == GL_FALSE; } -bool OsmAnd::GPUAPI_OpenGLES2plus::attachToRenderTarget() -{ - if (isAttachedToRenderTarget()) - return false; - - GLint renderTarget = 0; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &renderTarget); - GL_CHECK_RESULT; - - GLint framebufferColorAttachmentType = 0; - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &framebufferColorAttachmentType); - GL_CHECK_RESULT; - - const char* pcsFramebufferColorAttachmentType; - switch (framebufferColorAttachmentType) - { - case GL_NONE: - LogPrintf(LogSeverityLevel::Error, "OpenGLES framebuffer color attachment is not present"); - return false; - - case GL_RENDERBUFFER: - pcsFramebufferColorAttachmentType = "renderbuffer"; - break; - - default: - LogPrintf(LogSeverityLevel::Error, "Unsupported OpenGLES framebuffer color attachment type: 0x%04X", framebufferColorAttachmentType); - return false; - } - LogPrintf(LogSeverityLevel::Info, "OpenGLES framebuffer color attachment type: %s (0x%04X)", pcsFramebufferColorAttachmentType, framebufferColorAttachmentType); - - GLint framebufferColorAttachment = 0; - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &framebufferColorAttachment); - GL_CHECK_RESULT; - - GLint framebufferWidth = 0; - GLint framebufferHeight = 0; - if (framebufferColorAttachmentType == GL_RENDERBUFFER) - { - GLint renderbuffer = 0; - glGetIntegerv(GL_RENDERBUFFER_BINDING, &renderbuffer); - GL_CHECK_RESULT; - - glBindRenderbuffer(GL_RENDERBUFFER, framebufferColorAttachment); - GL_CHECK_RESULT; - - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth); - GL_CHECK_RESULT; - - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight); - GL_CHECK_RESULT; - - glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); - GL_CHECK_RESULT; - } - LogPrintf(LogSeverityLevel::Info, "OpenGLES framebuffer size: %dx%d", framebufferWidth, framebufferHeight); - - GLint framebufferDepthAttachmentType = 0; - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &framebufferDepthAttachmentType); - GL_CHECK_RESULT; - - const char* pcsFramebufferDepthAttachmentType; - switch (framebufferDepthAttachmentType) - { - case GL_NONE: - LogPrintf(LogSeverityLevel::Error, "OpenGLES framebuffer depth attachment is not present"); - return false; - - case GL_TEXTURE: - pcsFramebufferDepthAttachmentType = "texture"; - break; - - default: - LogPrintf(LogSeverityLevel::Error, "Unsupported OpenGLES framebuffer depth attachment type: 0x%04X", framebufferDepthAttachmentType); - return false; - } - LogPrintf(LogSeverityLevel::Info, "OpenGLES framebuffer depth attachment type: %s (0x%04X)", pcsFramebufferDepthAttachmentType, framebufferDepthAttachmentType); - - GLint framebufferDepthAttachment = 0; - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &framebufferDepthAttachment); - GL_CHECK_RESULT; - - if (framebufferDepthAttachmentType == GL_TEXTURE) - { - if (!glIsTexture(framebufferDepthAttachment)) - { - LogPrintf(LogSeverityLevel::Error, "OpenGLES framebuffer depth attachment is not a texture"); - return false; - } - - _depthTexture = framebufferDepthAttachment; - - if (isSupported_texture_storage) - { - GLint boundTexture2D = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture2D); - GL_CHECK_RESULT; - - glBindTexture(GL_TEXTURE_2D, _depthTexture); - GL_CHECK_RESULT; - - GLint depthTextureInternalFormat = 0; - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_IMMUTABLE_FORMAT_EXT, &depthTextureInternalFormat); - GL_CHECK_RESULT; - LogPrintf(LogSeverityLevel::Info, "OpenGLES framebuffer depth attachment internal format: 0x%04X", depthTextureInternalFormat); - - if (depthTextureInternalFormat != 0) // NOTE: 0 means that texture is mutable (allocacted not with glTexStorage) - { - // TODO: decode? - } - - glBindTexture(GL_TEXTURE_2D, boundTexture2D); - GL_CHECK_RESULT; - } - - if (_depthTextureBits == 0) - { - // NOTE: Hopefully this workaround would work - glGetIntegerv(GL_DEPTH_BITS, &_depthTextureBits); - GL_CHECK_RESULT; - - _depthTextureBytes = _depthTextureBits / 8; - LogPrintf(LogSeverityLevel::Info, "OpenGLES framebuffer depth attachment: %d bits (%d bytes)", _depthTextureBits, _depthTextureBytes); - } - } - - glGenFramebuffers(1, &_depthFramebuffer); - GL_CHECK_RESULT; - glBindFramebuffer(GL_FRAMEBUFFER, _depthFramebuffer); - GL_CHECK_RESULT; - glGenRenderbuffers(1, &_depthFramebufferColorRenderbuffer); - GL_CHECK_RESULT; - glBindRenderbuffer(GL_RENDERBUFFER, _depthFramebufferColorRenderbuffer); - GL_CHECK_RESULT; - GLint depthFramebufferColorRenderbufferInternalFormat = GL_RGBA4; - switch (_depthTextureBits) - { - case 32: - if (glVersion >= 30) - depthFramebufferColorRenderbufferInternalFormat = GL_R32UI; - else if (isSupported_ARM_rgba8 || isSupported_OES_rgb8_rgba8 || glVersion >= 30) - depthFramebufferColorRenderbufferInternalFormat = GL_RGBA8_OES; - break; - - case 24: - if (glVersion >= 30) - depthFramebufferColorRenderbufferInternalFormat = GL_R32UI; - else if (isSupported_OES_rgb8_rgba8 || glVersion >= 30) - depthFramebufferColorRenderbufferInternalFormat = GL_RGB8_OES; - break; - - case 16: - if (glVersion >= 30) - depthFramebufferColorRenderbufferInternalFormat = GL_R16UI; - break; - } - glRenderbufferStorage(GL_RENDERBUFFER, depthFramebufferColorRenderbufferInternalFormat, framebufferWidth, framebufferHeight); - GL_CHECK_RESULT; - GLint depthFramebufferColorRenderbufferBitsRGBA[4]; - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_RED_SIZE, &depthFramebufferColorRenderbufferBitsRGBA[0]); - GL_CHECK_RESULT; - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_GREEN_SIZE, &depthFramebufferColorRenderbufferBitsRGBA[1]); - GL_CHECK_RESULT; - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_BLUE_SIZE, &depthFramebufferColorRenderbufferBitsRGBA[2]); - GL_CHECK_RESULT; - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE, &depthFramebufferColorRenderbufferBitsRGBA[3]); - GL_CHECK_RESULT; - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _depthFramebufferColorRenderbuffer); - GL_CHECK_RESULT; - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - { - LogPrintf(LogSeverityLevel::Error, "Failed to initialize depth framebuffer"); - return false; - } - GL_CHECK_RESULT; - glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &_depthFramebufferColorRenderbufferFormat); - GL_CHECK_RESULT; - glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &_depthFramebufferColorRenderbufferType); - GL_CHECK_RESULT; - glBindFramebuffer(GL_FRAMEBUFFER, renderTarget); - GL_CHECK_RESULT; - - LogPrintf(LogSeverityLevel::Info, "OpenGLES depth framebuffer: 0x%04X format, 0x%04X type", _depthFramebufferColorRenderbufferFormat, _depthFramebufferColorRenderbufferType); - - // NOTE: Since we're rendering actual depth texture to a color attachment, it's resolution is the final depth bits count - _framebufferDepthBits = - depthFramebufferColorRenderbufferBitsRGBA[0] - + depthFramebufferColorRenderbufferBitsRGBA[1] - + depthFramebufferColorRenderbufferBitsRGBA[2] - + depthFramebufferColorRenderbufferBitsRGBA[3]; - if (_depthFramebufferColorRenderbufferFormat == GL_RGBA && _depthFramebufferColorRenderbufferType == GL_UNSIGNED_BYTE) - { - _framebufferDepthBytes = 4; - } - else - { - _framebufferDepthBytes = _framebufferDepthBits / 8; - } - LogPrintf(LogSeverityLevel::Info, "OpenGLES render target depth buffer: %d bits (%d bytes)", _framebufferDepthBits, _framebufferDepthBytes); - - int depthBitsToColorBitsRGBA[4] = { - std::max( - _depthTextureBits, - 0), - std::max( - _depthTextureBits - - depthFramebufferColorRenderbufferBitsRGBA[0], - 0), - std::max( - _depthTextureBits - - depthFramebufferColorRenderbufferBitsRGBA[0] - - depthFramebufferColorRenderbufferBitsRGBA[1], - 0), - std::max( - _depthTextureBits - - depthFramebufferColorRenderbufferBitsRGBA[0] - - depthFramebufferColorRenderbufferBitsRGBA[1] - - depthFramebufferColorRenderbufferBitsRGBA[2], - 0), - }; - - // it seems that in any case during sampling, the depth component would be converted into a color 0..1 - // so that also means that we don't really need to convert it anyhow, just write it out? but the precision.. - // 1. read as highp SAMPLE_TEXTURE_2D().r - // 2. glFragColor.r = r if GL version > 300 (if target format is R, or if depthFramebufferColorRenderbufferBitsRGBA[0] == _framebufferDepthBits) - // - out_FragColor also needs to be highp then, in this specific case - // 3. otherwise, do math to split value into 3 or 4 components - // - this can be done either with integer operations, and then convert back to 0..255 to 0..1 range again to reconsturct - // - - - // Compile vertex shader - const QString vertexShader = QStringLiteral( - // Input data - "INPUT vec2 in_vs_vertexPosition; ""\n" - "INPUT vec2 in_vs_vertexTexCoords; ""\n" - " ""\n" - // Output data to next shader stages - "PARAM_OUTPUT highp vec2 v2f_texCoords; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - " v2f_texCoords = in_vs_vertexTexCoords; ""\n" - " ""\n" - " gl_Position.xy = in_vs_vertexPosition; ""\n" - " gl_Position.z = 0.0; ""\n" - " gl_Position.w = 1.0; ""\n" - "} ""\n"); - auto preprocessedVertexShader = vertexShader; - preprocessVertexShader(preprocessedVertexShader); - optimizeVertexShader(preprocessedVertexShader); - const auto vsId = compileShader(GL_VERTEX_SHADER, qPrintable(preprocessedVertexShader)); - if (vsId == 0) - { - LogPrintf(LogSeverityLevel::Error, - "Failed to compile depth framebuffer vertex shader"); - return false; - } - - // Compile fragment shader - const QString fragmentShader = QStringLiteral( - // Definitions - "#define DEPTH_TEXTURE_BITS %DepthTextureBits% ""\n" - "#define COLOR_BUFFER_BITS %ColorBufferBits% ""\n" - "#define COLOR_BUFFER_BITS_R %ColorBufferBitsR% ""\n" - "#define COLOR_BUFFER_BITS_G %ColorBufferBitsG% ""\n" - "#define COLOR_BUFFER_BITS_B %ColorBufferBitsB% ""\n" - "#define COLOR_BUFFER_BITS_A %ColorBufferBitsA% ""\n" - "#define COLOR_BUFFER_VALUE_IS_INTEGER %ColorBufferValueIsInteger% ""\n" - "#define DEPTH_BITS_TO_COLOR_BITS_R %DepthBitsToColorBitsR% ""\n" - "#define DEPTH_BITS_TO_COLOR_BITS_G %DepthBitsToColorBitsG% ""\n" - "#define DEPTH_BITS_TO_COLOR_BITS_B %DepthBitsToColorBitsB% ""\n" - "#define DEPTH_BITS_TO_COLOR_BITS_A %DepthBitsToColorBitsA% ""\n" - " ""\n" - // Input data - "PARAM_INPUT highp vec2 v2f_texCoords; ""\n" - " ""\n" - // Parameters - "uniform highp sampler2D param_fs_depthTextureSampler; ""\n" - " ""\n" - "void main() ""\n" - "{ ""\n" - // Enforce max 32-bit depth textures - "#if DEPTH_TEXTURE_BITS > 32 ""\n" - "#error \"Depth texture: max 32 bits supported\" ""\n" - "#endif // DEPTH_TEXTURE_BITS > 32 ""\n" - " ""\n" - // Read 0..1 value from the depth texture - " highp float depthValueN = SAMPLE_TEXTURE_2D(param_fs_depthTextureSampler, v2f_texCoords).r; ""\n" - " ""\n" - "#if COLOR_BUFFER_BITS == COLOR_BUFFER_BITS_R && COLOR_BUFFER_BITS >= DEPTH_TEXTURE_BITS ""\n" - // In case entire depth value fits into R component, no or minimal conversion is needed - "#if COLOR_BUFFER_VALUE_IS_INTEGER ""\n" - " highp uvec4 colorValue = uvec4(0); ""\n" - "#if COLOR_BUFFER_BITS == 32 ""\n" - // Using floatBitsToUint is acceptable since highp floats are encoded as IEEE 754 - " colorValue.r = floatBitsToUint(depthValueN); ""\n" - "#else // COLOR_BUFFER_BITS == 32 ""\n" - " colorValue.r = int(depthValueN * %MaxIntColorValue%.0); ""\n" - "#endif // COLOR_BUFFER_BITS == 32 ""\n" - "#else // COLOR_BUFFER_VALUE_IS_INTEGER ""\n" - " highp vec4 colorValue = vec4(0.0); ""\n" - " colorValue.r = depthValueN; ""\n" - "#endif // COLOR_BUFFER_VALUE_IS_INTEGER ""\n" - "#else // COLOR_BUFFER_BITS == COLOR_BUFFER_BITS_R && COLOR_BUFFER_BITS >= DEPTH_TEXTURE_BITS ""\n" - " ""\n" - " highp vec4 colorValue = vec4(0.0); ""\n" - "#if INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp int depthValue = int(depthValueN * %MaxIntDepthValue%.0); ""\n" - "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" - " ""\n" - // Otherwise, splitting by components is required (msb-to-lsb) - "#if DEPTH_BITS_TO_COLOR_BITS_R > 0 && COLOR_BUFFER_BITS_R > 0 ""\n" - "#if INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp int rDepthValue = (depthValue >> ( ""\n" - " %DepthBitsToColorBitsR% - %ColorBufferBitsR% ""\n" - " )) & ((1 << %ColorBufferBitsR%) - 1); ""\n" - " colorValue.r = float(rDepthValue) / float((1 << %ColorBufferBitsR%) - 1); ""\n" - "#else // INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp float maxColorValueR = pow(2.0, %ColorBufferBitsR%.0) - 1.0; ""\n" - " highp float depthDivisorR = pow(2.0, %DepthBitsToColorBitsR%.0 - %ColorBufferBitsR%.0) / %MaxIntDepthValue%.0; ""\n" - " colorValue.r = floor(depthValueN / depthDivisorR) / maxColorValueR; ""\n" - " depthValueN -= colorValue.r * depthDivisorR * maxColorValueR; ""\n" - "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" - "#endif // DEPTH_BITS_TO_COLOR_BITS_R > 0 && COLOR_BUFFER_BITS_R > ""\n" - " ""\n" - "#if DEPTH_BITS_TO_COLOR_BITS_G > 0 && COLOR_BUFFER_BITS_G > 0 ""\n" - "#if INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp int gDepthValue = (depthValue >> ( ""\n" - " %DepthBitsToColorBitsG% - %ColorBufferBitsG% ""\n" - " )) & ((1 << %ColorBufferBitsG%) - 1); ""\n" - " colorValue.g = float(gDepthValue) / float((1 << %ColorBufferBitsG%) - 1); ""\n" - "#else // INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp float maxColorValueG = pow(2.0, %ColorBufferBitsG%.0) - 1.0; ""\n" - " highp float depthDivisorG = pow(2.0, %DepthBitsToColorBitsG%.0 - %ColorBufferBitsG%.0) / %MaxIntDepthValue%.0; ""\n" - " colorValue.g = floor(depthValueN / depthDivisorG) / maxColorValueG; ""\n" - " depthValueN -= colorValue.g * depthDivisorG * maxColorValueG; ""\n" - "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" - "#endif // DEPTH_BITS_TO_COLOR_BITS_G > 0 && COLOR_BUFFER_BITS_G > 0 ""\n" - " ""\n" - "#if DEPTH_BITS_TO_COLOR_BITS_B > 0 && COLOR_BUFFER_BITS_B > 0 ""\n" - "#if INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp int bDepthValue = (depthValue >> ( ""\n" - " %DepthBitsToColorBitsB% - %ColorBufferBitsB% ""\n" - " )) & ((1 << %ColorBufferBitsB%) - 1); ""\n" - " colorValue.b = float(bDepthValue) / float((1 << %ColorBufferBitsB%) - 1); ""\n" - "#else // INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp float maxColorValueB = pow(2.0, %ColorBufferBitsB%.0) - 1.0; ""\n" - " highp float depthDivisorB = pow(2.0, %DepthBitsToColorBitsB%.0 - %ColorBufferBitsB%.0) / %MaxIntDepthValue%.0; ""\n" - " colorValue.b = floor(depthValueN / depthDivisorB) / maxColorValueR; ""\n" - " depthValueN -= colorValue.b * depthDivisorB * maxColorValueB; ""\n" - "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" - "#endif // DEPTH_BITS_TO_COLOR_BITS_B > 0 && COLOR_BUFFER_BITS_B > 0 ""\n" - " ""\n" - "#if DEPTH_BITS_TO_COLOR_BITS_A > 0 && COLOR_BUFFER_BITS_A > 0 ""\n" - "#if INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp int aDepthValue = (depthValue >> ( ""\n" - " %DepthBitsToColorBitsA% - %ColorBufferBitsA% ""\n" - " )) & ((1 << %ColorBufferBitsA%) - 1); ""\n" - " colorValue.a = float(aDepthValue) / float((1 << %ColorBufferBitsA%) - 1); ""\n" - "#else // INTEGER_OPERATIONS_SUPPORTED ""\n" - " highp float maxColorValueA = pow(2.0, %ColorBufferBitsA%.0) - 1.0; ""\n" - " highp float depthDivisorA = pow(2.0, %DepthBitsToColorBitsA%.0 - %ColorBufferBitsA%.0) / %MaxIntDepthValue%.0; ""\n" - " colorValue.a = floor(depthValueN / depthDivisorA) / maxColorValueA; ""\n" - " depthValueN -= colorValue.a * depthDivisorA * maxColorValueA; ""\n" - "#endif // INTEGER_OPERATIONS_SUPPORTED ""\n" - "#endif // DEPTH_BITS_TO_COLOR_BITS_A > 0 && COLOR_BUFFER_BITS_A > 0 ""\n" - " ""\n" - "#endif // COLOR_BUFFER_BITS == COLOR_BUFFER_BITS_R && COLOR_BUFFER_BITS >= DEPTH_TEXTURE_BITS ""\n" - " ""\n" - " FRAGMENT_COLOR_OUTPUT = colorValue; ""\n" - "} ""\n"); - auto preprocessedFragmentShader = fragmentShader; - preprocessedFragmentShader.replace("%DepthTextureBits%", QString::number(_depthTextureBits)); - preprocessedFragmentShader.replace("%MaxIntDepthValue%", QString::number((1ull << _depthTextureBits) - 1)); - preprocessedFragmentShader.replace("%ColorBufferBits%", QString::number(_framebufferDepthBits)); - preprocessedFragmentShader.replace("%ColorBufferBitsR%", QString::number(depthFramebufferColorRenderbufferBitsRGBA[0])); - preprocessedFragmentShader.replace("%ColorBufferBitsG%", QString::number(depthFramebufferColorRenderbufferBitsRGBA[1])); - preprocessedFragmentShader.replace("%ColorBufferBitsB%", QString::number(depthFramebufferColorRenderbufferBitsRGBA[2])); - preprocessedFragmentShader.replace("%ColorBufferBitsA%", QString::number(depthFramebufferColorRenderbufferBitsRGBA[3])); - preprocessedFragmentShader.replace("%MaxIntColorValue%", QString::number((1ull << _framebufferDepthBits) - 1)); - preprocessedFragmentShader.replace("%DepthBitsToColorBitsR%", QString::number(depthBitsToColorBitsRGBA[0])); - preprocessedFragmentShader.replace("%DepthBitsToColorBitsG%", QString::number(depthBitsToColorBitsRGBA[1])); - preprocessedFragmentShader.replace("%DepthBitsToColorBitsB%", QString::number(depthBitsToColorBitsRGBA[2])); - preprocessedFragmentShader.replace("%DepthBitsToColorBitsA%", QString::number(depthBitsToColorBitsRGBA[3])); - if (_depthFramebufferColorRenderbufferFormat == GL_RED_INTEGER && _depthFramebufferColorRenderbufferType == GL_UNSIGNED_INT) - { - preprocessedFragmentShader.replace(QStringLiteral("%ColorBufferValueIsInteger%"), QStringLiteral("1")); - preprocessFragmentShader(preprocessedFragmentShader, QStringLiteral("u"), QStringLiteral("highp")); - } - else if (_depthFramebufferColorRenderbufferFormat == GL_RED_INTEGER && _depthFramebufferColorRenderbufferType == GL_UNSIGNED_SHORT) - { - preprocessedFragmentShader.replace(QStringLiteral("%ColorBufferValueIsInteger%"), QStringLiteral("1")); - preprocessFragmentShader(preprocessedFragmentShader, QStringLiteral("u"), QStringLiteral("highp")); - } - else - { - preprocessedFragmentShader.replace(QStringLiteral("%ColorBufferValueIsInteger%"), QStringLiteral("0")); - preprocessFragmentShader(preprocessedFragmentShader, QString(), QStringLiteral("highp")); - } - optimizeFragmentShader(preprocessedFragmentShader); - const auto fsId = compileShader(GL_FRAGMENT_SHADER, qPrintable(preprocessedFragmentShader)); - if (fsId == 0) - { - glDeleteShader(vsId); - GL_CHECK_RESULT; - - LogPrintf(LogSeverityLevel::Error, - "Failed to compile depth framebuffer fragment shader"); - return false; - } - - // Link everything into program object - GLuint shaders[] = { vsId, fsId }; - QHash< QString, GPUAPI_OpenGL::GlslProgramVariable > variablesMap; - _depthFramebufferProgram.id = linkProgram(2, shaders, true, &variablesMap); - if (!_depthFramebufferProgram.id.isValid()) - { - LogPrintf(LogSeverityLevel::Error, - "Failed to link depth framebuffer program"); - return false; - } - - bool ok = true; - const auto& lookup = obtainVariablesLookupContext(_depthFramebufferProgram.id, variablesMap); - ok = ok && lookup->lookupLocation(_depthFramebufferProgram.vs.in.vertexPosition, "in_vs_vertexPosition", GlslVariableType::In); - ok = ok && lookup->lookupLocation(_depthFramebufferProgram.vs.in.vertexTexCoords, "in_vs_vertexTexCoords", GlslVariableType::In); - ok = ok && lookup->lookupLocation(_depthFramebufferProgram.fs.param.depthTextureSampler, "param_fs_depthTextureSampler", GlslVariableType::Uniform); - -#pragma pack(push, 1) - struct Vertex - { - // XY coordinates. Z is assumed to be 0 - float positionXY[2]; - - // UV coordinates - float textureUV[2]; - }; -#pragma pack(pop) - - // Vertex data - Vertex vertices[4] = - { - { { -1.0f, -1.0f }, { 0.0f, 0.0f } },//BL - { { -1.0f, 1.0f }, { 0.0f, 1.0f } },//TL - { { 1.0f, 1.0f }, { 1.0f, 1.0f } },//TR - { { 1.0f, -1.0f }, { 1.0f, 0.0f } } //BR - }; - const auto verticesCount = 4; - - // Index data - GLushort indices[6] = - { - 0, 1, 2, - 0, 2, 3 - }; - const auto indicesCount = 6; - - _depthFramebufferVAO = allocateUninitializedVAO(); - - // Create vertex buffer and associate it with VAO - glGenBuffers(1, &_depthFramebufferVBO); - GL_CHECK_RESULT; - glBindBuffer(GL_ARRAY_BUFFER, _depthFramebufferVBO); - GL_CHECK_RESULT; - glBufferData(GL_ARRAY_BUFFER, verticesCount * sizeof(Vertex), vertices, GL_STATIC_DRAW); - GL_CHECK_RESULT; - glEnableVertexAttribArray(*_depthFramebufferProgram.vs.in.vertexPosition); - GL_CHECK_RESULT; - glVertexAttribPointer(*_depthFramebufferProgram.vs.in.vertexPosition, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, positionXY))); - GL_CHECK_RESULT; - glEnableVertexAttribArray(*_depthFramebufferProgram.vs.in.vertexTexCoords); - GL_CHECK_RESULT; - glVertexAttribPointer(*_depthFramebufferProgram.vs.in.vertexTexCoords, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, textureUV))); - GL_CHECK_RESULT; - - // Create index buffer and associate it with VAO - glGenBuffers(1, &_depthFramebufferIBO); - GL_CHECK_RESULT; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _depthFramebufferIBO); - GL_CHECK_RESULT; - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesCount * sizeof(GLushort), indices, GL_STATIC_DRAW); - GL_CHECK_RESULT; - - initializeVAO(_depthFramebufferVAO); - glBindBuffer(GL_ARRAY_BUFFER, 0); - GL_CHECK_RESULT; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - GL_CHECK_RESULT; - - return GPUAPI_OpenGL::attachToRenderTarget(); -} - -bool OsmAnd::GPUAPI_OpenGLES2plus::detachFromRenderTarget(bool gpuContextLost) -{ - if (!isAttachedToRenderTarget()) - return false; - - _depthTexture = 0; - _depthTextureBits = 0; - _depthTextureBytes = 0; - _framebufferDepthBits = 0; - _framebufferDepthBytes = 0; - - if (_depthFramebufferVAO.isValid()) - { - releaseVAO(_depthFramebufferVAO, gpuContextLost); - _depthFramebufferVAO.reset(); - } - - if (_depthFramebufferIBO.isValid()) - { - if (!gpuContextLost) - { - glDeleteBuffers(1, &_depthFramebufferIBO); - GL_CHECK_RESULT; - } - _depthFramebufferIBO.reset(); - } - if (_depthFramebufferVBO.isValid()) - { - if (!gpuContextLost) - { - glDeleteBuffers(1, &_depthFramebufferVBO); - GL_CHECK_RESULT; - } - _depthFramebufferVBO.reset(); - } - - if (_depthFramebufferProgram.id.isValid()) - { - if (!gpuContextLost) - { - glDeleteProgram(_depthFramebufferProgram.id); - GL_CHECK_RESULT; - } - _depthFramebufferProgram = DepthFramebufferProgram(); - } - - if (_depthFramebuffer > 0) - { - glDeleteFramebuffers(1, &_depthFramebuffer); - GL_CHECK_RESULT; - } - _depthFramebuffer = 0; - - if (_depthFramebufferColorRenderbuffer > 0) - { - glDeleteRenderbuffers(1, &_depthFramebufferColorRenderbuffer); - GL_CHECK_RESULT; - } - _depthFramebufferColorRenderbuffer = 0; - - return GPUAPI_OpenGL::detachFromRenderTarget(gpuContextLost); -} - bool OsmAnd::GPUAPI_OpenGLES2plus::release(bool gpuContextLost) { if (!gpuContextLost && _pointVisibilityCheckQueries.size() > 0) @@ -1795,122 +1247,3 @@ void OsmAnd::GPUAPI_OpenGLES2plus::setObjectLabel(ObjectType type_, GLuint name, GL_CHECK_RESULT; } } - -void OsmAnd::GPUAPI_OpenGLES2plus::glClearDepth_wrapper(float depth) -{ - GL_CHECK_PRESENT(glClearDepthf); - - glClearDepthf(depth); -} - -void OsmAnd::GPUAPI_OpenGLES2plus::readFramebufferDepth(GLint x, GLint y, GLsizei width, GLsizei height, std::vector& outData) -{ - assert(outData.size() >= width * height * _framebufferDepthBytes); - - GLint renderTarget = 0; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &renderTarget); - GL_CHECK_RESULT; - - GLboolean depthTestEnabled = GL_FALSE; - glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled); - GL_CHECK_RESULT; - - glBindFramebuffer(GL_FRAMEBUFFER, _depthFramebuffer); - GL_CHECK_RESULT; - - useVAO(_depthFramebufferVAO); - - glUseProgram(_depthFramebufferProgram.id); - GL_CHECK_RESULT; - - glActiveTexture(GL_TEXTURE0 + 0); - GL_CHECK_RESULT; - - setTextureBlockSampler(GL_TEXTURE0 + 0, GPUAPI_OpenGL::SamplerType::DepthBuffer); - - glUniform1i(_depthFramebufferProgram.fs.param.depthTextureSampler, 0); - GL_CHECK_RESULT; - - glBindTexture(GL_TEXTURE_2D, _depthTexture); - GL_CHECK_RESULT; - - applyTextureBlockToTexture(GL_TEXTURE_2D, GL_TEXTURE0 + 0); - - glDisable(GL_DEPTH_TEST); - GL_CHECK_RESULT; - - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); - GL_CHECK_RESULT; - - glReadPixels(x, y, width, height, _depthFramebufferColorRenderbufferFormat, _depthFramebufferColorRenderbufferType, outData.data()); - GL_CHECK_RESULT; - - glBindFramebuffer(GL_FRAMEBUFFER, renderTarget); - GL_CHECK_RESULT; - - if (depthTestEnabled == GL_TRUE) - { - glEnable(GL_DEPTH_TEST); - GL_CHECK_RESULT; - } -} - -bool OsmAnd::GPUAPI_OpenGLES2plus::pickFramebufferDepthValue( - const std::vector& data, GLint x, GLint y, GLsizei width, GLsizei height, GLfloat& outValue) -{ - if (x < 0 || x >= width || y < 0 || y >= height) - return false; - - double value; - const auto pValue = data.data() + (y * width + x) * _framebufferDepthBytes; - if (_depthFramebufferColorRenderbufferFormat == GL_RED_INTEGER && _depthFramebufferColorRenderbufferType == GL_UNSIGNED_INT) - { - // NOTE: Since floatBitsToUint() was used, decode as float directly - outValue = *reinterpret_cast(pValue); - return true; - } - else if (_depthFramebufferColorRenderbufferFormat == GL_RED_INTEGER && _depthFramebufferColorRenderbufferType == GL_UNSIGNED_SHORT) - { - value = static_cast(*reinterpret_cast(pValue)) / std::numeric_limits::max(); - } - else if (_depthFramebufferColorRenderbufferFormat == GL_RGBA && _depthFramebufferColorRenderbufferType == GL_UNSIGNED_BYTE && _framebufferDepthBits == 32) - { - const auto rawIntValue = *reinterpret_cast(pValue); - const auto intValue - = ((rawIntValue >> 0) & 0xFF) << 24 - | ((rawIntValue >> 8) & 0xFF) << 16 - | ((rawIntValue >> 16) & 0xFF) << 8 - | ((rawIntValue >> 24) & 0xFF) << 0 - ; - value = static_cast(intValue) / std::numeric_limits::max(); - } - else if (_depthFramebufferColorRenderbufferFormat == GL_RGBA && _depthFramebufferColorRenderbufferType == GL_UNSIGNED_BYTE && _framebufferDepthBits == 24) - { - const auto rawIntValue = *reinterpret_cast(pValue); - const auto intValue - = ((rawIntValue >> 0) & 0xFF) << 16 - | ((rawIntValue >> 8) & 0xFF) << 8 - | ((rawIntValue >> 16) & 0xFF) << 0 - ; - value = static_cast(intValue) / (std::numeric_limits::max() >> 8); - } - else if (_depthFramebufferColorRenderbufferFormat == GL_RGBA && _depthFramebufferColorRenderbufferType == GL_UNSIGNED_SHORT_4_4_4_4) - { - // NOTE: Not really sure why this case does not need byte swapping - const auto intValue = *reinterpret_cast(pValue); - value = static_cast(intValue) / std::numeric_limits::max(); - } - else - { - LogPrintf(LogSeverityLevel::Error, - "Unsupported combination of framebuffer depth data format (0x%04x) and type (0x%04x)", - _depthFramebufferColorRenderbufferFormat, - _depthFramebufferColorRenderbufferType); - assert(false); - return false; - } - - outValue = static_cast(value); - - return true; -} diff --git a/src/Map/OpenGL/OpenGLES2plus/GPUAPI_OpenGLES2plus.h b/src/Map/OpenGL/OpenGLES2plus/GPUAPI_OpenGLES2plus.h index e83645f08..24da0809d 100644 --- a/src/Map/OpenGL/OpenGLES2plus/GPUAPI_OpenGLES2plus.h +++ b/src/Map/OpenGL/OpenGLES2plus/GPUAPI_OpenGLES2plus.h @@ -36,37 +36,7 @@ namespace OsmAnd static PFNGLLABELOBJECTEXTPROC glLabelObjectEXT; #endif // !OSMAND_TARGET_OS_ios private: - GLint _depthTexture; - GLint _depthTextureBits; - GLint _depthTextureBytes; - GLuint _depthFramebuffer; - GLuint _depthFramebufferColorRenderbuffer; - GLint _depthFramebufferColorRenderbufferFormat; - GLint _depthFramebufferColorRenderbufferType; QVector _pointVisibilityCheckQueries; - - struct DepthFramebufferProgram { - GLname id; - - struct { - // Input data - struct { - GLlocation vertexPosition; - GLlocation vertexTexCoords; - } in; - } vs; - - struct { - // Parameters - struct { - GLlocation depthTextureSampler; - } param; - } fs; - } _depthFramebufferProgram; - GLname _depthFramebufferVAO; - GLname _depthFramebufferVBO; - GLname _depthFramebufferIBO; - bool _isSupported_EXT_unpack_subimage; bool _isSupported_EXT_texture_storage; bool _isSupported_APPLE_texture_max_level; @@ -82,6 +52,7 @@ namespace OsmAnd bool _isSupported_EXT_debug_label; bool _isSupported_APPLE_sync; bool _isSupported_samplerObjects; + bool _isSupported_OES_get_program_binary; QSet _supportedVertexShaderPrecisionFormats; QSet _supportedFragmentShaderPrecisionFormats; @@ -112,8 +83,6 @@ namespace OsmAnd bool initialize() override; int checkElementVisibility(int queryIndex, float pointSize) override; bool elementIsVisible(int queryIndex) override; - bool attachToRenderTarget() override; - bool detachFromRenderTarget(bool gpuContextLost) override; bool release(bool gpuContextLost) override; const bool& isSupported_EXT_unpack_subimage; @@ -131,6 +100,7 @@ namespace OsmAnd const bool& isSupported_EXT_debug_label; const bool& isSupported_APPLE_sync; const bool& isSupported_samplerObjects; + const bool& isSupported_OES_get_program_binary; const QSet& supportedVertexShaderPrecisionFormats; const QSet& supportedFragmentShaderPrecisionFormats; @@ -156,11 +126,6 @@ namespace OsmAnd void popDebugGroupMarker() override; void setObjectLabel(ObjectType type, GLuint name, const QString& label) override; - - void glClearDepth_wrapper(float depth) override; - - void readFramebufferDepth(GLint x, GLint y, GLsizei width, GLsizei height, std::vector& outData) override; - bool pickFramebufferDepthValue(const std::vector& data, GLint x, GLint y, GLsizei width, GLsizei height, GLfloat& outValue) override; }; } diff --git a/wrappers/android/src/net/osmand/core/android/MapRendererView.java b/wrappers/android/src/net/osmand/core/android/MapRendererView.java index ebfc26fbd..2c56cdfa4 100644 --- a/wrappers/android/src/net/osmand/core/android/MapRendererView.java +++ b/wrappers/android/src/net/osmand/core/android/MapRendererView.java @@ -56,18 +56,26 @@ public abstract class MapRendererView extends FrameLayout { private static final String TAG = "OsmAndCore:Android/MapRendererView"; + private final static long INITIALIZE_TIMEOUT = 4000; + private final static long INITIALIZE_WAIT_TIME = 400; + private final static long RELEASE_STOP_TIMEOUT = 4000; private final static long RELEASE_WAIT_TIMEOUT = 400; /** - * Main GLSurfaceView + * Reference to OsmAndCore::IMapRenderer instance */ - private volatile GLSurfaceView _glSurfaceView; + protected IMapRenderer _mapRenderer; /** - * Reference to OsmAndCore::IMapRenderer instance + * Map renderer that is ready to export */ - protected IMapRenderer _mapRenderer; + protected volatile IMapRenderer _exportableMapRenderer; + + /** + * Main rendering view + */ + private RenderingView _renderingView; /** * Optional byte buffer for offscreen rendering @@ -172,10 +180,30 @@ public abstract class MapRendererView extends FrameLayout { */ private int _windowWidth; private int _windowHeight; + private boolean _flipWindow; private int frameId; - private boolean isPresent; + /** + * Rendering is meant to be initialized for the first time + */ + private volatile boolean isInitializing; + + /** + * Rendering is meant to be reinitialized + */ + private volatile boolean isReinitializing; + + /** + * Rendering is suspended but renderer must be kept initialized + */ + private volatile boolean isSuspended; + + /** + * Target surface view is present and ready for rendering + */ + private volatile boolean isSurfaceReady; + private volatile Runnable releaseTask; private volatile boolean waitRelease; @@ -198,91 +226,153 @@ public MapRendererView(Context context, AttributeSet attrs, int defaultStyle) { super(context, attrs, defaultStyle); } - public void setupRenderer(Context context, int bitmapWidth, int bitmapHeight, MapRendererView oldView) { + public synchronized void setupRenderer(Context context, int bitmapWidth, int bitmapHeight, MapRendererView oldView) { + Log.v(TAG, "setupRenderer()"); NativeCore.checkIfLoaded(); - boolean inWindow = (bitmapWidth == 0 || bitmapHeight == 0); + waitInitialization(); - synchronized (this) { - if (!inWindow) { - _byteBuffer = ByteBuffer.allocateDirect(bitmapWidth * bitmapHeight * 4); + if (_mapRenderer != null) { + synchronized (_mapRenderer) { + _exportableMapRenderer = null; + if (!isSuspended && _mapRenderer.isRenderingInitialized()) { + Log.v(TAG, "Rendering release due to setupRenderer()"); + releaseRendering(); + } + removeRenderingView(); } + } - // Get display density factor - _densityFactor = inWindow ? getResources().getDisplayMetrics().density : 1.0f; + boolean inWindow = (bitmapWidth == 0 || bitmapHeight == 0); + _flipWindow = !inWindow; - // Disable battery saving mode - disableBatterySavingMode(); + _windowWidth = bitmapWidth; + _windowHeight = bitmapHeight; + if (!inWindow) { + _byteBuffer = ByteBuffer.allocateDirect(bitmapWidth * bitmapHeight * 4); + } + + // Get display density factor + _densityFactor = inWindow ? getResources().getDisplayMetrics().density : 1.0f; + + // Disable battery saving mode + disableBatterySavingMode(); + + // Get already present renderer if available + IMapRenderer oldRenderer = oldView != null ? oldView.getRenderer() : null; + + if (oldRenderer == null) { // Set initial frame rate limit for battery saving mode setMaximumFrameRate(20); // 20 frames per seconds - // Create instance of OsmAndCore::IMapRenderer - if (oldView == null) + isReinitializing = (isSuspended && _mapRenderer != null && _mapRenderer.isRenderingInitialized()); + if (!isReinitializing) { + Log.v(TAG, "Setting up new renderer to initialize..."); _mapRenderer = createMapRendererInstance(); - else - _mapRenderer = oldView.getRenderer(); - - // Create GLSurfaceView and add it to hierarchy - if (inWindow && _glSurfaceView != null) - removeAllViews(); - _glSurfaceView = new GLSurfaceView(context); - if (inWindow) - addView(_glSurfaceView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - else { - _windowWidth = bitmapWidth; - _windowHeight = bitmapHeight; + isInitializing = true; + isSuspended = false; + } else { + Log.v(TAG, "Setting up present renderer to reinitialize..."); } + } else { + Log.v(TAG, "Setting up provided renderer to reinitialize..."); - // Create map animator for that map - if (_mapAnimator == null) - _mapAnimator = new MapAnimator(false); - _mapAnimator.setMapRenderer(_mapRenderer); - - // Create map markers animator - if (_mapMarkersAnimator == null) - _mapMarkersAnimator = new MapMarkersAnimator(); - _mapMarkersAnimator.setMapRenderer(_mapRenderer); - - // Configure GLSurfaceView - _glSurfaceView.setPreserveEGLContextOnPause(true); - _glSurfaceView.setEGLContextClientVersion(3); - _glSurfaceView.setEGLConfigChooser(new ComponentSizeChooser(8, 8, 8, 0, 16, 0, inWindow)); - _glSurfaceView.setEGLContextFactory(new EGLContextFactory()); - if (!inWindow) - _glSurfaceView.setEGLWindowSurfaceFactory(new PixelbufferSurfaceFactory(bitmapWidth, bitmapHeight)); - _glSurfaceView.setRenderer(new RendererProxy()); - _glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - if (!inWindow) { - _glSurfaceView.surfaceCreated(null); - _glSurfaceView.surfaceChanged(null, 0, bitmapWidth, bitmapHeight); - isPresent = true; - } + // Use previous frame rate limit for battery saving mode + setMaximumFrameRate(oldView.getMaximumFrameRate()); + + // Use present renderer + _mapRenderer = oldRenderer; + + // Reinitialize renderer + isReinitializing = true; + + // Take other useful data from old map renderer view + _gpuWorkerContext = oldView.takeGPUWorkerContext(); + _gpuWorkerFakeSurface = oldView.takeGPUWorkerSurface(); + + setMapRendererSetupOptionsConfigurator(oldView.getMapRendererSetupOptionsConfigurator()); + } + + // Create rendering view + _renderingView = new RenderingView(context); + _renderingView.isOffscreen = !inWindow; + + // Create map animator for that map + if (_mapAnimator == null) { + _mapAnimator = new MapAnimator(false); + } + _mapAnimator.setMapRenderer(_mapRenderer); + + // Create map markers animator + if (_mapMarkersAnimator == null) { + _mapMarkersAnimator = new MapMarkersAnimator(); + } + _mapMarkersAnimator.setMapRenderer(_mapRenderer); + + // Configure rendering view + _renderingView.setPreserveEGLContextOnPause(true); + _renderingView.setEGLContextClientVersion(3); + _renderingView.setEGLConfigChooser(new ComponentSizeChooser(8, 8, 8, 0, 16, 0)); + _renderingView.setEGLContextFactory(new EGLContextFactory()); + if (!inWindow) { + _renderingView.setEGLWindowSurfaceFactory(new PixelbufferSurfaceFactory(bitmapWidth, bitmapHeight)); + } + _renderingView.setRenderer(new RendererProxy()); + _renderingView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + if (inWindow) { + addView(_renderingView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + } else { + _renderingView.initializeView(bitmapWidth, bitmapHeight); + isSurfaceReady = true; + } + } + + public EGLContext takeGPUWorkerContext() { + synchronized (_mapRenderer) { + EGLContext gpuWorkerContext = _gpuWorkerContext; + _gpuWorkerContext = null; + return gpuWorkerContext; + } + } + + public EGLSurface takeGPUWorkerSurface() { + synchronized (_mapRenderer) { + EGLSurface gpuWorkerFakeSurface = _gpuWorkerFakeSurface; + _gpuWorkerFakeSurface = null; + return gpuWorkerFakeSurface; } } @Override public void setVisibility(int visibility) { - if (_glSurfaceView != null) - _glSurfaceView.setVisibility(visibility); + synchronized (_mapRenderer) { + if (_renderingView != null) { + _renderingView.setVisibility(visibility); + } + } super.setVisibility(visibility); } public void addListener(MapRendererViewListener listener) { - if (!this.listeners.contains(listener)) { - List listeners = new ArrayList<>(); - listeners.addAll(this.listeners); - listeners.add(listener); - this.listeners = listeners; + synchronized (_mapRenderer) { + if (!isSuspended && !this.listeners.contains(listener)) { + List listeners = new ArrayList<>(); + listeners.addAll(this.listeners); + listeners.add(listener); + this.listeners = listeners; + } } } public void removeListener(MapRendererViewListener listener) { - if (this.listeners.contains(listener)) { - List listeners = new ArrayList<>(); - listeners.addAll(this.listeners); - listeners.remove(listener); - this.listeners = listeners; + synchronized (_mapRenderer) { + if (!isSuspended && this.listeners.contains(listener)) { + List listeners = new ArrayList<>(); + listeners.addAll(this.listeners); + listeners.remove(listener); + this.listeners = listeners; + } } } @@ -303,56 +393,101 @@ public IMapRendererSetupOptionsConfigurator getMapRendererSetupOptionsConfigurat protected abstract IMapRenderer createMapRendererInstance(); public Bitmap getBitmap() { - if (_byteBuffer != null) { + if (!isSuspended && _byteBuffer != null) { + if (_resultBitmap == null) { + _resultBitmap = Bitmap.createBitmap(_windowWidth, _windowHeight, Bitmap.Config.ARGB_8888); + } if (_renderingResultIsReady) { - Bitmap bitmap = Bitmap.createBitmap(_windowWidth, _windowHeight, Bitmap.Config.ARGB_8888); synchronized (_byteBuffer) { _byteBuffer.rewind(); - bitmap.copyPixelsFromBuffer(_byteBuffer); + _resultBitmap.copyPixelsFromBuffer(_byteBuffer); _renderingResultIsReady = false; } - Matrix matrix = new Matrix(); - matrix.preScale(1.0f, -1.0f); - _resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, _windowWidth, _windowHeight, matrix, true); } - if (_resultBitmap == null) - _resultBitmap = Bitmap.createBitmap(_windowWidth, _windowHeight, Bitmap.Config.ARGB_8888); return _resultBitmap; } else return null; } - public IMapRenderer getRenderer() { - return _mapRenderer; + public void waitInitialization() { + long getTime = System.currentTimeMillis(); + while ((isInitializing || isReinitializing) && System.currentTimeMillis() < getTime + INITIALIZE_TIMEOUT) { + try { + Thread.sleep(INITIALIZE_WAIT_TIME); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } } - public void stopRenderer() { - Log.v(TAG, "removeRendering()"); - NativeCore.checkIfLoaded(); + public synchronized IMapRenderer getRenderer() { + Log.v(TAG, "getRenderer()"); + + waitInitialization(); + + if (_mapRenderer == null || !_mapRenderer.isRenderingInitialized() || _exportableMapRenderer == null) { + Log.v(TAG, "Can't provide absent or not yet initialized renderer"); + return null; + } + + synchronized (_mapRenderer) { + if (isSuspended) { + Log.v(TAG, "Renderer was already suspended"); + } else { + Log.v(TAG, "Suspend renderer to reuse it in other view"); + isSuspended = true; + isReinitializing = false; + } + + // Stop and remove rendering view + removeRenderingView(); + + // Clean up data + listeners = new ArrayList<>(); + _mapAnimator = null; + _mapMarkersAnimator = null; + } + return _exportableMapRenderer.isRenderingInitialized() ? _exportableMapRenderer : null; + } + + public synchronized void stopRenderer() { + Log.v(TAG, "stopRenderer()"); + + waitInitialization(); + + if (_mapRenderer == null) { + Log.v(TAG, "Can't stop absent renderer"); + return; + } - if (isPresent && _glSurfaceView != null) { - isPresent = false; - Log.v(TAG, "Rendering release due to removeRendering()"); - _glSurfaceView.onPause(); + synchronized (_mapRenderer) { + if (isSuspended) { + Log.v(TAG, "Can't stop suspended renderer - it must be stopped elsewhere"); + return; + } + + Log.v(TAG, "Rendering release due to stopRenderer()"); releaseRendering(); - removeAllViews(); - _byteBuffer = null; - _resultBitmap = null; + + // Stop and remove rendering view + removeRenderingView(); + + // Clean up data + listeners = new ArrayList<>(); _mapAnimator = null; _mapMarkersAnimator = null; - _glSurfaceView = null; } } private void releaseRendering() { - if(releaseTask == null && _glSurfaceView != null) { - waitRelease = true; + Log.v(TAG, "releaseRendering()"); + if (releaseTask == null) { releaseTask = new Runnable() { @Override public void run() { - if (_mapRenderer.isRenderingInitialized()) { - Log.v(TAG, "Forcibly releasing rendering by schedule"); + if (!isSuspended && _mapRenderer != null && _mapRenderer.isRenderingInitialized()) { + Log.v(TAG, "Release rendering..."); _mapRenderer.releaseRendering(true); } synchronized (this) { @@ -361,14 +496,17 @@ public void run() { } } }; - _glSurfaceView.queueEvent(releaseTask); } - if(_glSurfaceView != null) { + if (!waitRelease && _renderingView != null) { + waitRelease = true; + _renderingView.queueEvent(releaseTask); synchronized (releaseTask) { long stopTime = System.currentTimeMillis(); - while(waitRelease){ - if (System.currentTimeMillis() > stopTime + RELEASE_STOP_TIMEOUT) + while (waitRelease){ + if (System.currentTimeMillis() > stopTime + RELEASE_STOP_TIMEOUT) { + waitRelease = false; return; + } try { releaseTask.wait(RELEASE_WAIT_TIMEOUT); } catch (InterruptedException ex) {} @@ -377,20 +515,32 @@ public void run() { } } + public void removeRenderingView() { + Log.v(TAG, "removeRenderingView()"); + isSurfaceReady = false; + if (_renderingView != null) { + if (_renderingView.isOffscreen) { + _renderingView.removeView(); + } else { + removeAllViews(); + } + _renderingView = null; + } + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); Log.v(TAG, "onAttachedToWindow()"); - isPresent = true; + isSurfaceReady = true; } @Override protected void onDetachedFromWindow() { Log.v(TAG, "onDetachedFromWindow()"); NativeCore.checkIfLoaded(); - - if (isPresent) { - isPresent = false; + if (isSurfaceReady) { + isSurfaceReady = false; // Surface and context are going to be destroyed, thus try to release rendering // before that will happen Log.v(TAG, "Rendering release due to onDetachedFromWindow()"); @@ -403,22 +553,19 @@ protected void onDetachedFromWindow() { public final void handleOnCreate(Bundle savedInstanceState) { Log.v(TAG, "handleOnCreate()"); NativeCore.checkIfLoaded(); - isPresent = true; + isSurfaceReady = true; } public final void handleOnSaveInstanceState(Bundle outState) { Log.v(TAG, "handleOnSaveInstanceState()"); NativeCore.checkIfLoaded(); - - //TODO: do something here } public final void handleOnDestroy() { Log.v(TAG, "handleOnDestroy()"); NativeCore.checkIfLoaded(); - - if (isPresent) { - isPresent = false; + if (isSurfaceReady) { + isSurfaceReady = false; // Don't delete map renderer here, since context destruction will happen later. // Map renderer will be automatically deleted by GC anyways. But queue // action to release rendering @@ -438,9 +585,12 @@ public final void handleOnPause() { Log.v(TAG, "handleOnPause()"); NativeCore.checkIfLoaded(); - // Inform GLSurfaceView that activity was paused - if (_glSurfaceView != null) - _glSurfaceView.onPause(); + // Inform rendering view that activity was paused + synchronized (_mapRenderer) { + if (!isSuspended && _renderingView != null) { + _renderingView.onPause(); + } + } } public final void handleOnResume() { @@ -448,8 +598,11 @@ public final void handleOnResume() { NativeCore.checkIfLoaded(); // Inform GLSurfaceView that activity was resumed - if (_glSurfaceView != null) - _glSurfaceView.onResume(); + synchronized (_mapRenderer) { + if (!isSuspended && _renderingView != null) { + _renderingView.onResume(); + } + } } public final void requestRender() { @@ -457,8 +610,11 @@ public final void requestRender() { NativeCore.checkIfLoaded(); // Request GLSurfaceView render a frame - if (_glSurfaceView != null) - _glSurfaceView.requestRender(); + synchronized (_mapRenderer) { + if (!isSuspended && _renderingView != null) { + _renderingView.requestRender(); + } + } } public final MapAnimator getMapAnimator() { @@ -765,10 +921,11 @@ public final PointI getTarget() { NativeCore.checkIfLoaded(); PointI fixedPixel = _mapRenderer.getState().getFixedPixel(); - if (fixedPixel.getX() >= 0 && fixedPixel.getY() >= 0) + if (fixedPixel.getX() >= 0 && fixedPixel.getY() >= 0) { return _mapRenderer.getState().getFixedLocation31(); - else + } else { return _mapRenderer.getState().getTarget31(); + } } public final PointI getTargetScreenPosition() { @@ -780,10 +937,11 @@ public final PointI getTargetScreenPosition() { public final boolean setTarget(PointI target31) { NativeCore.checkIfLoaded(); - if (_windowWidth > 0 && _windowHeight > 0) + if (_windowWidth > 0 && _windowHeight > 0) { return _mapRenderer.setMapTargetLocation(target31); - else + } else { return _mapRenderer.setTarget(target31); + } } public final boolean setTarget(PointI target31, boolean forcedUpdate, boolean disableUpdate) { @@ -1387,22 +1545,40 @@ public final float getGPUWaitTimePartLast1K() { } public final void resumeMapAnimation() { - _mapAnimationStartTime = SystemClock.uptimeMillis(); - _mapAnimationFinished = false; - _mapAnimator.resume(); + synchronized (_mapRenderer) { + _mapAnimationStartTime = SystemClock.uptimeMillis(); + _mapAnimationFinished = false; + if (_mapAnimator != null) { + _mapAnimator.resume(); + } + } } public final boolean isMapAnimationPaused() { - return _mapAnimator.isPaused(); + boolean result = true; + synchronized (_mapRenderer) { + if (_mapAnimator != null) { + result = _mapAnimator.isPaused(); + } + } + return result; } public final void pauseMapAnimation() { - _mapAnimator.pause(); + synchronized (_mapRenderer) { + if (_mapAnimator != null) { + _mapAnimator.pause(); + } + } } public final void stopMapAnimation() { - _mapAnimator.pause(); - _mapAnimator.getAllAnimations(); + synchronized (_mapRenderer) { + if (_mapAnimator != null) { + _mapAnimator.pause(); + _mapAnimator.getAllAnimations(); + } + } } public final boolean isMapAnimationFinished() { @@ -1410,22 +1586,40 @@ public final boolean isMapAnimationFinished() { } public final void resumeMapMarkersAnimation() { - _mapMarkersAnimationStartTime = SystemClock.uptimeMillis(); - _mapMarkersAnimationFinished = false; - _mapMarkersAnimator.resume(); + synchronized (_mapRenderer) { + _mapMarkersAnimationStartTime = SystemClock.uptimeMillis(); + _mapMarkersAnimationFinished = false; + if (_mapMarkersAnimator != null) { + _mapMarkersAnimator.resume(); + } + } } public final boolean isMapMarkersAnimationPaused() { - return _mapMarkersAnimator.isPaused(); + boolean result = true; + synchronized (_mapRenderer) { + if (_mapMarkersAnimator != null) { + result = _mapMarkersAnimator.isPaused(); + } + } + return result; } public final void pauseMapMarkersAnimation() { - _mapMarkersAnimator.pause(); + synchronized (_mapRenderer) { + if (_mapMarkersAnimator != null) { + _mapMarkersAnimator.pause(); + } + } } public final void stopMapMarkersAnimation() { - _mapMarkersAnimator.pause(); - _mapMarkersAnimator.getAllAnimations(); + synchronized (_mapRenderer) { + if (_mapMarkersAnimator != null) { + _mapMarkersAnimator.pause(); + _mapMarkersAnimator.getAllAnimations(); + } + } } public final boolean isMapMarkersAnimationFinished() { @@ -1505,8 +1699,7 @@ public BaseConfigChooser(int[] configSpec) { public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { int[] num_config = new int[1]; - if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, - num_config)) { + if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config)) { Log.e(TAG, "Failed to choose EGL config"); } @@ -1517,8 +1710,7 @@ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { } EGLConfig[] configs = new EGLConfig[numConfigs]; - if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, - num_config)) { + if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config)) { Log.e(TAG, "Failed to choose suitable EGL configs"); } EGLConfig config = chooseConfig(egl, display, configs); @@ -1553,9 +1745,9 @@ private int[] filterConfigSpec(int[] configSpec) { */ private class ComponentSizeChooser extends BaseConfigChooser { public ComponentSizeChooser(int redSize, int greenSize, int blueSize, - int alphaSize, int depthSize, int stencilSize, boolean inWindow) { + int alphaSize, int depthSize, int stencilSize) { super(new int[] { - EGL10.EGL_SURFACE_TYPE, inWindow ? EGL10.EGL_WINDOW_BIT : EGL10.EGL_PBUFFER_BIT, + EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT | EGL10.EGL_PBUFFER_BIT, EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, EGL10.EGL_BLUE_SIZE, blueSize, @@ -1591,8 +1783,7 @@ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGL10.EGL_BLUE_SIZE, 0); int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); - if ((r == mRedSize) && (g == mGreenSize) - && (b == mBlueSize) && (a == mAlphaSize)) { + if ((r == mRedSize) && (g == mGreenSize) && (b == mBlueSize) && (a == mAlphaSize)) { if (d > maxDepthSize) { maxDepthSize = d; bestConfig = config; @@ -1648,16 +1839,63 @@ private final class EGLContextFactory implements GLSurfaceView.EGLContextFactory public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { Log.v(TAG, "EGLContextFactory.createContext()..."); - // In case context is create while rendering is initialized, release it first - if (_mapRenderer != null && _mapRenderer.isRenderingInitialized()) { - Log.v(TAG, "Rendering is still initialized during context creation, " + - "force releasing it!"); + if (isSuspended && !isReinitializing) { + Log.v(TAG, "No use to create context for moved renderer"); + return null; + } + + // In case context is created while rendering is initialized, release it first + if (!isReinitializing && _mapRenderer != null && _mapRenderer.isRenderingInitialized()) { + Log.v(TAG, "Rendering is still initialized during context creation, force releasing it!"); // Since there's no more context, where previous resources were created, // they are lost. Forcibly release rendering _mapRenderer.releaseRendering(true); } + // Create GPU-worker EGL context if needed + if (_gpuWorkerContext == null) { + try { + _gpuWorkerContext = egl.eglCreateContext( + display, + eglConfig, + EGL10.EGL_NO_CONTEXT, + contextAttributes); + } catch (Exception e) { + Log.e(TAG, "Failed to create GPU-worker EGL context", e); + } + if (_gpuWorkerContext == null || _gpuWorkerContext == EGL10.EGL_NO_CONTEXT) { + Log.e(TAG, "Failed to create GPU-worker EGL context: " + + getEglErrorString(egl.eglGetError())); + _gpuWorkerContext = null; + } + + // Create GPU-worker EGL surface + if (_gpuWorkerContext != null) { + if (_gpuWorkerFakeSurface != null) { + Log.w(TAG, "Previous GPU-worker EGL surface was not destroyed properly!"); + _gpuWorkerFakeSurface = null; + } + try { + _gpuWorkerFakeSurface = egl.eglCreatePbufferSurface( + display, + eglConfig, + gpuWorkerSurfaceAttributes); + } catch (Exception e) { + Log.e(TAG, "Failed to create GPU-worker EGL surface", e); + _gpuWorkerFakeSurface = null; + } + if (_gpuWorkerFakeSurface == null || _gpuWorkerFakeSurface == EGL10.EGL_NO_SURFACE) { + Log.e(TAG, "Failed to create GPU-worker EGL surface: " + + getEglErrorString(egl.eglGetError())); + + egl.eglDestroyContext(display, _gpuWorkerContext); + _gpuWorkerContext = null; + _gpuWorkerFakeSurface = null; + } + } + } + // Create main EGL context if (_mainContext != null) { Log.w(TAG, "Previous main EGL context was not destroyed properly!"); @@ -1667,73 +1905,29 @@ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConf _mainContext = egl.eglCreateContext( display, eglConfig, - EGL10.EGL_NO_CONTEXT, + _gpuWorkerContext != null ? _gpuWorkerContext : EGL10.EGL_NO_CONTEXT, contextAttributes); } catch (Exception e) { Log.e(TAG, "Failed to create main EGL context", e); return null; } if (_mainContext == null || _mainContext == EGL10.EGL_NO_CONTEXT) { - Log.e(TAG, "Failed to create main EGL context: " + - getEglErrorString(egl.eglGetError())); + Log.e(TAG, "Failed to create main EGL context: " + getEglErrorString(egl.eglGetError())); _mainContext = null; return null; } - // Create GPU-worker EGL context - if (_gpuWorkerContext != null) { - Log.w(TAG, "Previous GPU-worker EGL context was not destroyed properly!"); - _gpuWorkerContext = null; - } - try { - _gpuWorkerContext = egl.eglCreateContext( - display, - eglConfig, - _mainContext, - contextAttributes); - } catch (Exception e) { - Log.e(TAG, "Failed to create GPU-worker EGL context", e); - } - if (_gpuWorkerContext == null || _gpuWorkerContext == EGL10.EGL_NO_CONTEXT) { - Log.e(TAG, "Failed to create GPU-worker EGL context: " + - getEglErrorString(egl.eglGetError())); - _gpuWorkerContext = null; - } - - // Create GPU-worker EGL surface - if (_gpuWorkerContext != null) { - if (_gpuWorkerFakeSurface != null) { - Log.w(TAG, "Previous GPU-worker EGL surface was not destroyed properly!"); - _gpuWorkerFakeSurface = null; - } - try { - _gpuWorkerFakeSurface = egl.eglCreatePbufferSurface( - display, - eglConfig, - gpuWorkerSurfaceAttributes); - } catch (Exception e) { - Log.e(TAG, "Failed to create GPU-worker EGL surface", e); - } - if (_gpuWorkerFakeSurface == null || _gpuWorkerFakeSurface == EGL10.EGL_NO_SURFACE) { - Log.e(TAG, "Failed to create GPU-worker EGL surface: " + - getEglErrorString(egl.eglGetError())); - - egl.eglDestroyContext(display, _gpuWorkerContext); - _gpuWorkerContext = null; - _gpuWorkerFakeSurface = null; - } - } - // Save reference to EGL display _display = display; // Change renderer setup options _setupOptions = new MapRendererSetupOptions(); _setupOptions.setDisplayDensityFactor(_densityFactor); - if (_mapRendererSetupOptionsConfigurator != null) + if (_mapRendererSetupOptionsConfigurator != null) { _mapRendererSetupOptionsConfigurator.configureMapRendererSetupOptions(_setupOptions); + } return _mainContext; } @@ -1742,9 +1936,8 @@ public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { Log.v(TAG, "EGLContextFactory.destroyContext()..."); // In case context is destroyed while rendering is initialized, release it first - if (_mapRenderer != null && _mapRenderer.isRenderingInitialized()) { - Log.v(TAG, "Rendering is still initialized during context destruction, " + - "force releasing it!"); + if (!isSuspended && _mapRenderer != null && _mapRenderer.isRenderingInitialized()) { + Log.v(TAG, "Rendering is still initialized during context destruction, force releasing it!"); // Since there's no more context, where previous resources were created, // they are lost. Forcibly release rendering @@ -1752,13 +1945,13 @@ public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { } // Destroy GPU-worker EGL surface (if present) - if (_gpuWorkerFakeSurface != null) { + if (!isSuspended && _gpuWorkerFakeSurface != null) { egl.eglDestroySurface(display, _gpuWorkerFakeSurface); _gpuWorkerFakeSurface = null; } // Destroy GPU-worker EGL context (if present) - if (_gpuWorkerContext != null) { + if (!isSuspended && _gpuWorkerContext != null) { egl.eglDestroyContext(display, _gpuWorkerContext); _gpuWorkerContext = null; } @@ -1781,10 +1974,15 @@ private final class RendererProxy implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 gl, EGLConfig config) { Log.v(TAG, "RendererProxy.onSurfaceCreated()..."); + if (isSuspended && !isReinitializing) { + Log.v(TAG, "No use to create surface for moved renderer"); + return; + } + // In case a new surface was created, and rendering was initialized it means that // surface was changed, so release rendering to allow it to initialize on next // call to onSurfaceChanged - if (_mapRenderer.isRenderingInitialized()) { + if (!isReinitializing && _mapRenderer != null && _mapRenderer.isRenderingInitialized()) { Log.v(TAG, "Releasing rendering due to surface recreation"); // Context still exists here and is active, so just release resources @@ -1795,15 +1993,23 @@ public void onSurfaceCreated(GL10 gl, EGLConfig config) { public void onSurfaceChanged(GL10 gl, int width, int height) { Log.v(TAG, "RendererProxy.onSurfaceChanged()..."); + if (isSuspended && !isReinitializing) { + Log.v(TAG, "No use to change surface for moved renderer"); + return; + } + // Set new "window" size and viewport that covers entire "window" _windowWidth = width; _windowHeight = height; - _mapRenderer.setWindowSize(new PointI(width, height)); - _mapRenderer.setViewport(new AreaI(new PointI(0, 0), new PointI(width, height))); + if (_mapRenderer != null) { + _mapRenderer.setWindowSize(new PointI(width, height)); + _mapRenderer.setViewport(new AreaI(new PointI(0, 0), new PointI(width, height))); + _mapRenderer.setFlip(_flipWindow); + } - // In case rendering is not initialized, initialize it + // In case rendering is not initialized or just needs to be reinitialized, initialize it // (happens when surface is created for the first time, or recreated) - if (!_mapRenderer.isRenderingInitialized()) { + if (_mapRenderer != null && (isReinitializing || !_mapRenderer.isRenderingInitialized())) { Log.v(TAG, "Initializing rendering due to surface size change"); if (_gpuWorkerContext != null && _gpuWorkerFakeSurface != null) { @@ -1817,19 +2023,27 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { } _setupOptions.setFrameUpdateRequestCallback(_renderRequestCallback.getBinding()); _mapRenderer.setup(_setupOptions); - if (!_mapRenderer.initializeRendering(true)) - Log.e(TAG, "Failed to initialize rendering"); - else { + if (_mapRenderer.initializeRendering(false)) { + _exportableMapRenderer = _mapRenderer; + if (isReinitializing) { + isReinitializing = false; + isSuspended = false; + } _frameStartTime = SystemClock.uptimeMillis(); _frameRenderTime = 0; + } else { + _exportableMapRenderer = null; + isReinitializing = false; + Log.e(TAG, "Failed to initialize rendering"); } + isInitializing = false; } } public void onDrawFrame(GL10 gl) { // In case rendering was not initialized yet, don't do anything - if (!_mapRenderer.isRenderingInitialized()) { - Log.w(TAG, "Rendering not yet initialized"); + if (isSuspended || _mapRenderer == null || !_mapRenderer.isRenderingInitialized()) { + Log.w(TAG, "Can't draw a frame: renderer either suspended or isn't yet initialized"); return; } @@ -1887,8 +2101,7 @@ public void onDrawFrame(GL10 gl) { _frameRate = 1000.0f / (float) _frameRenderTime; _frameRateLast1K = _frameRateLast1K > 0.0 ? (_frameRateLast1K * 999.0f + _frameRate) / 1000.0f : _frameRate; - if (_batterySavingMode && _maxFrameRate > 0) - { + if (_batterySavingMode && _maxFrameRate > 0) { long extraTime = _maxFrameTime - _frameRenderTime; if (extraTime > 0) { try { @@ -1908,8 +2121,9 @@ private class RenderRequestCallback extends MapRendererSetupOptions.IFrameUpdateRequestCallback { @Override public void method(IMapRenderer mapRenderer) { - if (_glSurfaceView != null) - _glSurfaceView.requestRender(); + if (!isSuspended && _renderingView != null) { + _renderingView.requestRender(); + } } } @@ -1986,4 +2200,26 @@ public void method(IMapRenderer mapRenderer) { public interface IMapRendererSetupOptionsConfigurator { void configureMapRendererSetupOptions(MapRendererSetupOptions mapRendererSetupOptions); } + + public class RenderingView extends GLSurfaceView { + + public boolean isOffscreen = false; + + public RenderingView(Context context) { + super(context); + } + + public void initializeView(int bitmapWidth, int bitmapHeight) { + if (isOffscreen) { + super.surfaceCreated(null); + super.surfaceChanged(null, 0, bitmapWidth, bitmapHeight); + } + } + + public void removeView() { + if (isOffscreen) { + super.onDetachedFromWindow(); + } + } + } } From 3147481421b9cf40c384cf913e8acc53ae196f34 Mon Sep 17 00:00:00 2001 From: Alex Andrewschenko Date: Thu, 21 Nov 2024 11:42:06 +0100 Subject: [PATCH 2/5] Speed up OpenGL initialization for Android Auto --- src/Map/AtlasMapRendererSymbolsStage.cpp | 20 ++++++ src/Map/AtlasMapRendererSymbolsStage.h | 2 + src/Map/MapRenderer.cpp | 4 +- src/Map/MapRenderer.h | 2 +- .../AtlasMapRendererSymbolsStage_OpenGL.cpp | 1 + .../osmand/core/android/MapRendererView.java | 62 ++++++------------- 6 files changed, 46 insertions(+), 45 deletions(-) diff --git a/src/Map/AtlasMapRendererSymbolsStage.cpp b/src/Map/AtlasMapRendererSymbolsStage.cpp index 7f6c8b581..69de2328e 100644 --- a/src/Map/AtlasMapRendererSymbolsStage.cpp +++ b/src/Map/AtlasMapRendererSymbolsStage.cpp @@ -54,6 +54,26 @@ OsmAnd::AtlasMapRendererSymbolsStage::AtlasMapRendererSymbolsStage(AtlasMapRende OsmAnd::AtlasMapRendererSymbolsStage::~AtlasMapRendererSymbolsStage() = default; +bool OsmAnd::AtlasMapRendererSymbolsStage::release(bool gpuContextLost) +{ + { + QWriteLocker scopedLocker(&_lastPreparedIntersectionsLock); + + _lastPreparedIntersections.clear(); + } + + { + QWriteLocker scopedLocker(&_lastVisibleSymbolsLock); + + _lastVisibleSymbols.clear(); + } + + _lastAcceptedMapSymbolsByOrder.clear(); + denseSymbols.clear(); + renderableSymbols.clear(); + return true; +} + void OsmAnd::AtlasMapRendererSymbolsStage::prepare(AtlasMapRenderer_Metrics::Metric_renderFrame* const metric) { Stopwatch stopwatch(metric != nullptr); diff --git a/src/Map/AtlasMapRendererSymbolsStage.h b/src/Map/AtlasMapRendererSymbolsStage.h index 5d763b56e..dc8058aad 100644 --- a/src/Map/AtlasMapRendererSymbolsStage.h +++ b/src/Map/AtlasMapRendererSymbolsStage.h @@ -460,6 +460,8 @@ namespace OsmAnd AtlasMapRendererSymbolsStage(AtlasMapRenderer* const renderer); virtual ~AtlasMapRendererSymbolsStage(); + bool release(bool gpuContextLost) override; + void queryLastPreparedSymbolsAt( const PointI screenPoint, QList& outMapSymbols) const; diff --git a/src/Map/MapRenderer.cpp b/src/Map/MapRenderer.cpp index 1b8b1f304..bc01a3e81 100644 --- a/src/Map/MapRenderer.cpp +++ b/src/Map/MapRenderer.cpp @@ -104,7 +104,6 @@ OsmAnd::MapRendererSetupOptions OsmAnd::MapRenderer::getSetupOptions() const bool OsmAnd::MapRenderer::setup(const MapRendererSetupOptions& setupOptions) { - // We can not change setup options renderer once rendering has been initialized if (_isRenderingInitialized) { QWriteLocker scopedLocker(&_setupOptionsLock); @@ -1280,6 +1279,9 @@ int OsmAnd::MapRenderer::getSymbolsUpdateInterval() void OsmAnd::MapRenderer::setSymbolsUpdateInterval(int interval) { _symbolsUpdateInterval = interval; + clearSymbolsUpdated(); + shouldUpdateSymbols(); + invalidateFrame(); } void OsmAnd::MapRenderer::shouldUpdateSymbols() diff --git a/src/Map/MapRenderer.h b/src/Map/MapRenderer.h index e415edbda..4151fe227 100644 --- a/src/Map/MapRenderer.h +++ b/src/Map/MapRenderer.h @@ -125,7 +125,7 @@ namespace OsmAnd const std::shared_ptr& resource); bool validatePublishedMapSymbolsIntegrity(); QAtomicInt _suspendSymbolsUpdateCounter; - int _symbolsUpdateInterval; + volatile int _symbolsUpdateInterval; volatile bool _updateSymbols; volatile bool _freshSymbols; volatile bool _targetIsElevated; diff --git a/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.cpp b/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.cpp index c77ecedfc..cd3da8c73 100644 --- a/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.cpp +++ b/src/Map/OpenGL/AtlasMapRendererSymbolsStage_OpenGL.cpp @@ -305,6 +305,7 @@ bool OsmAnd::AtlasMapRendererSymbolsStage_OpenGL::release(bool gpuContextLost) ok = ok && releaseOnSurfaceVector(gpuContextLost); ok = ok && (!_model3DSubstage || _model3DSubstage->release(gpuContextLost)); ok = ok && releaseVisibilityCheck(gpuContextLost); + ok = ok && AtlasMapRendererSymbolsStage::release(gpuContextLost); return ok; } diff --git a/wrappers/android/src/net/osmand/core/android/MapRendererView.java b/wrappers/android/src/net/osmand/core/android/MapRendererView.java index 2c56cdfa4..f011db015 100644 --- a/wrappers/android/src/net/osmand/core/android/MapRendererView.java +++ b/wrappers/android/src/net/osmand/core/android/MapRendererView.java @@ -145,9 +145,7 @@ public abstract class MapRendererView extends FrameLayout { */ private final RenderRequestCallback _renderRequestCallback = new RenderRequestCallback(); - private MapRendererSetupOptions _setupOptions; - - private IMapRendererSetupOptionsConfigurator _mapRendererSetupOptionsConfigurator; + public MapRendererSetupOptions setupOptions = new MapRendererSetupOptions(); /** * Reference to valid EGL display @@ -253,8 +251,8 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit _byteBuffer = ByteBuffer.allocateDirect(bitmapWidth * bitmapHeight * 4); } - // Get display density factor - _densityFactor = inWindow ? getResources().getDisplayMetrics().density : 1.0f; + // Set display density factor + setupOptions.setDisplayDensityFactor(inWindow ? getResources().getDisplayMetrics().density : 1.0f); // Disable battery saving mode disableBatterySavingMode(); @@ -290,8 +288,6 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit // Take other useful data from old map renderer view _gpuWorkerContext = oldView.takeGPUWorkerContext(); _gpuWorkerFakeSurface = oldView.takeGPUWorkerSurface(); - - setMapRendererSetupOptionsConfigurator(oldView.getMapRendererSetupOptionsConfigurator()); } // Create rendering view @@ -376,15 +372,6 @@ public void removeListener(MapRendererViewListener listener) { } } - public void setMapRendererSetupOptionsConfigurator( - IMapRendererSetupOptionsConfigurator configurator) { - _mapRendererSetupOptionsConfigurator = configurator; - } - - public IMapRendererSetupOptionsConfigurator getMapRendererSetupOptionsConfigurator() { - return _mapRendererSetupOptionsConfigurator; - } - /** * Method to create instance of OsmAndCore::IMapRenderer * @@ -440,7 +427,6 @@ public synchronized IMapRenderer getRenderer() { isReinitializing = false; } - // Stop and remove rendering view removeRenderingView(); // Clean up data @@ -463,16 +449,14 @@ public synchronized void stopRenderer() { synchronized (_mapRenderer) { if (isSuspended) { - Log.v(TAG, "Can't stop suspended renderer - it must be stopped elsewhere"); - return; - } - - Log.v(TAG, "Rendering release due to stopRenderer()"); - releaseRendering(); + Log.v(TAG, "Stopping suspended renderer..."); + _mapRenderer.releaseRendering(true); + } else { + Log.v(TAG, "Stopping active renderer..."); + releaseRendering(); - // Stop and remove rendering view - removeRenderingView(); - + removeRenderingView(); + } // Clean up data listeners = new ArrayList<>(); _mapAnimator = null; @@ -1922,13 +1906,6 @@ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConf // Save reference to EGL display _display = display; - // Change renderer setup options - _setupOptions = new MapRendererSetupOptions(); - _setupOptions.setDisplayDensityFactor(_densityFactor); - if (_mapRendererSetupOptionsConfigurator != null) { - _mapRendererSetupOptionsConfigurator.configureMapRendererSetupOptions(_setupOptions); - } - return _mainContext; } @@ -2010,19 +1987,18 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { // In case rendering is not initialized or just needs to be reinitialized, initialize it // (happens when surface is created for the first time, or recreated) if (_mapRenderer != null && (isReinitializing || !_mapRenderer.isRenderingInitialized())) { - Log.v(TAG, "Initializing rendering due to surface size change"); - + Log.v(TAG, "Initializing rendering"); if (_gpuWorkerContext != null && _gpuWorkerFakeSurface != null) { - _setupOptions.setGpuWorkerThreadEnabled(true); - _setupOptions.setGpuWorkerThreadPrologue(_gpuWorkerThreadPrologue.getBinding()); - _setupOptions.setGpuWorkerThreadEpilogue(_gpuWorkerThreadEpilogue.getBinding()); + setupOptions.setGpuWorkerThreadEnabled(true); + setupOptions.setGpuWorkerThreadPrologue(_gpuWorkerThreadPrologue.getBinding()); + setupOptions.setGpuWorkerThreadEpilogue(_gpuWorkerThreadEpilogue.getBinding()); } else { - _setupOptions.setGpuWorkerThreadEnabled(false); - _setupOptions.setGpuWorkerThreadPrologue(null); - _setupOptions.setGpuWorkerThreadEpilogue(null); + setupOptions.setGpuWorkerThreadEnabled(false); + setupOptions.setGpuWorkerThreadPrologue(null); + setupOptions.setGpuWorkerThreadEpilogue(null); } - _setupOptions.setFrameUpdateRequestCallback(_renderRequestCallback.getBinding()); - _mapRenderer.setup(_setupOptions); + setupOptions.setFrameUpdateRequestCallback(_renderRequestCallback.getBinding()); + _mapRenderer.setup(setupOptions); if (_mapRenderer.initializeRendering(false)) { _exportableMapRenderer = _mapRenderer; if (isReinitializing) { From 60a9b22bfc50b71d27e44a2380ad33890da2bbf9 Mon Sep 17 00:00:00 2001 From: Alex Andrewschenko Date: Thu, 21 Nov 2024 22:41:28 +0100 Subject: [PATCH 3/5] Quick Android Auto initialization --- .../osmand/core/android/MapRendererView.java | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/wrappers/android/src/net/osmand/core/android/MapRendererView.java b/wrappers/android/src/net/osmand/core/android/MapRendererView.java index f011db015..f58443efb 100644 --- a/wrappers/android/src/net/osmand/core/android/MapRendererView.java +++ b/wrappers/android/src/net/osmand/core/android/MapRendererView.java @@ -56,11 +56,8 @@ public abstract class MapRendererView extends FrameLayout { private static final String TAG = "OsmAndCore:Android/MapRendererView"; - private final static long INITIALIZE_TIMEOUT = 4000; - private final static long INITIALIZE_WAIT_TIME = 400; - private final static long RELEASE_STOP_TIMEOUT = 4000; - private final static long RELEASE_WAIT_TIMEOUT = 400; + private final static long RELEASE_WAIT_TIMEOUT = 50; /** * Reference to OsmAndCore::IMapRenderer instance @@ -228,11 +225,10 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit Log.v(TAG, "setupRenderer()"); NativeCore.checkIfLoaded(); - waitInitialization(); - if (_mapRenderer != null) { synchronized (_mapRenderer) { _exportableMapRenderer = null; + stopRenderingView(); if (!isSuspended && _mapRenderer.isRenderingInitialized()) { Log.v(TAG, "Rendering release due to setupRenderer()"); releaseRendering(); @@ -272,6 +268,7 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit isSuspended = false; } else { Log.v(TAG, "Setting up present renderer to reinitialize..."); + isInitializing = false; } } else { Log.v(TAG, "Setting up provided renderer to reinitialize..."); @@ -283,6 +280,7 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit _mapRenderer = oldRenderer; // Reinitialize renderer + isInitializing = false; isReinitializing = true; // Take other useful data from old map renderer view @@ -397,36 +395,24 @@ public Bitmap getBitmap() { return null; } - public void waitInitialization() { - long getTime = System.currentTimeMillis(); - while ((isInitializing || isReinitializing) && System.currentTimeMillis() < getTime + INITIALIZE_TIMEOUT) { - try { - Thread.sleep(INITIALIZE_WAIT_TIME); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - public synchronized IMapRenderer getRenderer() { Log.v(TAG, "getRenderer()"); - waitInitialization(); - if (_mapRenderer == null || !_mapRenderer.isRenderingInitialized() || _exportableMapRenderer == null) { Log.v(TAG, "Can't provide absent or not yet initialized renderer"); return null; } synchronized (_mapRenderer) { + stopRenderingView(); if (isSuspended) { Log.v(TAG, "Renderer was already suspended"); } else { Log.v(TAG, "Suspend renderer to reuse it in other view"); isSuspended = true; + isInitializing = false; isReinitializing = false; } - removeRenderingView(); // Clean up data @@ -440,8 +426,6 @@ public synchronized IMapRenderer getRenderer() { public synchronized void stopRenderer() { Log.v(TAG, "stopRenderer()"); - waitInitialization(); - if (_mapRenderer == null) { Log.v(TAG, "Can't stop absent renderer"); return; @@ -453,8 +437,8 @@ public synchronized void stopRenderer() { _mapRenderer.releaseRendering(true); } else { Log.v(TAG, "Stopping active renderer..."); + stopRenderingView(); releaseRendering(); - removeRenderingView(); } // Clean up data @@ -499,6 +483,13 @@ public void run() { } } + public void stopRenderingView() { + Log.v(TAG, "stopRenderingView()"); + if (_renderingView != null) { + _renderingView.onPause(); + } + } + public void removeRenderingView() { Log.v(TAG, "removeRenderingView()"); isSurfaceReady = false; @@ -1987,7 +1978,7 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { // In case rendering is not initialized or just needs to be reinitialized, initialize it // (happens when surface is created for the first time, or recreated) if (_mapRenderer != null && (isReinitializing || !_mapRenderer.isRenderingInitialized())) { - Log.v(TAG, "Initializing rendering"); + Log.v(TAG, "Rendering is initializing..."); if (_gpuWorkerContext != null && _gpuWorkerFakeSurface != null) { setupOptions.setGpuWorkerThreadEnabled(true); setupOptions.setGpuWorkerThreadPrologue(_gpuWorkerThreadPrologue.getBinding()); @@ -2007,6 +1998,7 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { } _frameStartTime = SystemClock.uptimeMillis(); _frameRenderTime = 0; + Log.v(TAG, "Rendering is initialized successfully"); } else { _exportableMapRenderer = null; isReinitializing = false; From f8063b44b1fed15d35aba2f95e47fd38c907bfc8 Mon Sep 17 00:00:00 2001 From: Alex Andrewschenko Date: Tue, 26 Nov 2024 19:03:31 +0100 Subject: [PATCH 4/5] Use already initialized renderer in Android Auto --- src/Map/MapRenderer.cpp | 23 +- .../osmand/core/android/MapRendererView.java | 696 ++++++++++++------ 2 files changed, 495 insertions(+), 224 deletions(-) diff --git a/src/Map/MapRenderer.cpp b/src/Map/MapRenderer.cpp index bc01a3e81..f8c6798a1 100644 --- a/src/Map/MapRenderer.cpp +++ b/src/Map/MapRenderer.cpp @@ -315,11 +315,22 @@ bool OsmAnd::MapRenderer::initializeRendering(bool fresh /* = true */) if (_isRenderingInitialized && fresh) return false; + if (_isRenderingInitialized && !fresh) { + _renderThreadId = QThread::currentThreadId(); + return true; + } + bool ok; if (_isRenderingInitialized) { - ok = gpuAPI->release(true); + _renderThreadId = QThread::currentThreadId(); + + ok = doReleaseRendering(false); + if (!ok) + return false; + + ok = gpuAPI->release(false); if (!ok) return false; } @@ -330,15 +341,7 @@ bool OsmAnd::MapRenderer::initializeRendering(bool fresh /* = true */) if (!ok) return false; - if (_isRenderingInitialized) - { - _renderThreadId = QThread::currentThreadId(); - - ok = doReleaseRendering(true); - if (!ok) - return false; - } - else + if (!_isRenderingInitialized) { ok = preInitializeRendering(); if (!ok) diff --git a/wrappers/android/src/net/osmand/core/android/MapRendererView.java b/wrappers/android/src/net/osmand/core/android/MapRendererView.java index f58443efb..324d53626 100644 --- a/wrappers/android/src/net/osmand/core/android/MapRendererView.java +++ b/wrappers/android/src/net/osmand/core/android/MapRendererView.java @@ -42,6 +42,7 @@ import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL; import javax.microedition.khronos.opengles.GL10; import java.nio.ByteBuffer; @@ -144,38 +145,27 @@ public abstract class MapRendererView extends FrameLayout { public MapRendererSetupOptions setupOptions = new MapRendererSetupOptions(); - /** - * Reference to valid EGL display - */ - private EGLDisplay _display; - - /** - * Main EGL context - */ - private EGLContext _mainContext; - - /** - * GPU-worker EGL context - */ - private EGLContext _gpuWorkerContext; - - /** - * GPU-worker EGL surface. Not used in reality, but required to initialize context - */ - private EGLSurface _gpuWorkerFakeSurface; + enum EGLThreadOperation { + NO_OPERATION, + CHOOSE_CONFIG, + CREATE_CONTEXTS, + CREATE_WINDOW_SURFACE, + CREATE_PIXELBUFFER_SURFACE, + DESTROY_SURFACE, + DESTROY_CONTEXTS + } /** - * Device density factor (160 DPI == 1.0f) + * EGL Thread */ - private float _densityFactor; - + public volatile EGLThread eglThread; /** * Width and height of current window */ private int _windowWidth; private int _windowHeight; - private boolean _flipWindow; + private boolean _inWindow; private int frameId; @@ -199,6 +189,16 @@ public abstract class MapRendererView extends FrameLayout { */ private volatile boolean isSurfaceReady; + /** + * View is on pause + */ + private volatile boolean isPaused; + + /** + * Rendering should be initialized after view is resumed + */ + private volatile boolean initOnResume; + private volatile Runnable releaseTask; private volatile boolean waitRelease; @@ -237,24 +237,23 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit } } - boolean inWindow = (bitmapWidth == 0 || bitmapHeight == 0); - _flipWindow = !inWindow; + _inWindow = (bitmapWidth == 0 || bitmapHeight == 0); _windowWidth = bitmapWidth; _windowHeight = bitmapHeight; - if (!inWindow) { + if (!_inWindow) { _byteBuffer = ByteBuffer.allocateDirect(bitmapWidth * bitmapHeight * 4); } // Set display density factor - setupOptions.setDisplayDensityFactor(inWindow ? getResources().getDisplayMetrics().density : 1.0f); + setupOptions.setDisplayDensityFactor(_inWindow ? getResources().getDisplayMetrics().density : 1.0f); // Disable battery saving mode disableBatterySavingMode(); // Get already present renderer if available - IMapRenderer oldRenderer = oldView != null ? oldView.getRenderer() : null; + IMapRenderer oldRenderer = oldView != null ? oldView.suspendRenderer() : null; if (oldRenderer == null) { // Set initial frame rate limit for battery saving mode @@ -263,6 +262,8 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit isReinitializing = (isSuspended && _mapRenderer != null && _mapRenderer.isRenderingInitialized()); if (!isReinitializing) { Log.v(TAG, "Setting up new renderer to initialize..."); + eglThread = new EGLThread("EGLThread"); + eglThread.start(); _mapRenderer = createMapRendererInstance(); isInitializing = true; isSuspended = false; @@ -273,6 +274,11 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit } else { Log.v(TAG, "Setting up provided renderer to reinitialize..."); + if (_mapRenderer != oldRenderer && isSuspended && _mapRenderer.isRenderingInitialized()) { + Log.v(TAG, "Releasing suspended renderer"); + _mapRenderer.releaseRendering(true); + } + // Use previous frame rate limit for battery saving mode setMaximumFrameRate(oldView.getMaximumFrameRate()); @@ -283,14 +289,22 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit isInitializing = false; isReinitializing = true; - // Take other useful data from old map renderer view - _gpuWorkerContext = oldView.takeGPUWorkerContext(); - _gpuWorkerFakeSurface = oldView.takeGPUWorkerSurface(); - } + // Take all EGL references from old map renderer view + eglThread = oldView.eglThread; - // Create rendering view - _renderingView = new RenderingView(context); - _renderingView.isOffscreen = !inWindow; + // Reset rendering options for current view + if (eglThread.gpuWorkerContext != null && eglThread.gpuWorkerFakeSurface != null) { + setupOptions.setGpuWorkerThreadEnabled(true); + setupOptions.setGpuWorkerThreadPrologue(_gpuWorkerThreadPrologue.getBinding()); + setupOptions.setGpuWorkerThreadEpilogue(_gpuWorkerThreadEpilogue.getBinding()); + } else { + setupOptions.setGpuWorkerThreadEnabled(false); + setupOptions.setGpuWorkerThreadPrologue(null); + setupOptions.setGpuWorkerThreadEpilogue(null); + } + setupOptions.setFrameUpdateRequestCallback(_renderRequestCallback.getBinding()); + _mapRenderer.setup(setupOptions); + } // Create map animator for that map if (_mapAnimator == null) { @@ -304,37 +318,10 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit } _mapMarkersAnimator.setMapRenderer(_mapRenderer); - // Configure rendering view - _renderingView.setPreserveEGLContextOnPause(true); - _renderingView.setEGLContextClientVersion(3); - _renderingView.setEGLConfigChooser(new ComponentSizeChooser(8, 8, 8, 0, 16, 0)); - _renderingView.setEGLContextFactory(new EGLContextFactory()); - if (!inWindow) { - _renderingView.setEGLWindowSurfaceFactory(new PixelbufferSurfaceFactory(bitmapWidth, bitmapHeight)); - } - _renderingView.setRenderer(new RendererProxy()); - _renderingView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - if (inWindow) { - addView(_renderingView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - } else { - _renderingView.initializeView(bitmapWidth, bitmapHeight); - isSurfaceReady = true; - } - } + initOnResume = isPaused; - public EGLContext takeGPUWorkerContext() { synchronized (_mapRenderer) { - EGLContext gpuWorkerContext = _gpuWorkerContext; - _gpuWorkerContext = null; - return gpuWorkerContext; - } - } - - public EGLSurface takeGPUWorkerSurface() { - synchronized (_mapRenderer) { - EGLSurface gpuWorkerFakeSurface = _gpuWorkerFakeSurface; - _gpuWorkerFakeSurface = null; - return gpuWorkerFakeSurface; + startRenderingView(context); } } @@ -395,11 +382,10 @@ public Bitmap getBitmap() { return null; } - public synchronized IMapRenderer getRenderer() { - Log.v(TAG, "getRenderer()"); + public synchronized IMapRenderer suspendRenderer() { + Log.v(TAG, "suspendRenderer()"); if (_mapRenderer == null || !_mapRenderer.isRenderingInitialized() || _exportableMapRenderer == null) { - Log.v(TAG, "Can't provide absent or not yet initialized renderer"); return null; } @@ -407,8 +393,9 @@ public synchronized IMapRenderer getRenderer() { stopRenderingView(); if (isSuspended) { Log.v(TAG, "Renderer was already suspended"); + return null; } else { - Log.v(TAG, "Suspend renderer to reuse it in other view"); + Log.v(TAG, "Suspending renderer to reuse it in other view"); isSuspended = true; isInitializing = false; isReinitializing = false; @@ -427,7 +414,7 @@ public synchronized void stopRenderer() { Log.v(TAG, "stopRenderer()"); if (_mapRenderer == null) { - Log.v(TAG, "Can't stop absent renderer"); + Log.w(TAG, "Can't stop absent renderer"); return; } @@ -483,6 +470,33 @@ public void run() { } } + public void startRenderingView(Context context) { + Log.v(TAG, "startRenderingView()"); + if (context != null) { + _renderingView = new RenderingView(context); + _renderingView.isOffscreen = !_inWindow; + } + if (_renderingView != null && (context == null || !initOnResume)) { + _renderingView.setGLWrapper(new OpenGLWrapper()); + _renderingView.setPreserveEGLContextOnPause(true); + _renderingView.setEGLContextClientVersion(3); + eglThread.configChooser = + new ComponentSizeChooser(8, 8, 8, 0, 16, 0); + _renderingView.setEGLConfigChooser(eglThread.configChooser); + _renderingView.setEGLContextFactory(new EGLContextFactory()); + _renderingView.setEGLWindowSurfaceFactory( + _inWindow ? new WindowSurfaceFactory() : new PixelbufferSurfaceFactory(_windowWidth, _windowHeight)); + _renderingView.setRenderer(new RendererProxy()); + _renderingView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + if (_inWindow) { + addView(_renderingView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + } else { + _renderingView.initializeView(_windowWidth, _windowHeight); + isSurfaceReady = true; + } + } + } + public void stopRenderingView() { Log.v(TAG, "stopRenderingView()"); if (_renderingView != null) { @@ -559,6 +573,7 @@ public final void handleOnLowMemory() { public final void handleOnPause() { Log.v(TAG, "handleOnPause()"); NativeCore.checkIfLoaded(); + isPaused = true; // Inform rendering view that activity was paused synchronized (_mapRenderer) { @@ -571,11 +586,17 @@ public final void handleOnPause() { public final void handleOnResume() { Log.v(TAG, "handleOnResume()"); NativeCore.checkIfLoaded(); + isPaused = false; - // Inform GLSurfaceView that activity was resumed synchronized (_mapRenderer) { - if (!isSuspended && _renderingView != null) { - _renderingView.onResume(); + if (_renderingView != null) { + if (initOnResume && (isInitializing || isReinitializing)) { + initOnResume = false; + startRenderingView(null); + } else if (!isSuspended) { + // Inform rendering view that activity was resumed + _renderingView.onResume(); + } } } } @@ -1638,32 +1659,14 @@ public static String getEglErrorString(int error) { } } - private class PixelbufferSurfaceFactory implements GLSurfaceView.EGLWindowSurfaceFactory { - - public PixelbufferSurfaceFactory(int width, int height) { - surfaceAttributes = new int[] { - EGL10.EGL_WIDTH, width, - EGL10.EGL_HEIGHT, height, - EGL10.EGL_NONE}; - } - - public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, - EGLConfig config, Object nativeWindow) { - EGLSurface result = null; - try { - result = egl.eglCreatePbufferSurface(display, config, surfaceAttributes); - } catch (IllegalArgumentException e) { - Log.e(TAG, "eglCreateWindowSurface", e); + private class OpenGLWrapper implements GLSurfaceView.GLWrapper { + public GL wrap(GL gl) { + if (eglThread.context != null) { + return eglThread.context.getGL(); + } else { + return gl; } - return result; } - - public void destroySurface(EGL10 egl, EGLDisplay display, - EGLSurface surface) { - egl.eglDestroySurface(display, surface); - } - - private int[] surfaceAttributes; } private abstract class BaseConfigChooser @@ -1672,21 +1675,24 @@ public BaseConfigChooser(int[] configSpec) { mConfigSpec = filterConfigSpec(configSpec); } - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + public EGLConfig makeConfig(EGL10 egl, EGLDisplay display) { int[] num_config = new int[1]; if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config)) { Log.e(TAG, "Failed to choose EGL config"); + return null; } int numConfigs = num_config[0]; if (numConfigs <= 0) { Log.e(TAG, "No EGL configs match specified requirements"); + return null; } EGLConfig[] configs = new EGLConfig[numConfigs]; if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config)) { Log.e(TAG, "Failed to choose suitable EGL configs"); + return null; } EGLConfig config = chooseConfig(egl, display, configs); if (config == null) { @@ -1695,6 +1701,14 @@ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { return config; } + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + Log.v(TAG, "ComponentSizeChooser.chooseConfig()..."); + synchronized (eglThread) { + eglThread.startAndCompleteOperation(egl, EGLThreadOperation.CHOOSE_CONFIG); + } + return makeConfig(egl, display); + } + abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs); @@ -1788,34 +1802,99 @@ private int findConfigAttrib(EGL10 egl, EGLDisplay display, protected int mStencilSize; } + private class WindowSurfaceFactory implements GLSurfaceView.EGLWindowSurfaceFactory { + + public WindowSurfaceFactory() { + dummySurfaceAttributes = new int[] { + EGL10.EGL_WIDTH, 1, + EGL10.EGL_HEIGHT, 1, + EGL10.EGL_NONE + }; + } + + public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow) { + Log.v(TAG, "WindowSurfaceFactory.createWindowSurface()..."); + synchronized (eglThread) { + eglThread.nativeWindow = nativeWindow; + eglThread.startAndCompleteOperation(egl, EGLThreadOperation.CREATE_WINDOW_SURFACE); + } + + // Create dummy surface + EGLSurface result = null; + try { + result = egl.eglCreatePbufferSurface(display, config, dummySurfaceAttributes); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Failed to create dummy window surface", e); + } + return result; + } + + public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { + Log.v(TAG, "WindowSurfaceFactory.destroySurface()..."); + egl.eglDestroySurface(display, surface); + synchronized (eglThread) { + eglThread.startAndCompleteOperation(egl, EGLThreadOperation.DESTROY_SURFACE); + } + } + + private int[] dummySurfaceAttributes; + } + + private class PixelbufferSurfaceFactory implements GLSurfaceView.EGLWindowSurfaceFactory { + + public PixelbufferSurfaceFactory(int width, int height) { + surfaceWidth = width; + surfaceHeight = height; + dummySurfaceAttributes = new int[] { + EGL10.EGL_WIDTH, 1, + EGL10.EGL_HEIGHT, 1, + EGL10.EGL_NONE + }; + } + + public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow) { + Log.v(TAG, "PixelbufferSurfaceFactory.createWindowSurface()..."); + synchronized (eglThread) { + eglThread.surfaceWidth = surfaceWidth; + eglThread.surfaceHeight = surfaceHeight; + eglThread.startAndCompleteOperation(egl, EGLThreadOperation.CREATE_PIXELBUFFER_SURFACE); + } + + // Create dummy surface + EGLSurface result = null; + try { + result = egl.eglCreatePbufferSurface(display, config, dummySurfaceAttributes); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Failed to create dummy window surface", e); + } + return result; + } + + public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { + Log.v(TAG, "PixelbufferSurfaceFactory.destroySurface()..."); + egl.eglDestroySurface(display, surface); + synchronized (eglThread) { + eglThread.startAndCompleteOperation(egl, EGLThreadOperation.DESTROY_SURFACE); + } + } + + private int surfaceWidth; + private int surfaceHeight; + private int[] dummySurfaceAttributes; + } + /** * EGL context factory *

* Implements creation of main and GPU-worker contexts along with needed resources */ private final class EGLContextFactory implements GLSurfaceView.EGLContextFactory { - private int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - /** - * EGL attributes used to initialize EGL context: - * - EGL context must support at least OpenGLES 3.0 - */ - private final int[] contextAttributes = { - EGL_CONTEXT_CLIENT_VERSION, 3, - EGL10.EGL_NONE}; - - /** - * EGL attributes used to initialize GPU-worker EGL surface with 1x1 pixels size - */ - private final int[] gpuWorkerSurfaceAttributes = { - EGL10.EGL_WIDTH, 1, - EGL10.EGL_HEIGHT, 1, - EGL10.EGL_NONE}; - public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { Log.v(TAG, "EGLContextFactory.createContext()..."); if (isSuspended && !isReinitializing) { - Log.v(TAG, "No use to create context for moved renderer"); + Log.e(TAG, "No use to create contexts for moved renderer"); return null; } @@ -1828,76 +1907,32 @@ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConf _mapRenderer.releaseRendering(true); } - // Create GPU-worker EGL context if needed - if (_gpuWorkerContext == null) { - try { - _gpuWorkerContext = egl.eglCreateContext( - display, - eglConfig, - EGL10.EGL_NO_CONTEXT, - contextAttributes); - } catch (Exception e) { - Log.e(TAG, "Failed to create GPU-worker EGL context", e); - } - if (_gpuWorkerContext == null || _gpuWorkerContext == EGL10.EGL_NO_CONTEXT) { - Log.e(TAG, "Failed to create GPU-worker EGL context: " + - getEglErrorString(egl.eglGetError())); - _gpuWorkerContext = null; - } - - // Create GPU-worker EGL surface - if (_gpuWorkerContext != null) { - if (_gpuWorkerFakeSurface != null) { - Log.w(TAG, "Previous GPU-worker EGL surface was not destroyed properly!"); - _gpuWorkerFakeSurface = null; - } - try { - _gpuWorkerFakeSurface = egl.eglCreatePbufferSurface( - display, - eglConfig, - gpuWorkerSurfaceAttributes); - } catch (Exception e) { - Log.e(TAG, "Failed to create GPU-worker EGL surface", e); - _gpuWorkerFakeSurface = null; - } - if (_gpuWorkerFakeSurface == null || _gpuWorkerFakeSurface == EGL10.EGL_NO_SURFACE) { - Log.e(TAG, "Failed to create GPU-worker EGL surface: " + - getEglErrorString(egl.eglGetError())); - - egl.eglDestroyContext(display, _gpuWorkerContext); - _gpuWorkerContext = null; - _gpuWorkerFakeSurface = null; - } - } + if (config == null) { + return null; } - // Create main EGL context - if (_mainContext != null) { - Log.w(TAG, "Previous main EGL context was not destroyed properly!"); - _mainContext = null; + synchronized (eglThread) { + eglThread.startAndCompleteOperation(egl, EGLThreadOperation.CREATE_CONTEXTS); } + + // Create dummy EGL context for GLSurfaceView only + EGLContext dummyContext; try { - _mainContext = egl.eglCreateContext( + dummyContext = egl.eglCreateContext( display, - eglConfig, - _gpuWorkerContext != null ? _gpuWorkerContext : EGL10.EGL_NO_CONTEXT, - contextAttributes); + config, + EGL10.EGL_NO_CONTEXT, + eglThread.contextAttributes); } catch (Exception e) { - Log.e(TAG, "Failed to create main EGL context", e); + Log.e(TAG, "Failed to create dummy EGL context", e); return null; } - if (_mainContext == null || _mainContext == EGL10.EGL_NO_CONTEXT) { - Log.e(TAG, "Failed to create main EGL context: " + getEglErrorString(egl.eglGetError())); - - _mainContext = null; - + if (dummyContext == null || dummyContext == EGL10.EGL_NO_CONTEXT) { + Log.e(TAG, "Failed to create dummy EGL context: " + getEglErrorString(egl.eglGetError())); return null; } - // Save reference to EGL display - _display = display; - - return _mainContext; + return dummyContext; } public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { @@ -1912,24 +1947,14 @@ public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { _mapRenderer.releaseRendering(true); } - // Destroy GPU-worker EGL surface (if present) - if (!isSuspended && _gpuWorkerFakeSurface != null) { - egl.eglDestroySurface(display, _gpuWorkerFakeSurface); - _gpuWorkerFakeSurface = null; - } - - // Destroy GPU-worker EGL context (if present) - if (!isSuspended && _gpuWorkerContext != null) { - egl.eglDestroyContext(display, _gpuWorkerContext); - _gpuWorkerContext = null; - } - - // Destroy main context + // Destroy dummy context egl.eglDestroyContext(display, context); - _mainContext = null; - // Remove reference to EGL display - _display = null; + if (!isSuspended) { + synchronized (eglThread) { + eglThread.startAndCompleteOperation(egl, EGLThreadOperation.DESTROY_CONTEXTS); + } + } } } @@ -1943,18 +1968,15 @@ public void onSurfaceCreated(GL10 gl, EGLConfig config) { Log.v(TAG, "RendererProxy.onSurfaceCreated()..."); if (isSuspended && !isReinitializing) { - Log.v(TAG, "No use to create surface for moved renderer"); return; } - // In case a new surface was created, and rendering was initialized it means that - // surface was changed, so release rendering to allow it to initialize on next + // New context was created, but rendering was initialized before, + // so, release rendering to allow it to initialize on next // call to onSurfaceChanged if (!isReinitializing && _mapRenderer != null && _mapRenderer.isRenderingInitialized()) { - Log.v(TAG, "Releasing rendering due to surface recreation"); - - // Context still exists here and is active, so just release resources - _mapRenderer.releaseRendering(); + Log.v(TAG, "Release rendering due to context recreation"); + _mapRenderer.releaseRendering(true); } } @@ -1962,7 +1984,23 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { Log.v(TAG, "RendererProxy.onSurfaceChanged()..."); if (isSuspended && !isReinitializing) { - Log.v(TAG, "No use to change surface for moved renderer"); + return; + } + + // Get EGL interface + EGL10 egl = (EGL10) EGLContext.getEGL(); + if (egl == null) { + Log.e(TAG, "Failed to obtain EGL interface"); + return; + } + + try { + if (!egl.eglMakeCurrent(eglThread.display, eglThread.surface, eglThread.surface, eglThread.context)) { + Log.e(TAG, "Failed to set main EGL context active: " + getEglErrorString(egl.eglGetError())); + return; + } + } catch (Exception e) { + Log.e(TAG, "Failed to set main EGL context active", e); return; } @@ -1972,14 +2010,12 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { if (_mapRenderer != null) { _mapRenderer.setWindowSize(new PointI(width, height)); _mapRenderer.setViewport(new AreaI(new PointI(0, 0), new PointI(width, height))); - _mapRenderer.setFlip(_flipWindow); + _mapRenderer.setFlip(!_inWindow); } - // In case rendering is not initialized or just needs to be reinitialized, initialize it - // (happens when surface is created for the first time, or recreated) - if (_mapRenderer != null && (isReinitializing || !_mapRenderer.isRenderingInitialized())) { - Log.v(TAG, "Rendering is initializing..."); - if (_gpuWorkerContext != null && _gpuWorkerFakeSurface != null) { + // Setup rendering options + if (_mapRenderer != null && !_mapRenderer.isRenderingInitialized()) { + if (eglThread.gpuWorkerContext != null && eglThread.gpuWorkerFakeSurface != null) { setupOptions.setGpuWorkerThreadEnabled(true); setupOptions.setGpuWorkerThreadPrologue(_gpuWorkerThreadPrologue.getBinding()); setupOptions.setGpuWorkerThreadEpilogue(_gpuWorkerThreadEpilogue.getBinding()); @@ -1990,6 +2026,12 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { } setupOptions.setFrameUpdateRequestCallback(_renderRequestCallback.getBinding()); _mapRenderer.setup(setupOptions); + } + + // In case rendering is not initialized or just needs to be reinitialized, initialize it + // (happens when surface is created for the first time, or recreated) + if (_mapRenderer != null && (isReinitializing || !_mapRenderer.isRenderingInitialized())) { + Log.v(TAG, "Rendering is initializing..."); if (_mapRenderer.initializeRendering(false)) { _exportableMapRenderer = _mapRenderer; if (isReinitializing) { @@ -2009,7 +2051,7 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { } public void onDrawFrame(GL10 gl) { - // In case rendering was not initialized yet, don't do anything + // In case rendering was not ready yet, don't do anything if (isSuspended || _mapRenderer == null || !_mapRenderer.isRenderingInitialized()) { Log.w(TAG, "Can't draw a frame: renderer either suspended or isn't yet initialized"); return; @@ -2054,7 +2096,8 @@ public void onDrawFrame(GL10 gl) { gl.glFinish(); synchronized (_byteBuffer) { _byteBuffer.rewind(); - gl.glReadPixels(0, 0, _windowWidth, _windowHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, _byteBuffer); + gl.glReadPixels( + 0, 0, _windowWidth, _windowHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, _byteBuffer); _renderingResultIsReady = true; } for (MapRendererViewListener listener : listeners) { @@ -2067,7 +2110,8 @@ public void onDrawFrame(GL10 gl) { _frameGPUFinishTime = postRenderTime - preFlushTime; _frameRenderTime = Math.max(postRenderTime - _frameStartTime, 1); _frameRate = 1000.0f / (float) _frameRenderTime; - _frameRateLast1K = _frameRateLast1K > 0.0 ? (_frameRateLast1K * 999.0f + _frameRate) / 1000.0f : _frameRate; + _frameRateLast1K = + _frameRateLast1K > 0.0 ? (_frameRateLast1K * 999.0f + _frameRate) / 1000.0f : _frameRate; if (_batterySavingMode && _maxFrameRate > 0) { long extraTime = _maxFrameTime - _frameRenderTime; @@ -2079,6 +2123,8 @@ public void onDrawFrame(GL10 gl) { } } } + EGL10 egl = (EGL10) EGLContext.getEGL(); + egl.eglSwapBuffers(eglThread.display, eglThread.surface); } } @@ -2103,17 +2149,17 @@ private class GpuWorkerThreadPrologue extends MapRendererSetupOptions.IGpuWorkerThreadPrologue { @Override public void method(IMapRenderer mapRenderer) { - if (_display == null) { + if (eglThread.display == null) { Log.e(TAG, "EGL display is missing"); return; } - if (_gpuWorkerContext == null) { + if (eglThread.gpuWorkerContext == null) { Log.e(TAG, "GPU-worker context is missing"); return; } - if (_gpuWorkerFakeSurface == null) { + if (eglThread.gpuWorkerFakeSurface == null) { Log.e(TAG, "GPU-worker surface is missing"); return; } @@ -2127,10 +2173,10 @@ public void method(IMapRenderer mapRenderer) { try { if (!egl.eglMakeCurrent( - _display, - _gpuWorkerFakeSurface, - _gpuWorkerFakeSurface, - _gpuWorkerContext)) { + eglThread.display, + eglThread.gpuWorkerFakeSurface, + eglThread.gpuWorkerFakeSurface, + eglThread.gpuWorkerContext)) { Log.e(TAG, "Failed to set GPU-worker EGL context active: " + getEglErrorString(egl.eglGetError())); } @@ -2190,4 +2236,226 @@ public void removeView() { } } } + + public class EGLThread extends Thread { + public EGL10 egl; + public EGLDisplay display; + public EGLConfig config; + public EGLContext context; + public EGLSurface surface; + public EGLContext gpuWorkerContext; + public EGLSurface gpuWorkerFakeSurface; + + public ComponentSizeChooser configChooser; + + public Object nativeWindow; + + public int surfaceWidth; + public int surfaceHeight; + + private final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + + /** + * EGL attributes used to initialize EGL context: + * - EGL context must support at least OpenGLES 3.0 + */ + public final int[] contextAttributes = { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL10.EGL_NONE + }; + + /** + * EGL attributes used to initialize GPU-worker EGL surface with 1x1 pixels size + */ + private final int[] gpuWorkerSurfaceAttributes = { + EGL10.EGL_WIDTH, 1, + EGL10.EGL_HEIGHT, 1, + EGL10.EGL_NONE + }; + + public EGLThreadOperation eglThreadOperation = EGLThreadOperation.NO_OPERATION; + + public EGLThread(String name) { + super(name); + } + + // NOTE: Needs to be called from synchronized block + public void startAndCompleteOperation(EGL10 egl_, EGLThreadOperation operation) { + egl = egl_; + eglThreadOperation = operation; + notifyAll(); + while (eglThreadOperation != EGLThreadOperation.NO_OPERATION) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + @Override + public void run() { + while (true) { + synchronized (this) { + switch (eglThreadOperation) { + case CHOOSE_CONFIG: + if (display == null) { + display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + if (display == EGL10.EGL_NO_DISPLAY) { + Log.e(TAG, "Failed to get EGL display"); + display = null; + } + int[] version = new int[2]; + if(!egl.eglInitialize(display, version)) { + Log.e(TAG, "Failed to initialize EGL display connection"); + display = null; + } + } + if (display != null && config == null) { + config = configChooser.makeConfig(egl, display); + } + break; + + case CREATE_CONTEXTS: + // Create GPU-worker EGL context if needed + if (gpuWorkerContext == null) { + try { + gpuWorkerContext = egl.eglCreateContext( + display, + config, + EGL10.EGL_NO_CONTEXT, + contextAttributes); + } catch (Exception e) { + Log.e(TAG, "Failed to create GPU-worker EGL context", e); + } + if (gpuWorkerContext == null || gpuWorkerContext == EGL10.EGL_NO_CONTEXT) { + Log.e(TAG, "Failed to create GPU-worker EGL context: " + + getEglErrorString(egl.eglGetError())); + gpuWorkerContext = null; + } + // Create GPU-worker EGL surface + if (gpuWorkerContext != null) { + if (gpuWorkerFakeSurface != null) { + Log.w(TAG, "Previous GPU-worker EGL surface was not destroyed properly!"); + gpuWorkerFakeSurface = null; + } + try { + gpuWorkerFakeSurface = egl.eglCreatePbufferSurface( + display, + config, + gpuWorkerSurfaceAttributes); + } catch (Exception e) { + Log.e(TAG, "Failed to create GPU-worker EGL surface", e); + gpuWorkerFakeSurface = null; + } + if (gpuWorkerFakeSurface == null || gpuWorkerFakeSurface == EGL10.EGL_NO_SURFACE) { + Log.e(TAG, "Failed to create GPU-worker EGL surface: " + + getEglErrorString(egl.eglGetError())); + + egl.eglDestroyContext(display, gpuWorkerContext); + gpuWorkerContext = null; + gpuWorkerFakeSurface = null; + } + } + } + // Create main EGL context + if (context == null) { + try { + context = egl.eglCreateContext( + display, + config, + gpuWorkerContext != null ? gpuWorkerContext : EGL10.EGL_NO_CONTEXT, + contextAttributes); + } catch (Exception e) { + Log.e(TAG, "Failed to create main EGL context", e); + } + if (context == null || context == EGL10.EGL_NO_CONTEXT) { + Log.e(TAG, "Failed to create main EGL context: " + + getEglErrorString(egl.eglGetError())); + context = null; + } + } + + break; + + case CREATE_WINDOW_SURFACE: + surface = null; + if (display != null && config != null) { + try { + surface = egl.eglCreateWindowSurface(display, config, nativeWindow, null); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Failed to create window surface", e); + } + } + if (surface == null || surface == EGL10.EGL_NO_SURFACE) { + surface = null; + } + break; + + case CREATE_PIXELBUFFER_SURFACE: + surface = null; + if (display != null && config != null) { + int[] surfaceAttributes = new int[] { + EGL10.EGL_WIDTH, surfaceWidth, + EGL10.EGL_HEIGHT, surfaceHeight, + EGL10.EGL_NONE + }; + try { + surface = egl.eglCreatePbufferSurface(display, config, surfaceAttributes); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Failed to create pixelbuffer surface", e); + } + } + if (surface == null || surface == EGL10.EGL_NO_SURFACE) { + surface = null; + } + break; + + case DESTROY_SURFACE: + if (display != null && surface != null) { + egl.eglDestroySurface(display, surface); + surface = null; + } + if (display == null || config == null) { + context = null; + } + break; + + case DESTROY_CONTEXTS: + if (display != null) { + // Destroy main context + if (context != null) { + egl.eglDestroyContext(display, context); + context = null; + } + // Destroy GPU-worker EGL surface (if present) + if (gpuWorkerFakeSurface != null) { + egl.eglDestroySurface(display, gpuWorkerFakeSurface); + gpuWorkerFakeSurface = null; + } + // Destroy GPU-worker EGL context (if present) + if (gpuWorkerContext != null) { + egl.eglDestroyContext(display, gpuWorkerContext); + gpuWorkerContext = null; + } + // Remove EGL config + if (config != null) { + config = null; + } + // Terminate connection to EGL display + egl.eglTerminate(display); + display = null; + } + } + eglThreadOperation = EGLThreadOperation.NO_OPERATION; + notifyAll(); + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + } } From 0488b3b3008f0c3f2d89d9c9374e8f52241ec097 Mon Sep 17 00:00:00 2001 From: Alex Andrewschenko Date: Wed, 27 Nov 2024 14:39:43 +0100 Subject: [PATCH 5/5] Use already initialized renderer if possible --- src/Map/MapRenderer.cpp | 17 +- .../osmand/core/android/MapRendererView.java | 271 ++++++++++-------- 2 files changed, 158 insertions(+), 130 deletions(-) diff --git a/src/Map/MapRenderer.cpp b/src/Map/MapRenderer.cpp index f8c6798a1..852bae775 100644 --- a/src/Map/MapRenderer.cpp +++ b/src/Map/MapRenderer.cpp @@ -312,12 +312,17 @@ bool OsmAnd::MapRenderer::initializeRendering(bool fresh /* = true */) { QMutexLocker scopedLocker(&_initializationLock); - if (_isRenderingInitialized && fresh) - return false; - - if (_isRenderingInitialized && !fresh) { - _renderThreadId = QThread::currentThreadId(); - return true; + if (_isRenderingInitialized) + { + if (fresh) + return false; + else + { + // Don't reinitialize already initialized renderer - remove this block if reinitialized renderer is needed + _renderThreadId = QThread::currentThreadId(); + invalidateFrame(); + return true; + } } bool ok; diff --git a/wrappers/android/src/net/osmand/core/android/MapRendererView.java b/wrappers/android/src/net/osmand/core/android/MapRendererView.java index 324d53626..6f188e43b 100644 --- a/wrappers/android/src/net/osmand/core/android/MapRendererView.java +++ b/wrappers/android/src/net/osmand/core/android/MapRendererView.java @@ -151,6 +151,9 @@ enum EGLThreadOperation { CREATE_CONTEXTS, CREATE_WINDOW_SURFACE, CREATE_PIXELBUFFER_SURFACE, + INITIALIZE_RENDERING, + RENDER_FRAME, + RELEASE_RENDERING, DESTROY_SURFACE, DESTROY_CONTEXTS } @@ -180,15 +183,20 @@ enum EGLThreadOperation { private volatile boolean isReinitializing; /** - * Rendering is suspended but renderer must be kept initialized + * Rendering is suspended, but renderer needs to be kept initialized */ private volatile boolean isSuspended; /** - * Target surface view is present and ready for rendering + * Target surface is present and ready for drawing */ private volatile boolean isSurfaceReady; + /** + * OpenGL view is started and ready for rendering + */ + private volatile boolean isViewStarted; + /** * View is on pause */ @@ -242,9 +250,7 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit _windowWidth = bitmapWidth; _windowHeight = bitmapHeight; - if (!_inWindow) { - _byteBuffer = ByteBuffer.allocateDirect(bitmapWidth * bitmapHeight * 4); - } + _byteBuffer = _inWindow ? null : ByteBuffer.allocateDirect(bitmapWidth * bitmapHeight * 4); // Set display density factor setupOptions.setDisplayDensityFactor(_inWindow ? getResources().getDisplayMetrics().density : 1.0f); @@ -262,7 +268,7 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit isReinitializing = (isSuspended && _mapRenderer != null && _mapRenderer.isRenderingInitialized()); if (!isReinitializing) { Log.v(TAG, "Setting up new renderer to initialize..."); - eglThread = new EGLThread("EGLThread"); + eglThread = new EGLThread("OpenGLThread"); eglThread.start(); _mapRenderer = createMapRendererInstance(); isInitializing = true; @@ -275,8 +281,11 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit Log.v(TAG, "Setting up provided renderer to reinitialize..."); if (_mapRenderer != oldRenderer && isSuspended && _mapRenderer.isRenderingInitialized()) { - Log.v(TAG, "Releasing suspended renderer"); - _mapRenderer.releaseRendering(true); + Log.v(TAG, "Releasing suspended renderer..."); + synchronized (eglThread) { + eglThread.mapRenderer = _mapRenderer; + eglThread.startAndCompleteOperation(EGLThreadOperation.RELEASE_RENDERING); + } } // Use previous frame rate limit for battery saving mode @@ -318,9 +327,8 @@ public synchronized void setupRenderer(Context context, int bitmapWidth, int bit } _mapMarkersAnimator.setMapRenderer(_mapRenderer); - initOnResume = isPaused; - synchronized (_mapRenderer) { + initOnResume = isPaused; startRenderingView(context); } } @@ -421,7 +429,10 @@ public synchronized void stopRenderer() { synchronized (_mapRenderer) { if (isSuspended) { Log.v(TAG, "Stopping suspended renderer..."); - _mapRenderer.releaseRendering(true); + synchronized (eglThread) { + eglThread.mapRenderer = _mapRenderer; + eglThread.startAndCompleteOperation(EGLThreadOperation.RELEASE_RENDERING); + } } else { Log.v(TAG, "Stopping active renderer..."); stopRenderingView(); @@ -437,36 +448,12 @@ public synchronized void stopRenderer() { private void releaseRendering() { Log.v(TAG, "releaseRendering()"); - if (releaseTask == null) { - releaseTask = new Runnable() { - @Override - public void run() { - if (!isSuspended && _mapRenderer != null && _mapRenderer.isRenderingInitialized()) { - Log.v(TAG, "Release rendering..."); - _mapRenderer.releaseRendering(true); - } - synchronized (this) { - waitRelease = false; - this.notifyAll(); - } - } - }; - } - if (!waitRelease && _renderingView != null) { - waitRelease = true; - _renderingView.queueEvent(releaseTask); - synchronized (releaseTask) { - long stopTime = System.currentTimeMillis(); - while (waitRelease){ - if (System.currentTimeMillis() > stopTime + RELEASE_STOP_TIMEOUT) { - waitRelease = false; - return; - } - try { - releaseTask.wait(RELEASE_WAIT_TIMEOUT); - } catch (InterruptedException ex) {} - } - } + if (!isSuspended && _mapRenderer != null && _mapRenderer.isRenderingInitialized()) { + Log.v(TAG, "Release rendering..."); + synchronized (eglThread) { + eglThread.mapRenderer = _mapRenderer; + eglThread.startAndCompleteOperation(EGLThreadOperation.RELEASE_RENDERING); + } } } @@ -477,7 +464,6 @@ public void startRenderingView(Context context) { _renderingView.isOffscreen = !_inWindow; } if (_renderingView != null && (context == null || !initOnResume)) { - _renderingView.setGLWrapper(new OpenGLWrapper()); _renderingView.setPreserveEGLContextOnPause(true); _renderingView.setEGLContextClientVersion(3); eglThread.configChooser = @@ -488,6 +474,7 @@ public void startRenderingView(Context context) { _inWindow ? new WindowSurfaceFactory() : new PixelbufferSurfaceFactory(_windowWidth, _windowHeight)); _renderingView.setRenderer(new RendererProxy()); _renderingView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + isViewStarted = true; if (_inWindow) { addView(_renderingView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); } else { @@ -499,8 +486,13 @@ public void startRenderingView(Context context) { public void stopRenderingView() { Log.v(TAG, "stopRenderingView()"); - if (_renderingView != null) { - _renderingView.onPause(); + if (isViewStarted) { + isViewStarted = false; + if (_renderingView != null) { + _renderingView.onPause(); + } + } else if (initOnResume) { + initOnResume = false; } } @@ -577,7 +569,7 @@ public final void handleOnPause() { // Inform rendering view that activity was paused synchronized (_mapRenderer) { - if (!isSuspended && _renderingView != null) { + if (isViewStarted && !isSuspended && _renderingView != null) { _renderingView.onPause(); } } @@ -1659,16 +1651,6 @@ public static String getEglErrorString(int error) { } } - private class OpenGLWrapper implements GLSurfaceView.GLWrapper { - public GL wrap(GL gl) { - if (eglThread.context != null) { - return eglThread.context.getGL(); - } else { - return gl; - } - } - } - private abstract class BaseConfigChooser implements GLSurfaceView.EGLConfigChooser { public BaseConfigChooser(int[] configSpec) { @@ -1704,7 +1686,7 @@ public EGLConfig makeConfig(EGL10 egl, EGLDisplay display) { public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { Log.v(TAG, "ComponentSizeChooser.chooseConfig()..."); synchronized (eglThread) { - eglThread.startAndCompleteOperation(egl, EGLThreadOperation.CHOOSE_CONFIG); + eglThread.startAndCompleteOperation(EGLThreadOperation.CHOOSE_CONFIG); } return makeConfig(egl, display); } @@ -1816,7 +1798,7 @@ public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig c Log.v(TAG, "WindowSurfaceFactory.createWindowSurface()..."); synchronized (eglThread) { eglThread.nativeWindow = nativeWindow; - eglThread.startAndCompleteOperation(egl, EGLThreadOperation.CREATE_WINDOW_SURFACE); + eglThread.startAndCompleteOperation(EGLThreadOperation.CREATE_WINDOW_SURFACE); } // Create dummy surface @@ -1833,7 +1815,7 @@ public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { Log.v(TAG, "WindowSurfaceFactory.destroySurface()..."); egl.eglDestroySurface(display, surface); synchronized (eglThread) { - eglThread.startAndCompleteOperation(egl, EGLThreadOperation.DESTROY_SURFACE); + eglThread.startAndCompleteOperation(EGLThreadOperation.DESTROY_SURFACE); } } @@ -1857,7 +1839,7 @@ public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig c synchronized (eglThread) { eglThread.surfaceWidth = surfaceWidth; eglThread.surfaceHeight = surfaceHeight; - eglThread.startAndCompleteOperation(egl, EGLThreadOperation.CREATE_PIXELBUFFER_SURFACE); + eglThread.startAndCompleteOperation(EGLThreadOperation.CREATE_PIXELBUFFER_SURFACE); } // Create dummy surface @@ -1874,7 +1856,7 @@ public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { Log.v(TAG, "PixelbufferSurfaceFactory.destroySurface()..."); egl.eglDestroySurface(display, surface); synchronized (eglThread) { - eglThread.startAndCompleteOperation(egl, EGLThreadOperation.DESTROY_SURFACE); + eglThread.startAndCompleteOperation(EGLThreadOperation.DESTROY_SURFACE); } } @@ -1904,7 +1886,7 @@ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) // Since there's no more context, where previous resources were created, // they are lost. Forcibly release rendering - _mapRenderer.releaseRendering(true); + releaseRendering(); } if (config == null) { @@ -1912,7 +1894,7 @@ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) } synchronized (eglThread) { - eglThread.startAndCompleteOperation(egl, EGLThreadOperation.CREATE_CONTEXTS); + eglThread.startAndCompleteOperation(EGLThreadOperation.CREATE_CONTEXTS); } // Create dummy EGL context for GLSurfaceView only @@ -1944,7 +1926,7 @@ public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { // Since there's no more context, where previous resources were created, // they are lost. Forcibly release rendering - _mapRenderer.releaseRendering(true); + releaseRendering(); } // Destroy dummy context @@ -1952,7 +1934,7 @@ public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { if (!isSuspended) { synchronized (eglThread) { - eglThread.startAndCompleteOperation(egl, EGLThreadOperation.DESTROY_CONTEXTS); + eglThread.startAndCompleteOperation(EGLThreadOperation.DESTROY_CONTEXTS); } } } @@ -1976,7 +1958,7 @@ public void onSurfaceCreated(GL10 gl, EGLConfig config) { // call to onSurfaceChanged if (!isReinitializing && _mapRenderer != null && _mapRenderer.isRenderingInitialized()) { Log.v(TAG, "Release rendering due to context recreation"); - _mapRenderer.releaseRendering(true); + releaseRendering(); } } @@ -1987,23 +1969,6 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { return; } - // Get EGL interface - EGL10 egl = (EGL10) EGLContext.getEGL(); - if (egl == null) { - Log.e(TAG, "Failed to obtain EGL interface"); - return; - } - - try { - if (!egl.eglMakeCurrent(eglThread.display, eglThread.surface, eglThread.surface, eglThread.context)) { - Log.e(TAG, "Failed to set main EGL context active: " + getEglErrorString(egl.eglGetError())); - return; - } - } catch (Exception e) { - Log.e(TAG, "Failed to set main EGL context active", e); - return; - } - // Set new "window" size and viewport that covers entire "window" _windowWidth = width; _windowHeight = height; @@ -2032,7 +1997,14 @@ public void onSurfaceChanged(GL10 gl, int width, int height) { // (happens when surface is created for the first time, or recreated) if (_mapRenderer != null && (isReinitializing || !_mapRenderer.isRenderingInitialized())) { Log.v(TAG, "Rendering is initializing..."); - if (_mapRenderer.initializeRendering(false)) { + boolean ok = false; + synchronized (eglThread) { + eglThread.ok = false; + eglThread.mapRenderer = _mapRenderer; + eglThread.startAndCompleteOperation(EGLThreadOperation.INITIALIZE_RENDERING); + ok = eglThread.ok; + } + if (ok) { _exportableMapRenderer = _mapRenderer; if (isReinitializing) { isReinitializing = false; @@ -2077,29 +2049,21 @@ public void onDrawFrame(GL10 gl) { listener.onUpdateFrame(MapRendererView.this); } - // Allow renderer to update - _mapRenderer.update(); - - // In case a new frame was prepared, render it - if (_mapRenderer.prepareFrame()) { - frameId++; - _mapRenderer.renderFrame(); + long preFlushTime; + synchronized (eglThread) { + eglThread.mapRenderer = _mapRenderer; + eglThread.byteBuffer = _byteBuffer; + eglThread.renderingResultIsReady = false; + eglThread.startAndCompleteOperation(EGLThreadOperation.RENDER_FRAME); + if (eglThread.renderingResultIsReady) { + _renderingResultIsReady = true; + } + preFlushTime = eglThread.preFlushTime; } - long preFlushTime = SystemClock.uptimeMillis(); + frameId++; - // Flush all the commands to GPU - gl.glFlush(); - - // Read the result raster to byte buffer when rendering off-screen if (_byteBuffer != null) { - gl.glFinish(); - synchronized (_byteBuffer) { - _byteBuffer.rewind(); - gl.glReadPixels( - 0, 0, _windowWidth, _windowHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, _byteBuffer); - _renderingResultIsReady = true; - } for (MapRendererViewListener listener : listeners) { listener.onFrameReady(MapRendererView.this); } @@ -2123,8 +2087,6 @@ public void onDrawFrame(GL10 gl) { } } } - EGL10 egl = (EGL10) EGLContext.getEGL(); - egl.eglSwapBuffers(eglThread.display, eglThread.surface); } } @@ -2237,21 +2199,29 @@ public void removeView() { } } - public class EGLThread extends Thread { - public EGL10 egl; - public EGLDisplay display; - public EGLConfig config; - public EGLContext context; - public EGLSurface surface; - public EGLContext gpuWorkerContext; - public EGLSurface gpuWorkerFakeSurface; + private class EGLThread extends Thread { + + private EGL10 egl; + private EGLDisplay display; + private EGLConfig config; + private EGLContext context; + private EGLSurface surface; + GL10 gl; + + protected volatile EGLContext gpuWorkerContext; + protected volatile EGLSurface gpuWorkerFakeSurface; + + protected volatile ComponentSizeChooser configChooser; + + protected volatile Object nativeWindow; - public ComponentSizeChooser configChooser; + protected volatile int surfaceWidth; + protected volatile int surfaceHeight; - public Object nativeWindow; + protected volatile IMapRenderer mapRenderer; - public int surfaceWidth; - public int surfaceHeight; + protected volatile ByteBuffer byteBuffer; + protected volatile boolean renderingResultIsReady; private final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; @@ -2259,7 +2229,7 @@ public class EGLThread extends Thread { * EGL attributes used to initialize EGL context: * - EGL context must support at least OpenGLES 3.0 */ - public final int[] contextAttributes = { + protected final int[] contextAttributes = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; @@ -2273,15 +2243,16 @@ public class EGLThread extends Thread { EGL10.EGL_NONE }; - public EGLThreadOperation eglThreadOperation = EGLThreadOperation.NO_OPERATION; + public volatile EGLThreadOperation eglThreadOperation = EGLThreadOperation.NO_OPERATION; + public volatile boolean ok; + public volatile long preFlushTime = SystemClock.uptimeMillis(); public EGLThread(String name) { super(name); } - // NOTE: Needs to be called from synchronized block - public void startAndCompleteOperation(EGL10 egl_, EGLThreadOperation operation) { - egl = egl_; + // NOTE: It needs to be called from synchronized block + public void startAndCompleteOperation(EGLThreadOperation operation) { eglThreadOperation = operation; notifyAll(); while (eglThreadOperation != EGLThreadOperation.NO_OPERATION) { @@ -2295,6 +2266,7 @@ public void startAndCompleteOperation(EGL10 egl_, EGLThreadOperation operation) @Override public void run() { + egl = (EGL10) EGLContext.getEGL(); while (true) { synchronized (this) { switch (eglThreadOperation) { @@ -2389,6 +2361,16 @@ public void run() { } if (surface == null || surface == EGL10.EGL_NO_SURFACE) { surface = null; + } else { + try { + if (!egl.eglMakeCurrent(display, surface, surface, context)) { + Log.e(TAG, "Failed to set main EGL context to window active: " + + getEglErrorString(egl.eglGetError())); + } + } catch (Exception e) { + Log.e(TAG, "Failed to set main EGL context to window active", e); + } + gl = (GL10) context.getGL(); } break; @@ -2408,17 +2390,58 @@ public void run() { } if (surface == null || surface == EGL10.EGL_NO_SURFACE) { surface = null; + } else { + try { + if (!egl.eglMakeCurrent(display, surface, surface, context)) { + Log.e(TAG, "Failed to set main EGL context to pixelbuffer active: " + + getEglErrorString(egl.eglGetError())); + } + } catch (Exception e) { + Log.e(TAG, "Failed to set main EGL context to pixelbuffer active", e); + } + gl = (GL10) context.getGL(); } break; + case INITIALIZE_RENDERING: + ok = mapRenderer.initializeRendering(false); + break; + + case RENDER_FRAME: + mapRenderer.update(); + boolean isReady = mapRenderer.prepareFrame(); + if (isReady) { + mapRenderer.renderFrame(); + } + preFlushTime = SystemClock.uptimeMillis(); + if (isReady) { + gl.glFlush(); + if (byteBuffer != null) { + gl.glFinish(); + synchronized (byteBuffer) { + byteBuffer.rewind(); + gl.glReadPixels(0, 0, surfaceWidth, surfaceHeight, + GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, byteBuffer); + renderingResultIsReady = true; + } + } else { + egl.eglSwapBuffers(display, surface); + } + } + break; + + case RELEASE_RENDERING: + mapRenderer.releaseRendering(true); + break; + case DESTROY_SURFACE: if (display != null && surface != null) { + egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_CONTEXT); egl.eglDestroySurface(display, surface); surface = null; } - if (display == null || config == null) { - context = null; - } break; case DESTROY_CONTEXTS: