feat: add retained underlay render cache poc
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
tei
2026-09-21 22:53:00 +09:00
parent f47469d2ab
commit 8b9ab30322
39 changed files with 781 additions and 23 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -82,6 +82,10 @@ public:
virtual void reduceMemoryUsage() = 0;
virtual std::unique_ptr<OffscreenTexture> createOffscreenTexture(Size, TextureChannelDataType) = 0;
virtual std::unique_ptr<OffscreenTexture> createOffscreenTexture(Size,
TextureChannelDataType,
bool,
bool) = 0;
template <RenderbufferPixelType pixelType>
Renderbuffer<pixelType> createRenderbuffer(const Size size) {

View File

@@ -106,7 +106,10 @@ public:
void setDirtyState() override;
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool);
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size,
gfx::TextureChannelDataType,
bool,
bool) override;
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType) override;

View File

@@ -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) {}

View File

@@ -0,0 +1,29 @@
// Generated code, do not modify this file!
#pragma once
#include <mbgl/shaders/shader_source.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::RetainedUnderlayCompositeShader, gfx::Backend::Type::OpenGL> {
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

View File

@@ -156,6 +156,13 @@ struct ShaderInfo<BuiltIn::ParticleGpuValidationShader, gfx::Backend::Type::Open
static const std::vector<TextureInfo> textures;
};
template <>
struct ShaderInfo<BuiltIn::RetainedUnderlayCompositeShader, gfx::Backend::Type::OpenGL> {
static const std::vector<AttributeInfo> attributes;
static const std::vector<UniformBlockInfo> uniformBlocks;
static const std::vector<TextureInfo> textures;
};
template <>
struct ShaderInfo<BuiltIn::HillshadePrepareShader, gfx::Backend::Type::OpenGL> {
static const std::vector<AttributeInfo> attributes;

View File

@@ -0,0 +1,34 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/mtl/shader_program.hpp>
namespace mbgl::shaders {
template <>
struct ShaderSource<BuiltIn::RetainedUnderlayCompositeShader, gfx::Backend::Type::Metal> {
static constexpr auto name = "RetainedUnderlayCompositeShader";
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 1> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> 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<float, access::sample> image [[texture(0)]],
sampler imageSampler [[sampler(0)]]) {
return half4(image.sample(imageSampler, in.uv));
}
)";
};
} // namespace mbgl::shaders

View File

@@ -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<uint3
static_cast<uint32_t>(fillExtrusionTextureCount),
static_cast<uint32_t>(heatmapTextureCount),
static_cast<uint32_t>(particleGpuValidationTextureCount),
static_cast<uint32_t>(retainedUnderlayCompositeTextureCount),
static_cast<uint32_t>(hillshadeTextureCount),
static_cast<uint32_t>(colorReliefTextureCount),
static_cast<uint32_t>(lineTextureCount),
@@ -410,6 +416,11 @@ enum {
particleGpuValidationVertexAttributeCount
};
enum {
idRetainedUnderlayCompositePosVertexAttribute,
retainedUnderlayCompositeVertexAttributeCount
};
enum {
idCirclePosVertexAttribute,

View File

@@ -25,6 +25,7 @@
#include <mbgl/shaders/gl/heatmap.hpp>
#include <mbgl/shaders/gl/heatmap_texture.hpp>
#include <mbgl/shaders/gl/particle_gpu_validation.hpp>
#include <mbgl/shaders/gl/retained_underlay_composite.hpp>
#include <mbgl/shaders/gl/hillshade_prepare.hpp>
#include <mbgl/shaders/gl/color_relief.hpp>
#include <mbgl/shaders/gl/hillshade.hpp>

View File

@@ -31,6 +31,7 @@ enum class BuiltIn {
HeatmapShader,
HeatmapTextureShader,
ParticleGpuValidationShader,
RetainedUnderlayCompositeShader,
HillshadePrepareShader,
ColorReliefShader,
HillshadeShader,

View File

@@ -106,7 +106,10 @@ public:
void setDirtyState() override {}
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool);
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size,
gfx::TextureChannelDataType,
bool,
bool) override;
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType) override;

View File

@@ -43,7 +43,10 @@ public:
void reduceMemoryUsage() override;
// Resource creation
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool);
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size,
gfx::TextureChannelDataType,
bool,
bool) override;
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType) override;
std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override;
gfx::VertexAttributeArrayPtr createVertexAttributeArray() const override;

