diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c9b6ce04..c11962a22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,8 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/uniform_buffer.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/vertex_attribute.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_target.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/retained_underlay_cache.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/retained_underlay_cache.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/change_request.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layer_group.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_layer_group.cpp diff --git a/cmake/metal.cmake b/cmake/metal.cmake index 3ce93728a..31813d536 100644 --- a/cmake/metal.cmake +++ b/cmake/metal.cmake @@ -48,6 +48,7 @@ list(APPEND ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/line.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/location_indicator.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/particle_gpu_validation.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/retained_underlay_composite.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/raster.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/shader_group.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/shader_program.hpp @@ -92,6 +93,7 @@ list(APPEND ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/line.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/location_indicator.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/particle_gpu_validation.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/retained_underlay_composite.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/raster.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/symbol.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/widevector.cpp diff --git a/cmake/opengl.cmake b/cmake/opengl.cmake index f2990c439..66601e48c 100644 --- a/cmake/opengl.cmake +++ b/cmake/opengl.cmake @@ -37,6 +37,7 @@ list(APPEND ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/fill_extrusion_pattern_instanced.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/heatmap.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/heatmap_texture.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/retained_underlay_composite.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/hillshade_prepare.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/hillshade.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/color_relief.hpp diff --git a/include/mbgl/gfx/context.hpp b/include/mbgl/gfx/context.hpp index 01816e0ec..a9648738f 100644 --- a/include/mbgl/gfx/context.hpp +++ b/include/mbgl/gfx/context.hpp @@ -82,6 +82,10 @@ public: virtual void reduceMemoryUsage() = 0; virtual std::unique_ptr createOffscreenTexture(Size, TextureChannelDataType) = 0; + virtual std::unique_ptr createOffscreenTexture(Size, + TextureChannelDataType, + bool, + bool) = 0; template Renderbuffer createRenderbuffer(const Size size) { diff --git a/include/mbgl/mtl/context.hpp b/include/mbgl/mtl/context.hpp index 3dd2ab322..3785f14ee 100644 --- a/include/mbgl/mtl/context.hpp +++ b/include/mbgl/mtl/context.hpp @@ -106,7 +106,10 @@ public: void setDirtyState() override; - std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool); + std::unique_ptr createOffscreenTexture(Size, + gfx::TextureChannelDataType, + bool, + bool) override; std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType) override; diff --git a/include/mbgl/particle/renderer_debug_counters.hpp b/include/mbgl/particle/renderer_debug_counters.hpp index a22d508f8..278c637f2 100644 --- a/include/mbgl/particle/renderer_debug_counters.hpp +++ b/include/mbgl/particle/renderer_debug_counters.hpp @@ -22,6 +22,13 @@ enum class RendererDebugCounter : uint8_t { TrailTargetCreateOrResize, V4ProjectionMaterialize, FieldWindowRevision, + FullRendererFrame, + RetainedUnderlayBuild, + RetainedUnderlayReuse, + RetainedUnderlayFallback, + RetainedUnderlayInvalidation, + RetainedUnderlayStaticGroupDraw, + RetainedDynamicGroupDraw, Count, }; @@ -40,6 +47,13 @@ struct RendererDebugCounterSnapshot { uint64_t trailTargetCreateOrResize = 0; uint64_t v4ProjectionMaterialize = 0; uint64_t fieldWindowRevision = 0; + uint64_t fullRendererFrame = 0; + uint64_t retainedUnderlayBuild = 0; + uint64_t retainedUnderlayReuse = 0; + uint64_t retainedUnderlayFallback = 0; + uint64_t retainedUnderlayInvalidation = 0; + uint64_t retainedUnderlayStaticGroupDraw = 0; + uint64_t retainedDynamicGroupDraw = 0; }; inline std::atomic_uint64_t* rendererDebugCounterStorage() { @@ -59,7 +73,10 @@ inline RendererDebugCounterSnapshot rendererDebugCounterSnapshot() { values[6].load(std::memory_order_relaxed), values[7].load(std::memory_order_relaxed), values[8].load(std::memory_order_relaxed), values[9].load(std::memory_order_relaxed), values[10].load(std::memory_order_relaxed), values[11].load(std::memory_order_relaxed), - values[12].load(std::memory_order_relaxed)}; + values[12].load(std::memory_order_relaxed), values[13].load(std::memory_order_relaxed), + values[14].load(std::memory_order_relaxed), values[15].load(std::memory_order_relaxed), + values[16].load(std::memory_order_relaxed), values[17].load(std::memory_order_relaxed), + values[18].load(std::memory_order_relaxed), values[19].load(std::memory_order_relaxed)}; } #else inline void incrementRendererDebugCounter(RendererDebugCounter) {} diff --git a/include/mbgl/shaders/gl/retained_underlay_composite.hpp b/include/mbgl/shaders/gl/retained_underlay_composite.hpp new file mode 100644 index 000000000..5eaf7cce6 --- /dev/null +++ b/include/mbgl/shaders/gl/retained_underlay_composite.hpp @@ -0,0 +1,29 @@ +// Generated code, do not modify this file! +#pragma once +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr const char* name = "RetainedUnderlayCompositeShader"; + static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos; +out vec2 v_uv; + +void main() { + gl_Position = vec4(a_pos.x * 2.0 - 1.0, 1.0 - a_pos.y * 2.0, 0.0, 1.0); + v_uv = vec2(a_pos.x, 1.0 - a_pos.y); +} +)"; + static constexpr const char* fragment = R"(in vec2 v_uv; +uniform sampler2D u_image; + +void main() { + fragColor = texture(u_image, v_uv); +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/gl/shader_info.hpp b/include/mbgl/shaders/gl/shader_info.hpp index 20fa76e47..ecd9f1de8 100644 --- a/include/mbgl/shaders/gl/shader_info.hpp +++ b/include/mbgl/shaders/gl/shader_info.hpp @@ -156,6 +156,13 @@ struct ShaderInfo textures; }; +template <> +struct ShaderInfo { + static const std::vector attributes; + static const std::vector uniformBlocks; + static const std::vector textures; +}; + template <> struct ShaderInfo { static const std::vector attributes; diff --git a/include/mbgl/shaders/mtl/retained_underlay_composite.hpp b/include/mbgl/shaders/mtl/retained_underlay_composite.hpp new file mode 100644 index 000000000..86508045f --- /dev/null +++ b/include/mbgl/shaders/mtl/retained_underlay_composite.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace mbgl::shaders { + +template <> +struct ShaderSource { + static constexpr auto name = "RetainedUnderlayCompositeShader"; + static constexpr auto vertexMainFunction = "vertexMain"; + static constexpr auto fragmentMainFunction = "fragmentMain"; + static const std::array attributes; + static constexpr std::array instanceAttributes{}; + static const std::array textures; + static constexpr auto prelude = R"()"; + static constexpr auto source = R"( +struct VertexStage { short2 pos [[attribute(0)]]; }; +struct FragmentStage { float4 position [[position, invariant]]; float2 uv; }; + +FragmentStage vertex vertexMain(thread const VertexStage in [[stage_in]]) { + const float2 p = float2(in.pos); + return { .position = float4(p.x * 2.0 - 1.0, 1.0 - p.y * 2.0, 0.0, 1.0), .uv = p }; +} + +half4 fragment fragmentMain(FragmentStage in [[stage_in]], + texture2d image [[texture(0)]], + sampler imageSampler [[sampler(0)]]) { + return half4(image.sample(imageSampler, in.uv)); +} +)"; +}; + +} // namespace mbgl::shaders diff --git a/include/mbgl/shaders/shader_defines.hpp b/include/mbgl/shaders/shader_defines.hpp index 85a40fee0..ba335991c 100644 --- a/include/mbgl/shaders/shader_defines.hpp +++ b/include/mbgl/shaders/shader_defines.hpp @@ -347,6 +347,11 @@ enum { particleGpuValidationTextureCount }; +enum { + idRetainedUnderlayCompositeTexture, + retainedUnderlayCompositeTextureCount +}; + enum { idHillshadeImageTexture, hillshadeTextureCount @@ -392,6 +397,7 @@ static constexpr uint32_t maxTextureCountPerShader = std::max({static_cast(fillExtrusionTextureCount), static_cast(heatmapTextureCount), static_cast(particleGpuValidationTextureCount), + static_cast(retainedUnderlayCompositeTextureCount), static_cast(hillshadeTextureCount), static_cast(colorReliefTextureCount), static_cast(lineTextureCount), @@ -410,6 +416,11 @@ enum { particleGpuValidationVertexAttributeCount }; +enum { + idRetainedUnderlayCompositePosVertexAttribute, + retainedUnderlayCompositeVertexAttributeCount +}; + enum { idCirclePosVertexAttribute, diff --git a/include/mbgl/shaders/shader_manifest.hpp b/include/mbgl/shaders/shader_manifest.hpp index a60fadad9..0ab211d49 100644 --- a/include/mbgl/shaders/shader_manifest.hpp +++ b/include/mbgl/shaders/shader_manifest.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/include/mbgl/shaders/shader_source.hpp b/include/mbgl/shaders/shader_source.hpp index 12979aa4f..4a3006ef9 100644 --- a/include/mbgl/shaders/shader_source.hpp +++ b/include/mbgl/shaders/shader_source.hpp @@ -31,6 +31,7 @@ enum class BuiltIn { HeatmapShader, HeatmapTextureShader, ParticleGpuValidationShader, + RetainedUnderlayCompositeShader, HillshadePrepareShader, ColorReliefShader, HillshadeShader, diff --git a/include/mbgl/vulkan/context.hpp b/include/mbgl/vulkan/context.hpp index 357caaf99..a5e0bc86f 100644 --- a/include/mbgl/vulkan/context.hpp +++ b/include/mbgl/vulkan/context.hpp @@ -106,7 +106,10 @@ public: void setDirtyState() override {} - std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool); + std::unique_ptr createOffscreenTexture(Size, + gfx::TextureChannelDataType, + bool, + bool) override; std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType) override; diff --git a/include/mbgl/webgpu/context.hpp b/include/mbgl/webgpu/context.hpp index d381cfb32..6f4388ec0 100644 --- a/include/mbgl/webgpu/context.hpp +++ b/include/mbgl/webgpu/context.hpp @@ -43,7 +43,10 @@ public: void reduceMemoryUsage() override; // Resource creation - std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool); + std::unique_ptr createOffscreenTexture(Size, + gfx::TextureChannelDataType, + bool, + bool) override; std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType) override; std::unique_ptr createCommandEncoder() override; gfx::VertexAttributeArrayPtr createVertexAttributeArray() const override; diff --git a/shaders/manifest.json b/shaders/manifest.json index 45cd484d0..d1d03e728 100644 --- a/shaders/manifest.json +++ b/shaders/manifest.json @@ -151,6 +151,13 @@ "glsl_frag": "particle_gpu_validation.fragment.glsl", "uses_ubos": true }, + { + "name": "RetainedUnderlayCompositeShader", + "header": "retained_underlay_composite", + "glsl_vert": "retained_underlay_composite.vertex.glsl", + "glsl_frag": "retained_underlay_composite.fragment.glsl", + "uses_ubos": true + }, { "name": "HillshadePrepareShader", "header": "hillshade_prepare", diff --git a/shaders/retained_underlay_composite.fragment.glsl b/shaders/retained_underlay_composite.fragment.glsl new file mode 100644 index 000000000..26f045b7c --- /dev/null +++ b/shaders/retained_underlay_composite.fragment.glsl @@ -0,0 +1,6 @@ +in vec2 v_uv; +uniform sampler2D u_image; + +void main() { + fragColor = texture(u_image, v_uv); +} diff --git a/shaders/retained_underlay_composite.vertex.glsl b/shaders/retained_underlay_composite.vertex.glsl new file mode 100644 index 000000000..6c5eeb24a --- /dev/null +++ b/shaders/retained_underlay_composite.vertex.glsl @@ -0,0 +1,7 @@ +layout (location = 0) in vec2 a_pos; +out vec2 v_uv; + +void main() { + gl_Position = vec4(a_pos.x * 2.0 - 1.0, 1.0 - a_pos.y * 2.0, 0.0, 1.0); + v_uv = vec2(a_pos.x, 1.0 - a_pos.y); +} diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 5e7749013..ee87a0bc6 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -436,7 +436,15 @@ std::unique_ptr Context::createOffscreenTexture(const Siz const gfx::TextureChannelDataType type) { MLN_TRACE_FUNC(); - return std::make_unique(*this, size, type); + return std::make_unique(*this, size, type, false, false); +} + +std::unique_ptr Context::createOffscreenTexture(const Size size, + const gfx::TextureChannelDataType type, + const bool depth, + const bool stencil) { + MLN_TRACE_FUNC(); + return std::make_unique(*this, size, type, depth, stencil); } std::unique_ptr Context::createDrawScopeResource() { @@ -611,6 +619,24 @@ Framebuffer Context::createFramebuffer(const gfx::Texture2D& color) { return {color.getSize(), std::move(fbo)}; } +Framebuffer Context::createFramebuffer( + const gfx::Texture2D& color, + const gfx::Renderbuffer& depthStencil) { + if (color.getSize() != depthStencil.getSize()) { + throw std::runtime_error("Renderbuffer size mismatch"); + } + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + static_cast(color).getTextureID(), + 0)); + bindDepthStencilRenderbuffer(depthStencil); + checkFramebuffer(); + return {color.getSize(), std::move(fbo)}; +} + gfx::VertexAttributeArrayPtr Context::createVertexAttributeArray() const { return std::make_shared(); } diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 0d654c4ba..d792f3f16 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -134,6 +134,8 @@ public: RenderTargetPtr createRenderTarget(const Size size, const gfx::TextureChannelDataType type) override; Framebuffer createFramebuffer(const gfx::Texture2D& color); + Framebuffer createFramebuffer(const gfx::Texture2D& color, + const gfx::Renderbuffer&); gfx::VertexAttributeArrayPtr createVertexAttributeArray() const override; @@ -212,6 +214,10 @@ private: public: std::unique_ptr createOffscreenTexture(Size, gfx::TextureChannelDataType) override; + std::unique_ptr createOffscreenTexture(Size, + gfx::TextureChannelDataType, + bool, + bool) override; private: std::unique_ptr createRenderbufferResource(gfx::RenderbufferPixelType, diff --git a/src/mbgl/gl/offscreen_texture.cpp b/src/mbgl/gl/offscreen_texture.cpp index 90f71b414..9fa3e283b 100644 --- a/src/mbgl/gl/offscreen_texture.cpp +++ b/src/mbgl/gl/offscreen_texture.cpp @@ -8,7 +8,11 @@ namespace gl { class OffscreenTextureResource final : public gl::RenderableResource { public: - OffscreenTextureResource(gl::Context& context_, const Size size_, const gfx::TextureChannelDataType type_) + OffscreenTextureResource(gl::Context& context_, + const Size size_, + const gfx::TextureChannelDataType type_, + bool depth, + bool stencil) : context(context_), size(size_), type(type_) { @@ -19,6 +23,10 @@ public: texture->setSamplerConfiguration({.filter = gfx::TextureFilterType::Linear, .wrapU = gfx::TextureWrapType::Clamp, .wrapV = gfx::TextureWrapType::Clamp}); + if (depth || stencil) { + depthStencil = std::make_unique>( + context.createRenderbuffer(size)); + } } ~OffscreenTextureResource() noexcept override = default; @@ -27,7 +35,8 @@ public: if (!framebuffer) { assert(texture); texture->create(); - framebuffer = context.createFramebuffer(*texture); + framebuffer = depthStencil ? context.createFramebuffer(*texture, *depthStencil) + : context.createFramebuffer(*texture); } else { context.bindFramebuffer = framebuffer->framebuffer; } @@ -54,10 +63,16 @@ private: gfx::Texture2DPtr texture; const gfx::TextureChannelDataType type; std::optional framebuffer; + std::unique_ptr> depthStencil; }; -OffscreenTexture::OffscreenTexture(gl::Context& context, const Size size_, const gfx::TextureChannelDataType type) - : gfx::OffscreenTexture(size, std::make_unique(context, size_, type)) {} +OffscreenTexture::OffscreenTexture(gl::Context& context, + const Size size_, + const gfx::TextureChannelDataType type, + bool depth, + bool stencil) + : gfx::OffscreenTexture( + size_, std::make_unique(context, size_, type, depth, stencil)) {} bool OffscreenTexture::isRenderable() { try { diff --git a/src/mbgl/gl/offscreen_texture.hpp b/src/mbgl/gl/offscreen_texture.hpp index 13cec4c11..eba5fb94d 100644 --- a/src/mbgl/gl/offscreen_texture.hpp +++ b/src/mbgl/gl/offscreen_texture.hpp @@ -12,7 +12,9 @@ class OffscreenTexture final : public gfx::OffscreenTexture { public: OffscreenTexture(gl::Context&, Size size, - gfx::TextureChannelDataType type = gfx::TextureChannelDataType::UnsignedByte); + gfx::TextureChannelDataType type = gfx::TextureChannelDataType::UnsignedByte, + bool depth = false, + bool stencil = false); bool isRenderable() override; diff --git a/src/mbgl/gl/renderer_backend.cpp b/src/mbgl/gl/renderer_backend.cpp index e705e6846..c5554bdfc 100644 --- a/src/mbgl/gl/renderer_backend.cpp +++ b/src/mbgl/gl/renderer_backend.cpp @@ -136,6 +136,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::HeatmapShader, shaders::BuiltIn::HeatmapTextureShader, shaders::BuiltIn::ParticleGpuValidationShader, + shaders::BuiltIn::RetainedUnderlayCompositeShader, shaders::BuiltIn::HillshadePrepareShader, shaders::BuiltIn::HillshadeShader, shaders::BuiltIn::ColorReliefShader, diff --git a/src/mbgl/mtl/renderer_backend.cpp b/src/mbgl/mtl/renderer_backend.cpp index 65bf4106b..603424c88 100644 --- a/src/mbgl/mtl/renderer_backend.cpp +++ b/src/mbgl/mtl/renderer_backend.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::LocationIndicatorShader, shaders::BuiltIn::LocationIndicatorTexturedShader, shaders::BuiltIn::ParticleGpuValidationShader, + shaders::BuiltIn::RetainedUnderlayCompositeShader, shaders::BuiltIn::RasterShader, shaders::BuiltIn::SymbolIconShader, shaders::BuiltIn::SymbolSDFShader, diff --git a/src/mbgl/renderer/layers/render_location_indicator_layer.hpp b/src/mbgl/renderer/layers/render_location_indicator_layer.hpp index 35eab9591..0419dc20c 100644 --- a/src/mbgl/renderer/layers/render_location_indicator_layer.hpp +++ b/src/mbgl/renderer/layers/render_location_indicator_layer.hpp @@ -44,6 +44,7 @@ private: #ifndef MLN_DRAWABLE_LOCATION_INDICATOR void render(PaintParameters &) override; + bool usesLegacyRendering() const override { return true; } #endif void populateDynamicRenderFeatureIndex(DynamicFeatureIndex &) const override; diff --git a/src/mbgl/renderer/layers/render_raster_particle_layer.hpp b/src/mbgl/renderer/layers/render_raster_particle_layer.hpp index 0fae7a429..6452a737d 100644 --- a/src/mbgl/renderer/layers/render_raster_particle_layer.hpp +++ b/src/mbgl/renderer/layers/render_raster_particle_layer.hpp @@ -27,6 +27,7 @@ private: bool hasTransition() const override { return false; } bool hasCrossfade() const override { return false; } bool needsRepaint() const override; + bool startsRetainedDynamicRange() const override { return true; } void prepare(const LayerPrepareParameters&) override; void markLayerRenderable(bool, UniqueChangeRequestVec&) override; void layerRemoved(UniqueChangeRequestVec&) override; diff --git a/src/mbgl/renderer/layers/render_weather_color_layer.hpp b/src/mbgl/renderer/layers/render_weather_color_layer.hpp index 7b62c6528..3f90151fa 100644 --- a/src/mbgl/renderer/layers/render_weather_color_layer.hpp +++ b/src/mbgl/renderer/layers/render_weather_color_layer.hpp @@ -23,6 +23,7 @@ private: bool hasTransition() const override { return false; } bool hasCrossfade() const override { return false; } bool needsRepaint() const override; + bool startsRetainedDynamicRange() const override { return true; } void prepare(const LayerPrepareParameters&) override; void markLayerRenderable(bool, UniqueChangeRequestVec&) override; void layerRemoved(UniqueChangeRequestVec&) override; diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index cd27548a0..1087206b2 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -109,6 +109,14 @@ public: // Returns true when an active animated layer needs another MapLibre frame. virtual bool needsRepaint() const { return false; } + // Renderer-internal boundary for retained static-underlay rendering. This is + // deliberately not exposed through the style or Host APIs. + virtual bool startsRetainedDynamicRange() const { return false; } + + // Only layers that still issue commands through RenderLayer::render() need + // legacy ordering protection in the retained-underlay path. + virtual bool usesLegacyRendering() const { return false; } + // Exceptional source-driven cleanup for a hidden layer. The default keeps // normal MapLibre hidden-layer lifecycle unchanged. virtual bool needsHiddenLayerSourceCleanup(const RenderSource&) const { return false; } diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp index fc93ddbd4..dcd7fabb5 100644 --- a/src/mbgl/renderer/render_orchestrator.cpp +++ b/src/mbgl/renderer/render_orchestrator.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -364,6 +365,13 @@ std::unique_ptr RenderOrchestrator::createRenderTree( // Track which layers are flagged for rendering std::vector updateList(orderedLayers.size()); + RetainedDynamicBoundaryTracker boundaryTracker; + for (std::size_t index = 0; index < orderedLayers.size(); ++index) { + boundaryTracker.consider(static_cast(index), + orderedLayers[index].get().startsRetainedDynamicRange()); + } + retainedDynamicBoundary = boundaryTracker.value(); + // Update all sources and initialize renderItems. for (const auto& sourceImpl : *sourceImpls) { MLN_TRACE_ZONE(update source); @@ -848,6 +856,22 @@ bool RenderOrchestrator::hasTransitions(TimePoint timePoint) const { return false; } +bool RenderOrchestrator::retainedUnderlayIsStable(TimePoint timePoint, int32_t boundary) const { + if (renderLight.hasTransition() || placementController.hasTransitions(timePoint)) return false; + + const auto staticLayerCount = std::min( + boundary > 0 ? static_cast(boundary) : 0, orderedLayers.size()); + for (std::size_t index = 0; index < staticLayerCount; ++index) { + const auto& layer = orderedLayers[index].get(); + if (layer.hasTransition() || layer.needsRepaint()) return false; + } + + for (const auto& entry : renderSources) { + if (entry.second->hasFadingTiles()) return false; + } + return true; +} + bool RenderOrchestrator::isLoaded() const { MLN_TRACE_FUNC(); diff --git a/src/mbgl/renderer/render_orchestrator.hpp b/src/mbgl/renderer/render_orchestrator.hpp index c439204cf..97f7baef3 100644 --- a/src/mbgl/renderer/render_orchestrator.hpp +++ b/src/mbgl/renderer/render_orchestrator.hpp @@ -15,9 +15,11 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -106,6 +108,8 @@ public: bool addLayerGroup(LayerGroupBasePtr); bool removeLayerGroup(const LayerGroupBasePtr&); size_t numLayerGroups() const noexcept; + std::optional getRetainedDynamicBoundary() const noexcept { return retainedDynamicBoundary; } + bool retainedUnderlayIsStable(TimePoint, int32_t boundary) const; void updateLayerIndex(LayerGroupBasePtr, int32_t newIndex); template @@ -121,6 +125,72 @@ public: } } + template + void visitLayerGroupsBefore(int32_t boundary, Func f) { + size_t ordinal = 0; + for (auto& pair : layerGroupsByLayerIndex) { + if (!retainedLayerGroupInRange(pair.first, boundary, RetainedLayerGroupRange::Before)) break; + if (pair.second) { + try { + f(*pair.second, ordinal); + } catch (...) { + observer->onRenderError(std::current_exception()); + } + } + ++ordinal; + } + } + + template + void visitLayerGroupsFrom(int32_t boundary, Func f) { + size_t ordinal = static_cast(std::distance(layerGroupsByLayerIndex.begin(), + layerGroupsByLayerIndex.lower_bound(boundary))); + for (auto it = layerGroupsByLayerIndex.lower_bound(boundary); it != layerGroupsByLayerIndex.end(); ++it) { + if (!retainedLayerGroupInRange(it->first, boundary, RetainedLayerGroupRange::From)) continue; + if (it->second) { + try { + f(*it->second, ordinal); + } catch (...) { + observer->onRenderError(std::current_exception()); + } + } + ++ordinal; + } + } + + template + void visitLayerGroupsBeforeReversed(int32_t boundary, Func f) { + auto end = layerGroupsByLayerIndex.lower_bound(boundary); + size_t ordinal = static_cast(std::distance(layerGroupsByLayerIndex.begin(), end)); + while (end != layerGroupsByLayerIndex.begin()) { + --end; + --ordinal; + if (end->second) { + try { + f(*end->second, ordinal); + } catch (...) { + observer->onRenderError(std::current_exception()); + } + } + } + } + + template + void visitLayerGroupsFromReversed(int32_t boundary, Func f) { + auto begin = layerGroupsByLayerIndex.lower_bound(boundary); + size_t ordinal = layerGroupsByLayerIndex.size(); + for (auto it = layerGroupsByLayerIndex.rbegin(); it.base() != begin; ++it) { + --ordinal; + if (it->second) { + try { + f(*it->second, ordinal); + } catch (...) { + observer->onRenderError(std::current_exception()); + } + } + } + } + template void visitLayerGroupsReversed(Func f) { for (auto rit = layerGroupsByLayerIndex.rbegin(); rit != layerGroupsByLayerIndex.rend(); ++rit) { @@ -241,6 +311,7 @@ private: using LayerGroupMap = std::multimap; LayerGroupMap layerGroupsByLayerIndex; + std::optional retainedDynamicBoundary; std::vector renderTargets; RenderItem::DebugLayerGroupMap debugLayerGroups; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 500773570..b37134893 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #if MLN_RENDER_BACKEND_METAL #include @@ -57,7 +59,8 @@ Renderer::Impl::Impl(gfx::RendererBackend& backend_, : orchestrator(!backend_.contextIsShared(), backend_.getThreadPool(), localFontFamily_), backend(backend_), observer(&nullObserver()), - pixelRatio(pixelRatio_) {} + pixelRatio(pixelRatio_), + retainedUnderlayCache(std::make_unique()) {} Renderer::Impl::~Impl() { assert(gfx::BackendScope::exists()); @@ -91,6 +94,7 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) { void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr& updateParameters) { MLN_TRACE_FUNC(); + particle::incrementRendererDebugCounter(particle::RendererDebugCounter::FullRendererFrame); auto& context = backend.getContext(); context.setObserver(this); @@ -259,6 +263,58 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr< orchestrator.processChanges(); + std::optional retainedKey; + const auto retainedBoundary = orchestrator.getRetainedDynamicBoundary(); + size_t retainedStaticGroupCount = 0; + if (retainedBoundary) { + orchestrator.visitLayerGroupsBefore(*retainedBoundary, + [&](LayerGroupBase&, size_t) { ++retainedStaticGroupCount; }); + mat4 cameraMatrix; + state.getProjMatrix(cameraMatrix); + retainedKey = RetainedUnderlayKey{size, *retainedBoundary, cameraMatrix}; + } + bool legacyBelowBoundary = false; + if (retainedBoundary) { + for (const auto& item : layerRenderItems) { + if (item.index < static_cast(*retainedBoundary) && item.layer.get().usesLegacyRendering()) { + legacyBelowBoundary = true; + break; + } + } + } + const bool retainedSupported = retainedBoundary && retainedStaticGroupCount > 0 && + renderTreeParameters.loaded && !renderTreeParameters.has3D && + renderTreeParameters.debugOptions == MapDebugOptions::NoDebug && + orchestrator.retainedUnderlayIsStable(renderTreeParameters.timePoint, + *retainedBoundary) && + !renderTreeParameters.placementChanged && + renderTreeParameters.symbolFadeChange == 0.0f && + !legacyBelowBoundary; + const char* retainedFallbackReason = !retainedBoundary ? "no-boundary" + : retainedStaticGroupCount == 0 ? "empty-static-prefix" + : !renderTreeParameters.loaded ? "style-not-loaded" + : renderTreeParameters.has3D ? "3d" + : renderTreeParameters.debugOptions != MapDebugOptions::NoDebug + ? "debug-options" + : !orchestrator.retainedUnderlayIsStable(renderTreeParameters.timePoint, + *retainedBoundary) || + renderTreeParameters.placementChanged || + renderTreeParameters.symbolFadeChange != 0.0f + ? "static-transition-or-placement" + : legacyBelowBoundary ? "legacy-ordering" + : nullptr; + auto retainedDecision = retainedUnderlayCache->evaluate(retainedKey, + retainedSupported, + retainedFallbackReason); + if ((retainedDecision == RetainedUnderlayDecision::Build || + retainedDecision == RetainedUnderlayDecision::Reuse) && + (!staticData->shaders || !retainedUnderlayCache->prepareResources(context, *staticData->shaders, *retainedKey))) { + retainedUnderlayCache->markBuildFailed("resource-or-compositor"); + retainedDecision = RetainedUnderlayDecision::Fallback; + } + const bool useRetainedUnderlay = retainedDecision == RetainedUnderlayDecision::Build || + retainedDecision == RetainedUnderlayDecision::Reuse; + // Upload layer groups { const auto uploadPass = parameters.encoder->createUploadPass("layerGroup-upload", @@ -290,6 +346,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr< // Upload the Debug layer group orchestrator.visitDebugLayerGroups([&](LayerGroupBase& layerGroup) { layerGroup.upload(*uploadPass); }); + if (useRetainedUnderlay) retainedUnderlayCache->upload(*uploadPass); } const Size atlasSize = parameters.patternAtlas.getPixelSize(); @@ -353,6 +410,32 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr< [&](RenderTarget& renderTarget) { renderTarget.render(orchestrator, renderTree, parameters); }); }; + const auto buildRetainedUnderlay = [&] { + if (retainedDecision != RetainedUnderlayDecision::Build || !retainedBoundary) return; + retainedUnderlayCache->beginBuild(parameters); + context.bindGlobalUniformBuffers(*parameters.renderPass); + parameters.depthRangeSize = 1 - (orchestrator.numLayerGroups() + 2) * PaintParameters::numSublayers * + PaintParameters::depthEpsilon; + parameters.pass = RenderPass::Opaque; + orchestrator.visitLayerGroupsBeforeReversed(*retainedBoundary, + [&](LayerGroupBase& layerGroup, size_t ordinal) { + parameters.currentLayer = static_cast(orchestrator.numLayerGroups() - 1 - ordinal); + layerGroup.render(orchestrator, parameters); + particle::incrementRendererDebugCounter( + particle::RendererDebugCounter::RetainedUnderlayStaticGroupDraw); + }); + parameters.pass = RenderPass::Translucent; + orchestrator.visitLayerGroupsBefore(*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t ordinal) { + parameters.currentLayer = static_cast(orchestrator.numLayerGroups() - 1 - ordinal); + layerGroup.render(orchestrator, parameters); + particle::incrementRendererDebugCounter( + particle::RendererDebugCounter::RetainedUnderlayStaticGroupDraw); + }); + context.unbindGlobalUniformBuffers(*parameters.renderPass); + retainedUnderlayCache->finishBuild(parameters); + retainedUnderlayCache->markBuildSucceeded(); + }; + const auto commonClearPass = [&] { // - CLEAR // ------------------------------------------------------------------------------------- @@ -386,11 +469,20 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr< PaintParameters::depthEpsilon; // draw layer groups, opaque pass - parameters.currentLayer = 0; - orchestrator.visitLayerGroupsReversed([&](LayerGroupBase& layerGroup) { - layerGroup.render(orchestrator, parameters); - parameters.currentLayer++; - }); + if (useRetainedUnderlay && retainedBoundary) { + orchestrator.visitLayerGroupsFromReversed(*retainedBoundary, + [&](LayerGroupBase& layerGroup, size_t ordinal) { + parameters.currentLayer = static_cast(orchestrator.numLayerGroups() - 1 - ordinal); + layerGroup.render(orchestrator, parameters); + particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedDynamicGroupDraw); + }); + } else { + parameters.currentLayer = 0; + orchestrator.visitLayerGroupsReversed([&](LayerGroupBase& layerGroup) { + layerGroup.render(orchestrator, parameters); + parameters.currentLayer++; + }); + } }; const auto drawableTranslucentPass = [&] { @@ -400,13 +492,21 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr< PaintParameters::depthEpsilon; // draw layer groups, translucent pass - parameters.currentLayer = static_cast(orchestrator.numLayerGroups()) - 1; - orchestrator.visitLayerGroups([&](LayerGroupBase& layerGroup) { - layerGroup.render(orchestrator, parameters); - if (parameters.currentLayer > 0) { - parameters.currentLayer--; - } - }); + if (useRetainedUnderlay && retainedBoundary) { + orchestrator.visitLayerGroupsFrom(*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t ordinal) { + parameters.currentLayer = static_cast(orchestrator.numLayerGroups() - 1 - ordinal); + layerGroup.render(orchestrator, parameters); + particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedDynamicGroupDraw); + }); + } else { + parameters.currentLayer = static_cast(orchestrator.numLayerGroups()) - 1; + orchestrator.visitLayerGroups([&](LayerGroupBase& layerGroup) { + layerGroup.render(orchestrator, parameters); + if (parameters.currentLayer > 0) { + parameters.currentLayer--; + } + }); + } // Finally, render any legacy layers which have not been converted to drawables. // Note that they may be out of order, this is just a temporary fix for `RenderLocationIndicatorLayer` (#2216) @@ -439,8 +539,10 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr< drawable3DPass(); } drawableTargetsPass(); + buildRetainedUnderlay(); commonClearPass(); context.bindGlobalUniformBuffers(*parameters.renderPass); + if (useRetainedUnderlay) retainedUnderlayCache->composite(orchestrator, parameters); drawableOpaquePass(); drawableTranslucentPass(); drawableDebugOverlays(); diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 9e27dabf6..ea8a0bf48 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -16,6 +16,7 @@ namespace mbgl { class RendererObserver; class RenderStaticData; class RenderTree; +class RetainedUnderlayCache; namespace gfx { class RendererBackend; @@ -53,6 +54,7 @@ private: const float pixelRatio; std::unique_ptr staticData; + std::unique_ptr retainedUnderlayCache; gfx::DynamicTextureAtlasPtr dynamicTextureAtlas; bool styleLoaded = false; diff --git a/src/mbgl/renderer/retained_underlay_cache.cpp b/src/mbgl/renderer/retained_underlay_cache.cpp new file mode 100644 index 000000000..ea05fb612 --- /dev/null +++ b/src/mbgl/renderer/retained_underlay_cache.cpp @@ -0,0 +1,149 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +namespace { +constexpr auto CompositeShaderName = "RetainedUnderlayCompositeShader"; +} + +RetainedUnderlayCache::RetainedUnderlayCache() = default; +RetainedUnderlayCache::~RetainedUnderlayCache() = default; + +RetainedUnderlayDecision RetainedUnderlayCache::evaluate(const std::optional& nextKey, + bool supported, + const char* fallbackReason) { + if (!nextKey) return RetainedUnderlayDecision::Disabled; + if (!supported) { + ++fallbackCount; + particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedUnderlayFallback); + invalidate(fallbackReason ? fallbackReason : "unsupported"); + return RetainedUnderlayDecision::Fallback; + } + if (!key || *key != *nextKey) { + const char* reason = !key ? "first-frame" : (key->viewport != nextKey->viewport ? "viewport" : "camera-or-boundary"); + invalidate(reason); + key = nextKey; + } + if (valid) { + ++reuseCount; + particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedUnderlayReuse); + return RetainedUnderlayDecision::Reuse; + } + return RetainedUnderlayDecision::Build; +} + +bool RetainedUnderlayCache::prepareResources(gfx::Context& context, + gfx::ShaderRegistry& shaders, + const RetainedUnderlayKey& nextKey) { + try { + if (!offscreenTexture || offscreenTexture->getSize() != nextKey.viewport) { + offscreenTexture = context.createOffscreenTexture( + nextKey.viewport, gfx::TextureChannelDataType::UnsignedByte, true, true); + compositeLayerGroup.reset(); + } + if (!offscreenTexture || !offscreenTexture->isRenderable()) return false; + return compositeLayerGroup || rebuildComposite(context, shaders); + } catch (...) { + return false; + } +} + +bool RetainedUnderlayCache::rebuildComposite(gfx::Context& context, gfx::ShaderRegistry& shaders) { + auto shader = context.getGenericShader(shaders, CompositeShaderName); + auto group = context.createLayerGroup(0, 1, "retained-underlay-composite"); + if (!shader || !group || !offscreenTexture) return false; + + auto vertices = std::make_shared>( + RenderStaticData::heatmapTextureVertices()); + auto attributes = context.createVertexAttributeArray(); + if (const auto& attribute = attributes->set(shaders::idRetainedUnderlayCompositePosVertexAttribute)) { + attribute->setSharedRawData(vertices, + offsetof(HeatmapTextureLayoutVertex, a1), + 0, + sizeof(HeatmapTextureLayoutVertex), + gfx::AttributeDataType::Short2); + } + + auto builder = context.createDrawableBuilder("retained-underlay-composite"); + builder->setShader(std::move(shader)); + builder->setEnableDepth(false); + builder->setColorMode(gfx::ColorMode::alphaBlended()); + builder->setCullFaceMode(gfx::CullFaceMode::disabled()); + builder->setRenderPass(RenderPass::Translucent); + builder->setVertexAttributes(std::move(attributes)); + builder->setRawVertices({}, vertices->elements(), gfx::AttributeDataType::Short2); + auto segments = RenderStaticData::heatmapTextureSegments(); + builder->setSegments(gfx::Triangles(), + RenderStaticData::quadTriangleIndices().vector(), + segments.data(), + segments.size()); + builder->setTexture(offscreenTexture->getTexture(), shaders::idRetainedUnderlayCompositeTexture); + builder->flush(context); + for (auto& drawable : builder->clearDrawables()) group->addDrawable(std::move(drawable)); + compositeLayerGroup = std::move(group); + return !compositeLayerGroup->empty(); +} + +void RetainedUnderlayCache::upload(gfx::UploadPass& uploadPass) { + if (compositeLayerGroup) compositeLayerGroup->upload(uploadPass); +} + +void RetainedUnderlayCache::beginBuild(PaintParameters& parameters) { + parameters.renderPass = parameters.encoder->createRenderPass( + "retained static underlay", + {.renderable = *offscreenTexture, + .clearColor = Color{0.0f, 0.0f, 0.0f, 0.0f}, + .clearDepth = 1.0f, + .clearStencil = 0}); +#if MLN_RENDER_BACKEND_OPENGL + parameters.updateStencilBufferAvailability(); +#endif +} + +void RetainedUnderlayCache::finishBuild(PaintParameters& parameters) { + parameters.renderPass.reset(); + parameters.encoder->present(*offscreenTexture); +} + +void RetainedUnderlayCache::composite(RenderOrchestrator& orchestrator, PaintParameters& parameters) { + if (!compositeLayerGroup) return; + parameters.pass = RenderPass::Translucent; + parameters.currentLayer = 0; + compositeLayerGroup->render(orchestrator, parameters); +} + +void RetainedUnderlayCache::markBuildSucceeded() { + valid = true; + ++buildCount; + particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedUnderlayBuild); +} + +void RetainedUnderlayCache::markBuildFailed(const char* reason) { + ++fallbackCount; + particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedUnderlayFallback); + invalidate(reason); +} + +void RetainedUnderlayCache::invalidate(const char* reason) { + valid = false; + lastInvalidationReason = reason ? reason : "unknown"; + particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedUnderlayInvalidation); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/retained_underlay_cache.hpp b/src/mbgl/renderer/retained_underlay_cache.hpp new file mode 100644 index 000000000..466c7d3d5 --- /dev/null +++ b/src/mbgl/renderer/retained_underlay_cache.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +class LayerGroup; +class PaintParameters; +class RenderOrchestrator; + +namespace gfx { +class Context; +class OffscreenTexture; +class ShaderRegistry; +class UploadPass; +} // namespace gfx + +struct RetainedUnderlayKey { + Size viewport; + int32_t boundary = 0; + mat4 cameraMatrix{}; + + bool operator==(const RetainedUnderlayKey&) const = default; +}; + +enum class RetainedUnderlayDecision : uint8_t { Disabled, Build, Reuse, Fallback }; + +class RetainedDynamicBoundaryTracker { +public: + void consider(int32_t layerIndex, bool candidate) { + if (candidate && (!boundary || layerIndex < *boundary)) boundary = layerIndex; + } + std::optional value() const noexcept { return boundary; } + +private: + std::optional boundary; +}; + +enum class RetainedLayerGroupRange : uint8_t { Before, From }; +inline bool retainedLayerGroupInRange(int32_t layerIndex, int32_t boundary, RetainedLayerGroupRange range) { + return range == RetainedLayerGroupRange::Before ? layerIndex < boundary : layerIndex >= boundary; +} + +class RetainedUnderlayCache { +public: + RetainedUnderlayCache(); + ~RetainedUnderlayCache(); + + RetainedUnderlayDecision evaluate(const std::optional&, bool supported, + const char* fallbackReason = nullptr); + bool prepareResources(gfx::Context&, gfx::ShaderRegistry&, const RetainedUnderlayKey&); + void upload(gfx::UploadPass&); + void beginBuild(PaintParameters&); + void finishBuild(PaintParameters&); + void composite(RenderOrchestrator&, PaintParameters&); + void markBuildSucceeded(); + void markBuildFailed(const char* reason); + void invalidate(const char* reason); + + bool isValid() const noexcept { return valid; } + uint64_t getBuildCount() const noexcept { return buildCount; } + uint64_t getReuseCount() const noexcept { return reuseCount; } + uint64_t getFallbackCount() const noexcept { return fallbackCount; } + const std::string& getLastInvalidationReason() const noexcept { return lastInvalidationReason; } + +private: + bool rebuildComposite(gfx::Context&, gfx::ShaderRegistry&); + + std::optional key; + std::unique_ptr offscreenTexture; + std::shared_ptr compositeLayerGroup; + bool valid = false; + uint64_t buildCount = 0; + uint64_t reuseCount = 0; + uint64_t fallbackCount = 0; + std::string lastInvalidationReason; +}; + +} // namespace mbgl diff --git a/src/mbgl/shaders/gl/shader_info.cpp b/src/mbgl/shaders/gl/shader_info.cpp index 4cc3244ba..aeb154e30 100644 --- a/src/mbgl/shaders/gl/shader_info.cpp +++ b/src/mbgl/shaders/gl/shader_info.cpp @@ -304,6 +304,16 @@ const std::vector ParticleGpuValidationShaderInfo::textures = { TextureInfo{"u_state_texture", idParticleGpuValidationStateTexture}, }; +using RetainedUnderlayCompositeShaderInfo = + ShaderInfo; +const std::vector RetainedUnderlayCompositeShaderInfo::uniformBlocks = {}; +const std::vector RetainedUnderlayCompositeShaderInfo::attributes = { + AttributeInfo{"a_pos", idRetainedUnderlayCompositePosVertexAttribute}, +}; +const std::vector RetainedUnderlayCompositeShaderInfo::textures = { + TextureInfo{"u_image", idRetainedUnderlayCompositeTexture}, +}; + // Hillshade Prepare using HillshadePrepareShaderInfo = ShaderInfo; diff --git a/src/mbgl/shaders/mtl/retained_underlay_composite.cpp b/src/mbgl/shaders/mtl/retained_underlay_composite.cpp new file mode 100644 index 000000000..40b8aa3a0 --- /dev/null +++ b/src/mbgl/shaders/mtl/retained_underlay_composite.cpp @@ -0,0 +1,12 @@ +#include +#include + +namespace mbgl::shaders { +using Source = ShaderSource; +const std::array Source::attributes = { + AttributeInfo{0, gfx::AttributeDataType::Short2, 0, idRetainedUnderlayCompositePosVertexAttribute}, +}; +const std::array Source::textures = { + TextureInfo{0, idRetainedUnderlayCompositeTexture}, +}; +} // namespace mbgl::shaders diff --git a/src/mbgl/shaders/shader_source.cpp b/src/mbgl/shaders/shader_source.cpp index 1a4420ff3..a69b9b382 100644 --- a/src/mbgl/shaders/shader_source.cpp +++ b/src/mbgl/shaders/shader_source.cpp @@ -30,6 +30,7 @@ MBGL_DEFINE_ENUM(BuiltIn, { {BuiltIn::HeatmapShader, "HeatmapShader"}, {BuiltIn::HeatmapTextureShader, "HeatmapTextureShader"}, {BuiltIn::ParticleGpuValidationShader, "ParticleGpuValidationShader"}, +{BuiltIn::RetainedUnderlayCompositeShader, "RetainedUnderlayCompositeShader"}, {BuiltIn::HillshadePrepareShader, "HillshadePrepareShader"}, {BuiltIn::ColorReliefShader, "ColorReliefShader"}, {BuiltIn::HillshadeShader, "HillshadeShader"}, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 893838b72..9d6dadd2f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,6 +27,7 @@ add_library( ${PROJECT_SOURCE_DIR}/test/plugin/plugin.test.cpp ${PROJECT_SOURCE_DIR}/test/renderer/image_manager.test.cpp ${PROJECT_SOURCE_DIR}/test/renderer/pattern_atlas.test.cpp + ${PROJECT_SOURCE_DIR}/test/renderer/retained_underlay_cache.test.cpp ${PROJECT_SOURCE_DIR}/test/renderer/shader_registry.test.cpp $<$:${PROJECT_SOURCE_DIR}/test/renderer/wgsl_preprocessor.test.cpp> ${PROJECT_SOURCE_DIR}/test/sprite/sprite_loader.test.cpp diff --git a/test/gl/gl_functions.test.cpp b/test/gl/gl_functions.test.cpp index 9f22d229e..ed6b6cded 100644 --- a/test/gl/gl_functions.test.cpp +++ b/test/gl/gl_functions.test.cpp @@ -4,6 +4,10 @@ #include +#if defined(__APPLE__) +#include +#endif + using namespace mbgl::platform; TEST(GLFunctions, OpenGLES) { @@ -253,8 +257,15 @@ TEST(GLFunctions, OpenGLES) { EXPECT_NE(glGetProgramBinary, nullptr); EXPECT_NE(glProgramBinary, nullptr); EXPECT_NE(glProgramParameteri, nullptr); +#if defined(__APPLE__) +#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR EXPECT_NE(glInvalidateFramebuffer, nullptr); EXPECT_NE(glInvalidateSubFramebuffer, nullptr); +#endif +#else + EXPECT_NE(glInvalidateFramebuffer, nullptr); + EXPECT_NE(glInvalidateSubFramebuffer, nullptr); +#endif EXPECT_NE(glTexStorage2D, nullptr); EXPECT_NE(glTexStorage3D, nullptr); EXPECT_NE(glGetInternalformativ, nullptr); diff --git a/test/renderer/retained_underlay_cache.test.cpp b/test/renderer/retained_underlay_cache.test.cpp new file mode 100644 index 000000000..2217446cd --- /dev/null +++ b/test/renderer/retained_underlay_cache.test.cpp @@ -0,0 +1,89 @@ +#include + +#include + +#include + +using namespace mbgl; + +namespace { +RetainedUnderlayKey key(Size size = {640, 480}, int32_t boundary = 4) { + RetainedUnderlayKey result{size, boundary, {}}; + result.cameraMatrix[0] = 1.0; + result.cameraMatrix[5] = 1.0; + result.cameraMatrix[10] = 1.0; + result.cameraMatrix[15] = 1.0; + return result; +} +} // namespace + +TEST(RetainedDynamicBoundary, SelectsFirstCandidate) { + RetainedDynamicBoundaryTracker none; + none.consider(0, false); + none.consider(3, false); + EXPECT_FALSE(none.value()); + + RetainedDynamicBoundaryTracker particleOnly; + particleOnly.consider(5, true); + EXPECT_EQ(particleOnly.value(), 5); + + RetainedDynamicBoundaryTracker weatherAndParticle; + weatherAndParticle.consider(7, true); + weatherAndParticle.consider(2, true); + EXPECT_EQ(weatherAndParticle.value(), 2); + + RetainedDynamicBoundaryTracker particleAndWeather; + particleAndWeather.consider(2, true); + particleAndWeather.consider(7, true); + EXPECT_EQ(particleAndWeather.value(), 2); +} + +TEST(RetainedLayerGroupRange, PreservesForwardReverseOriginalOrdinals) { + const std::vector indices{0, 1, 3, 5, 8}; + std::vector beforeForward; + std::vector fromForward; + std::vector beforeReverse; + std::vector fromReverse; + for (size_t ordinal = 0; ordinal < indices.size(); ++ordinal) { + if (retainedLayerGroupInRange(indices[ordinal], 5, RetainedLayerGroupRange::Before)) + beforeForward.push_back(ordinal); + else + fromForward.push_back(ordinal); + } + for (size_t ordinal = indices.size(); ordinal-- > 0;) { + if (retainedLayerGroupInRange(indices[ordinal], 5, RetainedLayerGroupRange::Before)) + beforeReverse.push_back(ordinal); + else + fromReverse.push_back(ordinal); + } + EXPECT_EQ(beforeForward, (std::vector{0, 1, 2})); + EXPECT_EQ(fromForward, (std::vector{3, 4})); + EXPECT_EQ(beforeReverse, (std::vector{2, 1, 0})); + EXPECT_EQ(fromReverse, (std::vector{4, 3})); +} + +TEST(RetainedUnderlayCache, BuildReuseAndInvalidation) { + RetainedUnderlayCache cache; + EXPECT_EQ(cache.evaluate(std::nullopt, false), RetainedUnderlayDecision::Disabled); + EXPECT_EQ(cache.evaluate(key(), true), RetainedUnderlayDecision::Build); + cache.markBuildSucceeded(); + EXPECT_EQ(cache.evaluate(key(), true), RetainedUnderlayDecision::Reuse); + + EXPECT_EQ(cache.evaluate(key({800, 600}), true), RetainedUnderlayDecision::Build); + EXPECT_EQ(cache.getLastInvalidationReason(), "viewport"); + cache.markBuildSucceeded(); + EXPECT_EQ(cache.evaluate(key({800, 600}, 6), true), RetainedUnderlayDecision::Build); + EXPECT_EQ(cache.getLastInvalidationReason(), "camera-or-boundary"); +} + +TEST(RetainedUnderlayCache, UnsupportedAndResourceFailureFallBackSafely) { + RetainedUnderlayCache cache; + EXPECT_EQ(cache.evaluate(key(), false, "3d"), RetainedUnderlayDecision::Fallback); + EXPECT_EQ(cache.getLastInvalidationReason(), "3d"); + EXPECT_FALSE(cache.isValid()); + + EXPECT_EQ(cache.evaluate(key(), true), RetainedUnderlayDecision::Build); + cache.markBuildFailed("resource-or-compositor"); + EXPECT_FALSE(cache.isValid()); + EXPECT_EQ(cache.getFallbackCount(), 2u); +}