Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d02939ed authored by Matt Buckley's avatar Matt Buckley
Browse files

Cap the maximum message queue size for reportActualWorkDuration

Currently, the queue size is only cleared when it gets sent, so if a
client calls the API fast enough it can overwhelm the duration buffer
before a batch is sent. This patch caps the maximum buffer size to
prevent this.

Bug: 406377213
Test: atest PerformanceHintNativeTestCases
Flag: EXEMPT BUGFIX
Change-Id: I288299636682644d118250339330be161262398f
parent 25faa39d
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -13,6 +13,17 @@ flag {
    }
}

flag {
    name: "adpf_cap_max_batch_size"
    namespace: "game"
    description: "Caps the maximum batch size of work durations that can be sent in a report"
    is_fixed_read_only: true
    bug: "406377213"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "adpf_gpu_report_actual_work_duration"
    is_exported: true
+1 −0
Original line number Diff line number Diff line
@@ -432,6 +432,7 @@ LIBANDROID_PLATFORM {
    APerformanceHint_setUseNewLoadHintBehaviorForTesting;
    APerformanceHint_closeSessionFromJava;
    APerformanceHint_createSessionFromJava;
    APerformanceHint_setReportBatchSizeCapForTesting;
    extern "C++" {
        ASurfaceControl_registerSurfaceStatsListener*;
        ASurfaceControl_unregisterSurfaceStatsListener*;
+21 −3
Original line number Diff line number Diff line
@@ -98,6 +98,9 @@ constexpr double kReplenishRate = kMaxLoadHintsPerInterval / static_cast<double>
constexpr int64_t kSendHintTimeout = kLoadHintInterval / kMaxLoadHintsPerInterval;
bool kForceNewHintBehavior = false;

std::optional<size_t> kReportBatchSizeCap =
        android::os::adpf_cap_max_batch_size() ? std::make_optional(50) : std::nullopt;

template <class T>
constexpr int32_t enum_size() {
    return static_cast<int32_t>(*(ndk::enum_range<T>().end() - 1)) + 1;
@@ -783,7 +786,16 @@ int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* wor
    }

    traceActualDuration(actualTotalDurationNanos);
    mActualWorkDurations.push_back(std::move(*workDuration));
    mActualWorkDurations.push_back(*workDuration);

    if (kReportBatchSizeCap.has_value()) {
        // Check if the buffer is larger than the max size, and if it is pop the oldest elements
        const int overflow = mActualWorkDurations.size() - *kReportBatchSizeCap;
        if (overflow > 0) {
            mActualWorkDurations.erase(mActualWorkDurations.begin(),
                                       mActualWorkDurations.begin() + overflow);
        }
    }

    if (actualTotalDurationNanos >= mTargetDurationNanos) {
        // Reset timestamps if we are equal or over the target.
@@ -872,12 +884,14 @@ void APerformanceHintManager::layersFromNativeSurfaces(ANativeWindow** windows,
        std::vector<ASurfaceControl*> controlVec(controls, controls + numSurfaceControls);
        for (auto&& aSurfaceControl : controlVec) {
            SurfaceControl* control = reinterpret_cast<SurfaceControl*>(aSurfaceControl);
            if (control != nullptr) {
                if (control->isValid()) {
                    out.push_back(control->getHandle());
                }
            }
        }
    }
}

// ===================================== FMQ wrapper implementation

@@ -1396,6 +1410,10 @@ void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior) {
    kForceNewHintBehavior = newBehavior;
}

void APerformanceHint_setReportBatchSizeCapForTesting(int cap) {
    kReportBatchSizeCap = cap > 0 ? std::make_optional(cap) : std::nullopt;
}

void ASessionCreationConfig_setNativeSurfaces(ASessionCreationConfig* config,
                                              ANativeWindow** nativeWindows,
                                              size_t nativeWindowsSize,
+57 −0
Original line number Diff line number Diff line
@@ -640,6 +640,63 @@ TEST_F(PerformanceHintTest, TestSupportObject) {
    EXPECT_EQ(expectedSupportInt, actualSupportInt);
}

TEST_F(PerformanceHintTest, TestReportActualOverflow) {
    mClientData.preferredRateNanos = 10000000L;
    APerformanceHintManager* manager = createManager();

    auto&& config = configFromCreator({
            .tids = mTids,
            .targetDuration = 20,
    });

    auto&& session = createSession(manager);
    hal::WorkDuration duration{.timeStampNanos = 3,
                               .durationNanos = 10,
                               .workPeriodStartTimestampNanos = 1,
                               .cpuDurationNanos = 5,
                               .gpuDurationNanos = 5};

    EXPECT_CALL(*mMockSession, reportActualWorkDuration2(_)).Times(2);

    // Report a duration under the target, to signal the start of good behavior per the rate limiter
    APerformanceHint_reportActualWorkDuration2(session.get(),
                                               reinterpret_cast<AWorkDuration*>(&duration));

    // Sleep for longer than preferredUpdateRateNanos.
    usleep(12000);

    // Report a duration under the target, to signal continued good behavior per the rate limiter
    APerformanceHint_reportActualWorkDuration2(session.get(),
                                               reinterpret_cast<AWorkDuration*>(&duration));

    EXPECT_CALL(*mMockSession, reportActualWorkDuration2(SizeIs(Eq(30)))).Times(1);

    APerformanceHint_setReportBatchSizeCapForTesting(-1);
    for (int i = 0; i < 29; ++i) {
        APerformanceHint_reportActualWorkDuration2(session.get(),
                                                   reinterpret_cast<AWorkDuration*>(&duration));
    }

    // Sleep for longer than preferredUpdateRateNanos.
    usleep(12000);
    APerformanceHint_reportActualWorkDuration2(session.get(),
                                               reinterpret_cast<AWorkDuration*>(&duration));

    // Enforce that report spam gets capped
    EXPECT_CALL(*mMockSession, reportActualWorkDuration2(SizeIs(Eq(10)))).Times(1);

    APerformanceHint_setReportBatchSizeCapForTesting(10);
    for (int i = 0; i < 29; ++i) {
        APerformanceHint_reportActualWorkDuration2(session.get(),
                                                   reinterpret_cast<AWorkDuration*>(&duration));
    }

    // Sleep for longer than preferredUpdateRateNanos.
    usleep(12000);
    APerformanceHint_reportActualWorkDuration2(session.get(),
                                               reinterpret_cast<AWorkDuration*>(&duration));
}

TEST_F(PerformanceHintTest, TestCreatingAutoSession) {
    // Disable GPU capability for testing
    mClientData.supportInfo.sessionModes &= ~(1 << (int)hal::SessionMode::AUTO_GPU);