View File

@@ -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",

View File

@@ -0,0 +1,6 @@
in vec2 v_uv;
uniform sampler2D u_image;
void main() {
fragColor = texture(u_image, v_uv);
}

View File

@@ -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);
}

View File

@@ -436,7 +436,15 @@ std::unique_ptr<gfx::OffscreenTexture> Context::createOffscreenTexture(const Siz
const gfx::TextureChannelDataType type) {
MLN_TRACE_FUNC();
return std::make_unique<gl::OffscreenTexture>(*this, size, type);
return std::make_unique<gl::OffscreenTexture>(*this, size, type, false, false);
}
std::unique_ptr<gfx::OffscreenTexture> Context::createOffscreenTexture(const Size size,
const gfx::TextureChannelDataType type,
const bool depth,
const bool stencil) {
MLN_TRACE_FUNC();
return std::make_unique<gl::OffscreenTexture>(*this, size, type, depth, stencil);
}
std::unique_ptr<gfx::DrawScopeResource> 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<gfx::RenderbufferPixelType::DepthStencil>& 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<const gl::Texture2D&>(color).getTextureID(),
0));
bindDepthStencilRenderbuffer(depthStencil);
checkFramebuffer();
return {color.getSize(), std::move(fbo)};
}
gfx::VertexAttributeArrayPtr Context::createVertexAttributeArray() const {
return std::make_shared<VertexAttributeArrayGL>();
}

View File

