diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp index b3d157cf9..61693d91f 100644 --- a/indra/llmessage/aicurlperservice.cpp +++ b/indra/llmessage/aicurlperservice.cpp @@ -75,7 +75,9 @@ AIPerService::AIPerService(void) : mConcurrectConnections(CurlConcurrentConnectionsPerService), mTotalAdded(0), mApprovedFirst(0), - mUnapprovedFirst(0) + mUnapprovedFirst(0), + mUsedCT(0), + mCTInUse(0) { } @@ -279,6 +281,53 @@ void AIPerService::release(AIPerServicePtr& instance) instance.reset(); } +void AIPerService::redivide_connections(void) +{ + // Priority order. + static AICapabilityType order[number_of_capability_types] = { cap_inventory, cap_texture, cap_mesh, cap_other }; + // Count the number of capability types that are currently in use and store the types in an array. + AICapabilityType used_order[number_of_capability_types]; + int number_of_capability_types_in_use = 0; + for (int i = 0; i < number_of_capability_types; ++i) + { + U32 const mask = CT2mask(order[i]); + if ((mCTInUse & mask)) + { + used_order[number_of_capability_types_in_use++] = order[i]; + } + else + { + // Give every other type (that is not in use) one connection, so they can be used (at which point they'll get more). + mCapabilityType[order[i]].mConcurrectConnections = 1; + } + } + // Keep one connection in reserve for currently unused capability types (that have been used before). + int reserve = (mUsedCT != mCTInUse) ? 1 : 0; + // Distribute (mConcurrectConnections - reserve) over number_of_capability_types_in_use. + U16 max_connections_per_CT = (mConcurrectConnections - reserve) / number_of_capability_types_in_use + 1; + // The first count CTs get max_connections_per_CT connections. + int count = (mConcurrectConnections - reserve) % number_of_capability_types_in_use; + for(int i = 1, j = 0;; --i) + { + while (j < count) + { + mCapabilityType[used_order[j++]].mConcurrectConnections = max_connections_per_CT; + } + if (i == 0) + { + break; + } + // Finish the loop till all used CTs are assigned. + count = number_of_capability_types_in_use; + // Never assign 0 as maximum. + if (max_connections_per_CT > 1) + { + // The remaining CTs get one connection less so that the sum of all assigned connections is mConcurrectConnections - reserve. + --max_connections_per_CT; + } + } +} + bool AIPerService::throttled(AICapabilityType capability_type) const { return mTotalAdded >= mConcurrectConnections || @@ -293,14 +342,19 @@ void AIPerService::added_to_multi_handle(AICapabilityType capability_type) void AIPerService::removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something) { - llassert(mTotalAdded > 0 && mCapabilityType[capability_type].mAdded > 0); - --mCapabilityType[capability_type].mAdded; + CapabilityType& ct(mCapabilityType[capability_type]); + llassert(mTotalAdded > 0 && ct.mAdded > 0); + bool done = --ct.mAdded == 0; if (downloaded_something) { - llassert(mCapabilityType[capability_type].mDownloading > 0); - --mCapabilityType[capability_type].mDownloading; + llassert(ct.mDownloading > 0); + --ct.mDownloading; } --mTotalAdded; + if (done && ct.pipelined_requests() == 0) + { + mark_unused(capability_type); + } } // Returns true if the request was queued. @@ -489,6 +543,11 @@ void AIPerService::adjust_concurrent_connections(int increment) } } +void AIPerService::ResetUsed::operator()(AIPerService::instance_map_type::value_type const& service) const +{ + PerService_wat(*service.second)->resetUsedCt(); +} + void AIPerService::Approvement::honored(void) { if (!mHonored) diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index 85885a285..b8d8169ea 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -163,6 +163,48 @@ class AIPerService { int mApprovedFirst; // First capability type to try. int mUnapprovedFirst; // First capability type to try after all approved types were tried. + U32 mUsedCT; // Bit mask with one bit per capability type. A '1' means the capability was in use since the last resetUsedCT(). + U32 mCTInUse; // Bit mask with one bit per capability type. A '1' means the capability is in use right now. + + // Helper struct, used in the static resetUsed. + struct ResetUsed { void operator()(instance_map_type::value_type const& service) const; }; + + void redivide_connections(void); + void mark_inuse(AICapabilityType capability_type) + { + U32 bit = CT2mask(capability_type); + if ((mCTInUse & bit) == 0) // If this CT went from unused to used + { + mCTInUse |= bit; + mUsedCT |= bit; + if (mUsedCT != bit) // and more than one CT use this service. + { + redivide_connections(); + } + } + } + void mark_unused(AICapabilityType capability_type) + { + U32 bit = CT2mask(capability_type); + if ((mCTInUse & bit) != 0) // If this CT went from used to unused + { + mCTInUse &= ~bit; + if (mCTInUse && mUsedCT != bit) // and more than one CT use this service, and at least one is in use. + { + redivide_connections(); + } + } + } + public: + static U32 CT2mask(AICapabilityType capability_type) { return (U32)1 << capability_type; } + void resetUsedCt(void) { mUsedCT = mCTInUse; } + bool is_used(AICapabilityType capability_type) const { return (mUsedCT & CT2mask(capability_type)); } + bool is_inuse(AICapabilityType capability_type) const { return (mCTInUse & CT2mask(capability_type)); } + + static void resetUsed(void) { copy_forEach(ResetUsed()); } + U32 is_used(void) const { return mUsedCT; } // Non-zero if this service was used for any capability type. + U32 is_inuse(void) const { return mCTInUse; } // Non-zero if this service is in use for any capability type. + // Global administration of the total number of queued requests of all services combined. private: struct TotalQueued { @@ -212,7 +254,7 @@ class AIPerService { static bool sNoHTTPBandwidthThrottling; // Global override to disable bandwidth throttling. public: - void added_to_command_queue(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mQueuedCommands; } + void added_to_command_queue(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mQueuedCommands; mark_inuse(capability_type); } void removed_from_command_queue(AICapabilityType capability_type) { --mCapabilityType[capability_type].mQueuedCommands; llassert(mCapabilityType[capability_type].mQueuedCommands >= 0); } void added_to_multi_handle(AICapabilityType capability_type); // Called when an easy handle for this service has been added to the multi handle. void removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something); // Called when an easy handle for this service is removed again from the multi handle. diff --git a/indra/newview/aihttpview.cpp b/indra/newview/aihttpview.cpp index 522276cb5..aa02aa0a9 100644 --- a/indra/newview/aihttpview.cpp +++ b/indra/newview/aihttpview.cpp @@ -76,12 +76,32 @@ void AIServiceBar::draw() LLFontGL::getFontMonospace()->renderUTF8(mName, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(mName); std::string text; - PerService_rat per_service_r(*mPerService); + AIPerService::CapabilityType const* cts; + U32 is_used; + U32 is_inuse; + int total_added; + int concurrent_connections; + size_t bandwidth; + { + PerService_rat per_service_r(*mPerService); + is_used = per_service_r->is_used(); + is_inuse = per_service_r->is_inuse(); + total_added = per_service_r->mTotalAdded; + concurrent_connections = per_service_r->mConcurrectConnections; + bandwidth = per_service_r->bandwidth().truncateData(AIHTTPView::getTime_40ms()); + cts = per_service_r->mCapabilityType; // Not thread-safe, but we're only reading from it and only using the results to show in a debug console. + } for (int col = 0; col < number_of_capability_types; ++col) { - AIPerService::CapabilityType& ct(per_service_r->mCapabilityType[col]); + AICapabilityType capability_type = static_cast(col); + AIPerService::CapabilityType const& ct(cts[capability_type]); start = mHTTPView->updateColumn(col, start); - if (col < 2) + U32 mask = AIPerService::CT2mask(capability_type); + if (!(is_used & mask)) + { + text = " | "; + } + else if (col < 2) { text = llformat(" | %hu-%hu-%lu,{%hu/%hu,%u}/%u", ct.mApprovedRequests, ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrectConnections, ct.mDownloading, ct.mMaxPipelinedRequests); } @@ -89,15 +109,14 @@ void AIServiceBar::draw() { text = llformat(" | --%hu-%lu,{%hu/%hu,%u}", ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrectConnections, ct.mDownloading); } - LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, ((is_inuse & mask) == 0) ? LLColor4::grey : text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); } start = mHTTPView->updateColumn(mc_col, start); - text = llformat(" | %d/%d", per_service_r->mTotalAdded, per_service_r->mConcurrectConnections); + text = llformat(" | %d/%d", total_added, concurrent_connections); LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); start = mHTTPView->updateColumn(bw_col, start); - size_t bandwidth = per_service_r->bandwidth().truncateData(AIHTTPView::getTime_40ms()); size_t max_bandwidth = mHTTPView->mMaxBandwidthPerService; text = " | "; LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); @@ -107,7 +126,7 @@ void AIServiceBar::draw() LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); text = llformat("/%lu", max_bandwidth / 125); - LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, (bandwidth == 0) ? LLColor4::grey : text_color, LLFontGL::LEFT, LLFontGL::TOP); } LLRect AIServiceBar::getRequiredRect(void) @@ -241,6 +260,18 @@ U32 AIHTTPView::updateColumn(int col, U32 start) return mStartColumn[col]; } +//static +void AIHTTPView::toggle_visibility(void* user_data) +{ + LLView* viewp = (LLView*)user_data; + bool visible = !viewp->getVisible(); + if (visible) + { + AIPerService::resetUsed(); + } + viewp->setVisible(visible); +} + U64 AIHTTPView::sTime_40ms; struct KillView @@ -260,6 +291,8 @@ struct CreateServiceBar void operator()(AIPerService::instance_map_type::value_type const& service) { + if (!PerService_rat(*service.second)->is_used()) + return; AIServiceBar* service_bar = new AIServiceBar(mHTTPView, service); mHTTPView->addChild(service_bar); mHTTPView->mServiceBars.push_back(service_bar); diff --git a/indra/newview/aihttpview.h b/indra/newview/aihttpview.h index f63bc5a61..767d37e54 100644 --- a/indra/newview/aihttpview.h +++ b/indra/newview/aihttpview.h @@ -66,6 +66,7 @@ class AIHTTPView : public LLContainerView public: static U64 getTime_40ms(void) { return sTime_40ms; } + static void toggle_visibility(void* user_data); }; extern AIHTTPView *gHttpView; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index d9582da4f..572648025 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -169,6 +169,7 @@ #include "llfloatermessagelog.h" #include "shfloatermediaticker.h" #include "llpacketring.h" +#include "aihttpview.h" // #include "scriptcounter.h" @@ -818,7 +819,7 @@ void init_client_menu(LLMenuGL* menu) } sub->addChild(new LLMenuItemCheckGL("HTTP Console", - &toggle_visibility, + &AIHTTPView::toggle_visibility, NULL, &get_visibility, (void*)gHttpView,