#include #include #include #include #include #include #include #include using namespace mbgl; namespace { class TestUniformBuffers final : public gfx::UniformBufferArray { public: void bind(gfx::RenderPass&) override {} protected: std::unique_ptr copy(const gfx::UniformBuffer&) override { return nullptr; } }; class TestLayerGroup final : public LayerGroup { public: explicit TestLayerGroup(int32_t index) : LayerGroup(index, 0, "step097-error-group") {} const gfx::UniformBufferArray& getUniformBuffers() const override { return buffers; } gfx::UniformBufferArray& mutableUniformBuffers() override { return buffers; } private: TestUniformBuffers buffers; }; class ErrorObserver final : public RendererObserver { public: void onRenderError(std::exception_ptr error) override { ++errors; try { std::rethrow_exception(error); } catch (const std::runtime_error& e) { message = e.what(); } } int errors = 0; std::string message; }; } // namespace TEST(RenderOrchestratorError, RangeVisitorReportsOpaqueAndTranslucentFailures) { TaggedScheduler scheduler{Scheduler::GetBackground(), {}}; RenderOrchestrator orchestrator(false, scheduler, std::nullopt); ErrorObserver observer; orchestrator.setObserver(&observer); ASSERT_TRUE(orchestrator.addLayerGroup(std::make_shared(0))); ASSERT_TRUE(orchestrator.addLayerGroup(std::make_shared(1))); bool failed = false; orchestrator.visitLayerGroupsInRangeReversed(0, 2, [](LayerGroupBase&, size_t) { throw std::runtime_error("opaque failure"); }, &failed); EXPECT_TRUE(failed); EXPECT_EQ(observer.errors, 2); EXPECT_EQ(observer.message, "opaque failure"); failed = false; observer.errors = 0; orchestrator.visitLayerGroupsInRange(0, 2, [](LayerGroupBase&, size_t) { throw std::runtime_error("translucent failure"); }, &failed); EXPECT_TRUE(failed); EXPECT_EQ(observer.errors, 2); EXPECT_EQ(observer.message, "translucent failure"); failed = false; int visits = 0; orchestrator.visitLayerGroupsInRange(0, 2, [&](LayerGroupBase&, size_t) { ++visits; }, &failed); EXPECT_FALSE(failed); EXPECT_EQ(visits, 2); } TEST(RenderOrchestratorError, FailedRangeCannotReuseAndCleanRetryCanRecover) { TaggedScheduler scheduler{Scheduler::GetBackground(), {}}; RenderOrchestrator orchestrator(false, scheduler, std::nullopt); ErrorObserver observer; orchestrator.setObserver(&observer); ASSERT_TRUE(orchestrator.addLayerGroup(std::make_shared(0))); RetainedUnderlayCache cache; RetainedUnderlayKey key{{4, 4}, 0, 1, {}}; key.cameraMatrix[0] = key.cameraMatrix[5] = key.cameraMatrix[10] = key.cameraMatrix[15] = 1.0; for (bool opaque : {true, false}) { EXPECT_EQ(cache.evaluate(key, 1, true), RetainedUnderlayDecision::Build); bool failed = false; const auto throwOnRender = [](LayerGroupBase&, size_t) { throw std::runtime_error("render failure"); }; if (opaque) orchestrator.visitLayerGroupsInRangeReversed(0, 1, throwOnRender, &failed); else orchestrator.visitLayerGroupsInRange(0, 1, throwOnRender, &failed); ASSERT_TRUE(failed); cache.markBuildFailed("layer-render-error"); EXPECT_FALSE(cache.isValid()); EXPECT_EQ(cache.evaluate(key, 1, true), RetainedUnderlayDecision::Build); } bool failed = false; orchestrator.visitLayerGroupsInRange(0, 1, [](LayerGroupBase&, size_t) {}, &failed); ASSERT_FALSE(failed); cache.markBuildSucceeded(); EXPECT_EQ(cache.evaluate(key, 1, true), RetainedUnderlayDecision::Reuse); }