@@ -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::RenderbufferPixelType::DepthStencil>&);
gfx::VertexAttributeArrayPtr createVertexAttributeArray() const override;
@@ -212,6 +214,10 @@ private:
public:
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType) override;
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size,
gfx::TextureChannelDataType,
bool,
bool) override;
private:
std::unique_ptr<gfx::RenderbufferResource> createRenderbufferResource(gfx::RenderbufferPixelType,

View File

@@ -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<gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>>(
context.createRenderbuffer<gfx::RenderbufferPixelType::DepthStencil>(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<gl::Framebuffer> framebuffer;
std::unique_ptr<gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>> depthStencil;
};
OffscreenTexture::OffscreenTexture(gl::Context& context, const Size size_, const gfx::TextureChannelDataType type)
: gfx::OffscreenTexture(size, std::make_unique<OffscreenTextureResource>(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<OffscreenTextureResource>(context, size_, type, depth, stencil)) {}
bool OffscreenTexture::isRenderable() {
try {

View File

@@ -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;

View File

@@ -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,

View File

@@ -28,6 +28,7 @@
#include <mbgl/shaders/mtl/line.hpp>
#include <mbgl/shaders/mtl/location_indicator.hpp>
#include <mbgl/shaders/mtl/particle_gpu_validation.hpp>
#include <mbgl/shaders/mtl/retained_underlay_composite.hpp>
#include <mbgl/shaders/mtl/raster.hpp>
#include <mbgl/shaders/mtl/symbol.hpp>
#include <mbgl/shaders/mtl/widevector.hpp>
@@ -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,

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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; }

View File

@@ -7,6 +7,7 @@
#include <mbgl/renderer/render_source.hpp>
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/renderer/retained_underlay_cache.hpp>
#include <mbgl/renderer/render_tree.hpp>
#include <mbgl/renderer/update_parameters.hpp>
#include <mbgl/renderer/upload_parameters.hpp>
@@ -364,6 +365,13 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
// Track which layers are flagged for rendering
std::vector<bool> updateList(orderedLayers.size());
RetainedDynamicBoundaryTracker boundaryTracker;
for (std::size_t index = 0; index < orderedLayers.size(); ++index) {
boundaryTracker.consider(static_cast<int32_t>(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<std::size_t>(
boundary > 0 ? static_cast<std::size_t>(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();

View File

@@ -15,9 +15,11 @@
#include <mbgl/renderer/image_manager_observer.hpp>
#include <mbgl/text/placement.hpp>
#include <mbgl/renderer/render_tree.hpp>
#include <mbgl/renderer/retained_underlay_cache.hpp>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
@@ -106,6 +108,8 @@ public:
bool addLayerGroup(LayerGroupBasePtr);
bool removeLayerGroup(const LayerGroupBasePtr&);
size_t numLayerGroups() const noexcept;
std::optional<int32_t> getRetainedDynamicBoundary() const noexcept { return retainedDynamicBoundary; }
bool retainedUnderlayIsStable(TimePoint, int32_t boundary) const;
void updateLayerIndex(LayerGroupBasePtr, int32_t newIndex);
template <typename Func /* void(LayerGroupBase&) */>
@@ -121,6 +125,72 @@ public:
}
}
template <typename Func /* void(LayerGroupBase&, size_t originalOrdinal) */>
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 <typename Func /* void(LayerGroupBase&, size_t originalOrdinal) */>
void visitLayerGroupsFrom(int32_t boundary, Func f) {
size_t ordinal = static_cast<size_t>(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 <typename Func /* void(LayerGroupBase&, size_t originalOrdinal) */>
void visitLayerGroupsBeforeReversed(int32_t boundary, Func f) {
auto end = layerGroupsByLayerIndex.lower_bound(boundary);
size_t ordinal = static_cast<size_t>(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 <typename Func /* void(LayerGroupBase&, size_t originalOrdinal) */>
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 <typename Func /* void(LayerGroupBase&) */>
void visitLayerGroupsReversed(Func f) {
for (auto rit = layerGroupsByLayerIndex.rbegin(); rit != layerGroupsByLayerIndex.rend(); ++rit) {
@@ -241,6 +311,7 @@ private:
using LayerGroupMap = std::multimap<int32_t, LayerGroupBasePtr>;
LayerGroupMap layerGroupsByLayerIndex;
std::optional<int32_t> retainedDynamicBoundary;
std::vector<RenderTargetPtr> renderTargets;
RenderItem::DebugLayerGroupMap debugLayerGroups;

View File

@@ -23,6 +23,8 @@
#include <mbgl/gfx/drawable_tweaker.hpp>
#include <mbgl/renderer/layer_tweaker.hpp>
#include <mbgl/renderer/render_target.hpp>
#include <mbgl/renderer/retained_underlay_cache.hpp>
#include <mbgl/particle/renderer_debug_counters.hpp>
#if MLN_RENDER_BACKEND_METAL
#include <mbgl/mtl/renderer_backend.hpp>
@@ -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<RetainedUnderlayCache>()) {}
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>& 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<RetainedUnderlayKey> 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<uint32_t>(*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<uint32_t>(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<uint32_t>(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
if (useRetainedUnderlay && retainedBoundary) {
orchestrator.visitLayerGroupsFromReversed(*retainedBoundary,
[&](LayerGroupBase& layerGroup, size_t ordinal) {
parameters.currentLayer = static_cast<uint32_t>(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,6 +492,13 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
PaintParameters::depthEpsilon;
// draw layer groups, translucent pass
if (useRetainedUnderlay && retainedBoundary) {
orchestrator.visitLayerGroupsFrom(*retainedBoundary, [&](LayerGroupBase& layerGroup, size_t ordinal) {
parameters.currentLayer = static_cast<uint32_t>(orchestrator.numLayerGroups() - 1 - ordinal);
layerGroup.render(orchestrator, parameters);
particle::incrementRendererDebugCounter(particle::RendererDebugCounter::RetainedDynamicGroupDraw);
});
} else {
parameters.currentLayer = static_cast<uint32_t>(orchestrator.numLayerGroups()) - 1;
orchestrator.visitLayerGroups([&](LayerGroupBase& layerGroup) {
layerGroup.render(orchestrator, parameters);
@@ -407,6 +506,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
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();

View File

@@ -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<RenderStaticData> staticData;
std::unique_ptr<RetainedUnderlayCache> retainedUnderlayCache;
gfx::DynamicTextureAtlasPtr dynamicTextureAtlas;
bool styleLoaded = false;

View File

@@ -0,0 +1,149 @@
#include <mbgl/renderer/retained_underlay_cache.hpp>
#include <mbgl/gfx/color_mode.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/gfx/cull_face_mode.hpp>
#include <mbgl/gfx/drawable_builder.hpp>
#include <mbgl/gfx/offscreen_texture.hpp>
#include <mbgl/gfx/render_pass.hpp>
#include <mbgl/gfx/upload_pass.hpp>
#include <mbgl/gfx/vertex_attribute.hpp>
#include <mbgl/particle/renderer_debug_counters.hpp>
#include <mbgl/renderer/buckets/heatmap_bucket.hpp>
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_orchestrator.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/shaders/shader_defines.hpp>
namespace mbgl {
namespace {
constexpr auto CompositeShaderName = "RetainedUnderlayCompositeShader";
}
RetainedUnderlayCache::RetainedUnderlayCache() = default;
RetainedUnderlayCache::~RetainedUnderlayCache() = default;
RetainedUnderlayDecision RetainedUnderlayCache::evaluate(const std::optional<RetainedUnderlayKey>& 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<gfx::VertexVector<HeatmapTextureLayoutVertex>>(
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

View File

@@ -0,0 +1,85 @@
#pragma once
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/size.hpp>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
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<int32_t> value() const noexcept { return boundary; }
private:
std::optional<int32_t> 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<RetainedUnderlayKey>&, 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<RetainedUnderlayKey> key;
std::unique_ptr<gfx::OffscreenTexture> offscreenTexture;
std::shared_ptr<LayerGroup> compositeLayerGroup;
bool valid = false;
uint64_t buildCount = 0;
uint64_t reuseCount = 0;
uint64_t fallbackCount = 0;
std::string lastInvalidationReason;
};
} // namespace mbgl

View File

@@ -304,6 +304,16 @@ const std::vector<TextureInfo> ParticleGpuValidationShaderInfo::textures = {
TextureInfo{"u_state_texture", idParticleGpuValidationStateTexture},
};
using RetainedUnderlayCompositeShaderInfo =
ShaderInfo<BuiltIn::RetainedUnderlayCompositeShader, gfx::Backend::Type::OpenGL>;
const std::vector<UniformBlockInfo> RetainedUnderlayCompositeShaderInfo::uniformBlocks = {};
const std::vector<AttributeInfo> RetainedUnderlayCompositeShaderInfo::attributes = {
AttributeInfo{"a_pos", idRetainedUnderlayCompositePosVertexAttribute},
};
const std::vector<TextureInfo> RetainedUnderlayCompositeShaderInfo::textures = {
TextureInfo{"u_image", idRetainedUnderlayCompositeTexture},
};
// Hillshade Prepare
using HillshadePrepareShaderInfo = ShaderInfo<BuiltIn::HillshadePrepareShader, gfx::Backend::Type::OpenGL>;

View File

@@ -0,0 +1,12 @@
#include <mbgl/shaders/mtl/retained_underlay_composite.hpp>
#include <mbgl/shaders/shader_defines.hpp>
namespace mbgl::shaders {
using Source = ShaderSource<BuiltIn::RetainedUnderlayCompositeShader, gfx::Backend::Type::Metal>;
const std::array<AttributeInfo, 1> Source::attributes = {
AttributeInfo{0, gfx::AttributeDataType::Short2, 0, idRetainedUnderlayCompositePosVertexAttribute},
};
const std::array<TextureInfo, 1> Source::textures = {
TextureInfo{0, idRetainedUnderlayCompositeTexture},
};
} // namespace mbgl::shaders

View File

@@ -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"},

View File

@@ -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
$<$<BOOL:${MLN_WITH_WEBGPU}>:${PROJECT_SOURCE_DIR}/test/renderer/wgsl_preprocessor.test.cpp>
${PROJECT_SOURCE_DIR}/test/sprite/sprite_loader.test.cpp

View File

@@ -4,6 +4,10 @@
#include <mbgl/platform/gl_functions.hpp>
#if defined(__APPLE__)
#include <TargetConditionals.h>
#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);

View File

@@ -0,0 +1,89 @@
#include <mbgl/renderer/retained_underlay_cache.hpp>
#include <gtest/gtest.h>
#include <vector>
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<int32_t> indices{0, 1, 3, 5, 8};
std::vector<size_t> beforeForward;
std::vector<size_t> fromForward;
std::vector<size_t> beforeReverse;
std::vector<size_t> 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<size_t>{0, 1, 2}));
EXPECT_EQ(fromForward, (std::vector<size_t>{3, 4}));
EXPECT_EQ(beforeReverse, (std::vector<size_t>{2, 1, 0}));
EXPECT_EQ(fromReverse, (std::vector<size_t>{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);
}