Loading services/surfaceflinger/Scheduler/LayerHistory.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -97,6 +97,7 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) { } LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { ATRACE_CALL(); std::lock_guard lock(mLock); partitionLayers(now); Loading services/surfaceflinger/Scheduler/LayerInfoV2.cpp +22 −8 Original line number Diff line number Diff line Loading @@ -61,21 +61,35 @@ bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const { } bool LayerInfoV2::isFrequent(nsecs_t now) const { // Find the first valid frame time auto it = mFrameTimes.begin(); for (; it != mFrameTimes.end(); ++it) { if (isFrameTimeValid(*it)) { break; } } // If we know nothing about this layer we consider it as frequent as it might be the start // of an animation. if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) { if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) { return true; } // Layer is frequent if the earliest value in the window of most recent present times is // within threshold. const auto it = mFrameTimes.end() - FREQUENT_LAYER_WINDOW_SIZE; if (!isFrameTimeValid(*it)) { return true; // Find the first active frame for (; it != mFrameTimes.end(); ++it) { if (it->queueTime >= getActiveLayerThreshold(now)) { break; } } const auto numFrames = std::distance(it, mFrameTimes.end()) - 1; if (numFrames <= 0) { return false; } const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count(); return it->queueTime >= threshold; // Layer is considered frequent if the average frame rate is higher than the threshold const auto totalTime = mFrameTimes.back().queueTime - it->queueTime; return (1e9f * numFrames) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER; } bool LayerInfoV2::hasEnoughDataForHeuristic() const { Loading services/surfaceflinger/Scheduler/LayerInfoV2.h +3 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,9 @@ class LayerInfoV2 { // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in // favor of a low refresh rate. static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3; static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms; static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f; static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms; friend class LayerHistoryTestV2; Loading services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +87 −59 Original line number Diff line number Diff line Loading @@ -23,8 +23,6 @@ #include <chrono> #include <cmath> using namespace std::chrono_literals; namespace android::scheduler { using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType; Loading Loading @@ -84,14 +82,31 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContent( return *bestSoFar; } std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const { auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod); if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION || std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) { displayFramesQuot++; displayFramesRem = 0; } return {displayFramesQuot, displayFramesRem}; } const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2( const std::vector<LayerRequirement>& layers) const { constexpr nsecs_t MARGIN = std::chrono::nanoseconds(800us).count(); const std::vector<LayerRequirement>& layers, bool touchActive) const { ATRACE_CALL(); ALOGV("getRefreshRateForContent %zu layers", layers.size()); std::lock_guard lock(mLock); // For now if the touch is active return the peak refresh rate // This should be optimized to consider other layers as well. if (touchActive) { return *mAvailableRefreshRates.back(); } int noVoteLayers = 0; int minVoteLayers = 0; int maxVoteLayers = 0; Loading @@ -115,11 +130,6 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2( return *mAvailableRefreshRates.front(); } // If we have some Max layers and no Explicit we should return Max if (maxVoteLayers > 0 && explicitDefaultVoteLayers + explicitExactOrMultipleVoteLayers == 0) { return *mAvailableRefreshRates.back(); } // Find the best refresh rate based on score std::vector<std::pair<const RefreshRate*, float>> scores; scores.reserve(mAvailableRefreshRates.size()); Loading @@ -130,67 +140,85 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2( for (const auto& layer : layers) { ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote); if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min || layer.vote == LayerVoteType::Max) { if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) { continue; } // Adjust the weight in case we have explicit layers. The priority is: // - ExplicitExactOrMultiple // - ExplicitDefault // - Heuristic auto weight = layer.weight; if (explicitExactOrMultipleVoteLayers + explicitDefaultVoteLayers > 0) { if (layer.vote == LayerVoteType::Heuristic) { weight /= 2.f; } } if (explicitExactOrMultipleVoteLayers > 0) { if (layer.vote == LayerVoteType::Heuristic || layer.vote == LayerVoteType::ExplicitDefault) { weight /= 2.f; } for (auto i = 0u; i < scores.size(); i++) { // If the layer wants Max, give higher score to the higher refresh rate if (layer.vote == LayerVoteType::Max) { const auto ratio = scores[i].first->fps / scores.back().first->fps; // use ratio^2 to get a lower score the more we get further from peak const auto layerScore = ratio * ratio; ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } for (auto& [refreshRate, overallScore] : scores) { const auto displayPeriod = refreshRate->vsyncPeriod; const auto displayPeriod = scores[i].first->vsyncPeriod; const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate); if (layer.vote == LayerVoteType::ExplicitDefault) { const auto layerScore = [&]() { const auto [displayFramesQuot, displayFramesRem] = getDisplayFrames(layerPeriod, displayPeriod); if (displayFramesQuot == 0) { // Layer desired refresh rate is higher the display rate. return static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod); } // Calculate how many display vsyncs we need to present a single frame for this layer auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod); if (displayFramesRem <= MARGIN || std::abs(displayFramesRem - displayPeriod) <= MARGIN) { displayFramesQuot++; displayFramesRem = 0; return 1.0f - (static_cast<float>(displayFramesRem) / static_cast<float>(layerPeriod)); }(); ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } float layerScore; static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1 if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || layer.vote == LayerVoteType::Heuristic) { const auto layerScore = [&]() { // Calculate how many display vsyncs we need to present a single frame for this // layer const auto [displayFramesQuot, displayFramesRem] = getDisplayFrames(layerPeriod, displayPeriod); static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1 if (displayFramesRem == 0) { // Layer desired refresh rate matches the display rate. layerScore = weight * 1.0f; } else if (displayFramesQuot == 0) { return 1.0f; } if (displayFramesQuot == 0) { // Layer desired refresh rate is higher the display rate. layerScore = weight * (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) * return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) * (1.0f / (MAX_FRAMES_TO_FIT + 1)); } else { // Layer desired refresh rate is lower the display rate. Check how well it fits the // cadence } // Layer desired refresh rate is lower the display rate. Check how well it fits // the cadence auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem)); int iter = 2; while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) { while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) { diff = diff - (displayPeriod - diff); iter++; } layerScore = weight * (1.0f / iter); return 1.0f / iter; }(); ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight, 1e9f / layerPeriod, refreshRate->name.c_str(), layerScore); overallScore += layerScore; } } Loading services/surfaceflinger/Scheduler/RefreshRateConfigs.h +15 −4 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include "Scheduler/StrongTyping.h" namespace android::scheduler { using namespace std::chrono_literals; enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 }; Loading @@ -43,6 +44,10 @@ inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateC */ class RefreshRateConfigs { public: // Margin used when matching refresh rates to the content desired ones. static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION = std::chrono::nanoseconds(800us).count(); struct RefreshRate { // The tolerance within which we consider FPS approximately equals. static constexpr float FPS_EPSILON = 0.001f; Loading Loading @@ -123,13 +128,15 @@ public: bool operator!=(const LayerRequirement& other) const { return !(*this == other); } }; // Returns all available refresh rates according to the current policy. // Returns the refresh rate that fits best to the given layers. const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const EXCLUDES(mLock); // Returns all available refresh rates according to the current policy. const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers) const EXCLUDES(mLock); // Returns the refresh rate that fits best to the given layers. This function also gets a // boolean flag that indicates whether user touched the screen recently to be factored in when // choosing the refresh rate. const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers, bool touchActive) const EXCLUDES(mLock); // Returns all the refresh rates supported by the device. This won't change at runtime. const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock); Loading Loading @@ -188,6 +195,10 @@ private: template <typename Iter> const RefreshRate* getBestRefreshRate(Iter begin, Iter end) const; // Returns number of display frames and remainder when dividing the layer refresh period by // display refresh period. std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const; // The list of refresh rates, indexed by display config ID. This must not change after this // object is initialized. AllRefreshRatesMapType mRefreshRates; Loading Loading
services/surfaceflinger/Scheduler/LayerHistory.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -97,6 +97,7 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) { } LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { ATRACE_CALL(); std::lock_guard lock(mLock); partitionLayers(now); Loading
services/surfaceflinger/Scheduler/LayerInfoV2.cpp +22 −8 Original line number Diff line number Diff line Loading @@ -61,21 +61,35 @@ bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const { } bool LayerInfoV2::isFrequent(nsecs_t now) const { // Find the first valid frame time auto it = mFrameTimes.begin(); for (; it != mFrameTimes.end(); ++it) { if (isFrameTimeValid(*it)) { break; } } // If we know nothing about this layer we consider it as frequent as it might be the start // of an animation. if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) { if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) { return true; } // Layer is frequent if the earliest value in the window of most recent present times is // within threshold. const auto it = mFrameTimes.end() - FREQUENT_LAYER_WINDOW_SIZE; if (!isFrameTimeValid(*it)) { return true; // Find the first active frame for (; it != mFrameTimes.end(); ++it) { if (it->queueTime >= getActiveLayerThreshold(now)) { break; } } const auto numFrames = std::distance(it, mFrameTimes.end()) - 1; if (numFrames <= 0) { return false; } const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count(); return it->queueTime >= threshold; // Layer is considered frequent if the average frame rate is higher than the threshold const auto totalTime = mFrameTimes.back().queueTime - it->queueTime; return (1e9f * numFrames) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER; } bool LayerInfoV2::hasEnoughDataForHeuristic() const { Loading
services/surfaceflinger/Scheduler/LayerInfoV2.h +3 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,9 @@ class LayerInfoV2 { // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in // favor of a low refresh rate. static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3; static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms; static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f; static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms; friend class LayerHistoryTestV2; Loading
services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +87 −59 Original line number Diff line number Diff line Loading @@ -23,8 +23,6 @@ #include <chrono> #include <cmath> using namespace std::chrono_literals; namespace android::scheduler { using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType; Loading Loading @@ -84,14 +82,31 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContent( return *bestSoFar; } std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const { auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod); if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION || std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) { displayFramesQuot++; displayFramesRem = 0; } return {displayFramesQuot, displayFramesRem}; } const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2( const std::vector<LayerRequirement>& layers) const { constexpr nsecs_t MARGIN = std::chrono::nanoseconds(800us).count(); const std::vector<LayerRequirement>& layers, bool touchActive) const { ATRACE_CALL(); ALOGV("getRefreshRateForContent %zu layers", layers.size()); std::lock_guard lock(mLock); // For now if the touch is active return the peak refresh rate // This should be optimized to consider other layers as well. if (touchActive) { return *mAvailableRefreshRates.back(); } int noVoteLayers = 0; int minVoteLayers = 0; int maxVoteLayers = 0; Loading @@ -115,11 +130,6 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2( return *mAvailableRefreshRates.front(); } // If we have some Max layers and no Explicit we should return Max if (maxVoteLayers > 0 && explicitDefaultVoteLayers + explicitExactOrMultipleVoteLayers == 0) { return *mAvailableRefreshRates.back(); } // Find the best refresh rate based on score std::vector<std::pair<const RefreshRate*, float>> scores; scores.reserve(mAvailableRefreshRates.size()); Loading @@ -130,67 +140,85 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2( for (const auto& layer : layers) { ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote); if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min || layer.vote == LayerVoteType::Max) { if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) { continue; } // Adjust the weight in case we have explicit layers. The priority is: // - ExplicitExactOrMultiple // - ExplicitDefault // - Heuristic auto weight = layer.weight; if (explicitExactOrMultipleVoteLayers + explicitDefaultVoteLayers > 0) { if (layer.vote == LayerVoteType::Heuristic) { weight /= 2.f; } } if (explicitExactOrMultipleVoteLayers > 0) { if (layer.vote == LayerVoteType::Heuristic || layer.vote == LayerVoteType::ExplicitDefault) { weight /= 2.f; } for (auto i = 0u; i < scores.size(); i++) { // If the layer wants Max, give higher score to the higher refresh rate if (layer.vote == LayerVoteType::Max) { const auto ratio = scores[i].first->fps / scores.back().first->fps; // use ratio^2 to get a lower score the more we get further from peak const auto layerScore = ratio * ratio; ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } for (auto& [refreshRate, overallScore] : scores) { const auto displayPeriod = refreshRate->vsyncPeriod; const auto displayPeriod = scores[i].first->vsyncPeriod; const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate); if (layer.vote == LayerVoteType::ExplicitDefault) { const auto layerScore = [&]() { const auto [displayFramesQuot, displayFramesRem] = getDisplayFrames(layerPeriod, displayPeriod); if (displayFramesQuot == 0) { // Layer desired refresh rate is higher the display rate. return static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod); } // Calculate how many display vsyncs we need to present a single frame for this layer auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod); if (displayFramesRem <= MARGIN || std::abs(displayFramesRem - displayPeriod) <= MARGIN) { displayFramesQuot++; displayFramesRem = 0; return 1.0f - (static_cast<float>(displayFramesRem) / static_cast<float>(layerPeriod)); }(); ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } float layerScore; static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1 if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || layer.vote == LayerVoteType::Heuristic) { const auto layerScore = [&]() { // Calculate how many display vsyncs we need to present a single frame for this // layer const auto [displayFramesQuot, displayFramesRem] = getDisplayFrames(layerPeriod, displayPeriod); static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1 if (displayFramesRem == 0) { // Layer desired refresh rate matches the display rate. layerScore = weight * 1.0f; } else if (displayFramesQuot == 0) { return 1.0f; } if (displayFramesQuot == 0) { // Layer desired refresh rate is higher the display rate. layerScore = weight * (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) * return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) * (1.0f / (MAX_FRAMES_TO_FIT + 1)); } else { // Layer desired refresh rate is lower the display rate. Check how well it fits the // cadence } // Layer desired refresh rate is lower the display rate. Check how well it fits // the cadence auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem)); int iter = 2; while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) { while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) { diff = diff - (displayPeriod - diff); iter++; } layerScore = weight * (1.0f / iter); return 1.0f / iter; }(); ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight, 1e9f / layerPeriod, refreshRate->name.c_str(), layerScore); overallScore += layerScore; } } Loading
services/surfaceflinger/Scheduler/RefreshRateConfigs.h +15 −4 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include "Scheduler/StrongTyping.h" namespace android::scheduler { using namespace std::chrono_literals; enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 }; Loading @@ -43,6 +44,10 @@ inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateC */ class RefreshRateConfigs { public: // Margin used when matching refresh rates to the content desired ones. static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION = std::chrono::nanoseconds(800us).count(); struct RefreshRate { // The tolerance within which we consider FPS approximately equals. static constexpr float FPS_EPSILON = 0.001f; Loading Loading @@ -123,13 +128,15 @@ public: bool operator!=(const LayerRequirement& other) const { return !(*this == other); } }; // Returns all available refresh rates according to the current policy. // Returns the refresh rate that fits best to the given layers. const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const EXCLUDES(mLock); // Returns all available refresh rates according to the current policy. const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers) const EXCLUDES(mLock); // Returns the refresh rate that fits best to the given layers. This function also gets a // boolean flag that indicates whether user touched the screen recently to be factored in when // choosing the refresh rate. const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers, bool touchActive) const EXCLUDES(mLock); // Returns all the refresh rates supported by the device. This won't change at runtime. const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock); Loading Loading @@ -188,6 +195,10 @@ private: template <typename Iter> const RefreshRate* getBestRefreshRate(Iter begin, Iter end) const; // Returns number of display frames and remainder when dividing the layer refresh period by // display refresh period. std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const; // The list of refresh rates, indexed by display config ID. This must not change after this // object is initialized. AllRefreshRatesMapType mRefreshRates; Loading