diff options
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llappearance/llavatarappearance.cpp | 1 | ||||
| -rw-r--r-- | indra/llmessage/llpacketbuffer.cpp | 26 | ||||
| -rw-r--r-- | indra/llmessage/llpacketbuffer.h | 12 | ||||
| -rw-r--r-- | indra/llmessage/llpacketring.cpp | 444 | ||||
| -rw-r--r-- | indra/llmessage/llpacketring.h | 79 | ||||
| -rw-r--r-- | indra/llmessage/message.cpp | 13 | ||||
| -rw-r--r-- | indra/llmessage/message.h | 3 | ||||
| -rw-r--r-- | indra/llmessage/net.cpp | 10 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 82 | ||||
| -rw-r--r-- | indra/newview/llappviewer.h | 3 | ||||
| -rw-r--r-- | indra/newview/llmeshrepository.cpp | 1205 | ||||
| -rw-r--r-- | indra/newview/llmeshrepository.h | 211 | ||||
| -rw-r--r-- | indra/newview/llstartup.cpp | 287 | ||||
| -rw-r--r-- | indra/newview/lltextureview.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llviewermessage.cpp | 35 | ||||
| -rw-r--r-- | indra/newview/llviewertexturelist.cpp | 14 | ||||
| -rw-r--r-- | indra/newview/llvoavatar.cpp | 135 | ||||
| -rw-r--r-- | indra/newview/llvoavatar.h | 1 | 
18 files changed, 1655 insertions, 908 deletions
| diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp index 95d55c835f..3d66809ed6 100644 --- a/indra/llappearance/llavatarappearance.cpp +++ b/indra/llappearance/llavatarappearance.cpp @@ -799,7 +799,6 @@ void LLAvatarAppearance::buildCharacter()      bool status = loadAvatar();      stop_glerror(); -//  gPrintMessagesThisFrame = true;      LL_DEBUGS() << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << LL_ENDL;      if (!status) diff --git a/indra/llmessage/llpacketbuffer.cpp b/indra/llmessage/llpacketbuffer.cpp index dc5c7a73cb..0b04a560be 100644 --- a/indra/llmessage/llpacketbuffer.cpp +++ b/indra/llmessage/llpacketbuffer.cpp @@ -32,8 +32,6 @@  #include "lltimer.h"  #include "llhost.h" -/////////////////////////////////////////////////////////// -  LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32 size) : mHost(host)  {      mSize = 0; @@ -41,7 +39,7 @@ LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32      if (size > NET_BUFFER_SIZE)      { -        LL_ERRS() << "Sending packet > " << NET_BUFFER_SIZE << " of size " << size << LL_ENDL; +        LL_ERRS() << "Constructing packet with size=" << size << " > " << NET_BUFFER_SIZE << LL_ENDL;      }      else      { @@ -51,7 +49,6 @@ LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32              mSize = size;          }      } -  }  LLPacketBuffer::LLPacketBuffer (S32 hSocket) @@ -59,18 +56,29 @@ LLPacketBuffer::LLPacketBuffer (S32 hSocket)      init(hSocket);  } -/////////////////////////////////////////////////////////// -  LLPacketBuffer::~LLPacketBuffer ()  {  } -/////////////////////////////////////////////////////////// - -void LLPacketBuffer::init (S32 hSocket) +void LLPacketBuffer::init(S32 hSocket)  {      mSize = receive_packet(hSocket, mData);      mHost = ::get_sender();      mReceivingIF = ::get_receiving_interface();  } +void LLPacketBuffer::init(const char* buffer, S32 data_size, const LLHost& host) +{ +    if (data_size > NET_BUFFER_SIZE) +    { +        LL_ERRS() << "Initializing packet with size=" << data_size << " > " << NET_BUFFER_SIZE << LL_ENDL; +    } +    else +    { +        memcpy(mData, buffer, data_size); +        mSize = data_size; +        mHost = host; +        mReceivingIF = ::get_receiving_interface(); +    } +} + diff --git a/indra/llmessage/llpacketbuffer.h b/indra/llmessage/llpacketbuffer.h index a2d2973fb0..ac4012d330 100644 --- a/indra/llmessage/llpacketbuffer.h +++ b/indra/llmessage/llpacketbuffer.h @@ -35,20 +35,22 @@ class LLPacketBuffer  {  public:      LLPacketBuffer(const LLHost &host, const char *datap, const S32 size); -    LLPacketBuffer(S32 hSocket);           // receive a packet +    LLPacketBuffer(S32 hSocket);    // receive a packet      ~LLPacketBuffer();      S32         getSize() const                 { return mSize; }      const char  *getData() const                { return mData; }      LLHost      getHost() const                 { return mHost; }      LLHost      getReceivingInterface() const   { return mReceivingIF; } +      void init(S32 hSocket); +    void init(const char* buffer, S32 data_size, const LLHost& host);  protected: -    char    mData[NET_BUFFER_SIZE];        // packet data       /* Flawfinder : ignore */ -    S32     mSize;          // size of buffer in bytes -    LLHost  mHost;         // source/dest IP and port -    LLHost  mReceivingIF;         // source/dest IP and port +    char    mData[NET_BUFFER_SIZE]; // packet data       /* Flawfinder : ignore */ +    S32     mSize;                  // size of buffer in bytes +    LLHost  mHost;                  // source/dest IP and port +    LLHost  mReceivingIF;           // source/dest IP and port  };  #endif diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp index be838770a8..ae5a2168db 100644 --- a/indra/llmessage/llpacketring.cpp +++ b/indra/llmessage/llpacketring.cpp @@ -1,6 +1,6 @@  /**   * @file llpacketring.cpp - * @brief implementation of LLPacketRing class for a packet. + * @brief implementation of LLPacketRing class.   *   * $LicenseInfo:firstyear=2001&license=viewerlgpl$   * Second Life Viewer Source Code @@ -43,329 +43,301 @@  #include "message.h"  #include "u64.h" -/////////////////////////////////////////////////////////// -LLPacketRing::LLPacketRing () : -    mUseInThrottle(false), -    mUseOutThrottle(false), -    mInThrottle(256000.f), -    mOutThrottle(64000.f), -    mActualBitsIn(0), -    mActualBitsOut(0), -    mMaxBufferLength(64000), -    mInBufferLength(0), -    mOutBufferLength(0), -    mDropPercentage(0.0f), -    mPacketsToDrop(0x0) +constexpr S16 MAX_BUFFER_RING_SIZE = 1024; +constexpr S16 DEFAULT_BUFFER_RING_SIZE = 256; + +LLPacketRing::LLPacketRing () +    : mPacketRing(DEFAULT_BUFFER_RING_SIZE, nullptr)  { +    LLHost invalid_host; +    for (size_t i = 0; i < mPacketRing.size(); ++i) +    { +        mPacketRing[i] = new LLPacketBuffer(invalid_host, nullptr, 0); +    }  } -///////////////////////////////////////////////////////////  LLPacketRing::~LLPacketRing ()  { -    cleanup(); +    for (auto packet : mPacketRing) +    { +        delete packet; +    } +    mPacketRing.clear(); +    mNumBufferedPackets = 0; +    mNumBufferedBytes = 0; +    mHeadIndex = 0;  } -/////////////////////////////////////////////////////////// -void LLPacketRing::cleanup () +S32 LLPacketRing::receivePacket (S32 socket, char *datap)  { -    LLPacketBuffer *packetp; +    bool drop = computeDrop(); +    return (mNumBufferedPackets > 0) ? +        receiveOrDropBufferedPacket(datap, drop) : +        receiveOrDropPacket(socket, datap, drop); +} -    while (!mReceiveQueue.empty()) +bool send_packet_helper(int socket, const char * datap, S32 data_size, LLHost host) +{ +    if (!LLProxy::isSOCKSProxyEnabled())      { -        packetp = mReceiveQueue.front(); -        delete packetp; -        mReceiveQueue.pop(); +        return send_packet(socket, datap, data_size, host.getAddress(), host.getPort());      } -    while (!mSendQueue.empty()) -    { -        packetp = mSendQueue.front(); -        delete packetp; -        mSendQueue.pop(); -    } -} +    char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; -/////////////////////////////////////////////////////////// -void LLPacketRing::dropPackets (U32 num_to_drop) -{ -    mPacketsToDrop += num_to_drop; -} +    proxywrap_t *socks_header = static_cast<proxywrap_t*>(static_cast<void*>(&headered_send_buffer)); +    socks_header->rsv   = 0; +    socks_header->addr  = host.getAddress(); +    socks_header->port  = htons(host.getPort()); +    socks_header->atype = ADDRESS_IPV4; +    socks_header->frag  = 0; -/////////////////////////////////////////////////////////// -void LLPacketRing::setDropPercentage (F32 percent_to_drop) -{ -    mDropPercentage = percent_to_drop; -} +    memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, datap, data_size); -void LLPacketRing::setUseInThrottle(const bool use_throttle) -{ -    mUseInThrottle = use_throttle; +    return send_packet( socket, +                        headered_send_buffer, +                        data_size + SOCKS_HEADER_SIZE, +                        LLProxy::getInstance()->getUDPProxy().getAddress(), +                        LLProxy::getInstance()->getUDPProxy().getPort());  } -void LLPacketRing::setUseOutThrottle(const bool use_throttle) +bool LLPacketRing::sendPacket(int socket, const char * datap, S32 data_size, LLHost host)  { -    mUseOutThrottle = use_throttle; +    mActualBytesOut += data_size; +    return send_packet_helper(socket, datap, data_size, host);  } -void LLPacketRing::setInBandwidth(const F32 bps) +void LLPacketRing::dropPackets (U32 num_to_drop)  { -    mInThrottle.setRate(bps); +    mPacketsToDrop += num_to_drop;  } -void LLPacketRing::setOutBandwidth(const F32 bps) +void LLPacketRing::setDropPercentage (F32 percent_to_drop)  { -    mOutThrottle.setRate(bps); +    mDropPercentage = percent_to_drop;  } -/////////////////////////////////////////////////////////// -S32 LLPacketRing::receiveFromRing (S32 socket, char *datap) -{ -    if (mInThrottle.checkOverflow(0)) -    { -        // We don't have enough bandwidth, don't give them a packet. -        return 0; -    } - -    LLPacketBuffer *packetp = NULL; -    if (mReceiveQueue.empty()) +bool LLPacketRing::computeDrop() +{ +    bool drop= (mDropPercentage > 0.0f && (ll_frand(100.f) < mDropPercentage)); +    if (drop)      { -        // No packets on the queue, don't give them any. -        return 0; +        ++mPacketsToDrop;      } - -    S32 packet_size = 0; -    packetp = mReceiveQueue.front(); -    mReceiveQueue.pop(); -    packet_size = packetp->getSize(); -    if (packetp->getData() != NULL) +    if (mPacketsToDrop > 0)      { -        memcpy(datap, packetp->getData(), packet_size); /*Flawfinder: ignore*/ +        --mPacketsToDrop; +        drop = true;      } -    // need to set sender IP/port!! -    mLastSender = packetp->getHost(); -    mLastReceivingIF = packetp->getReceivingInterface(); -    delete packetp; - -    this->mInBufferLength -= packet_size; - -    // Adjust the throttle -    mInThrottle.throttleOverflow(packet_size * 8.f); -    return packet_size; +    return drop;  } -/////////////////////////////////////////////////////////// -S32 LLPacketRing::receivePacket (S32 socket, char *datap) +S32 LLPacketRing::receiveOrDropPacket(S32 socket, char *datap, bool drop)  {      S32 packet_size = 0; -    // If using the throttle, simulate a limited size input buffer. -    if (mUseInThrottle) +    // pull straight from socket +    if (LLProxy::isSOCKSProxyEnabled())      { -        bool done = false; - -        // push any current net packet (if any) onto delay ring -        while (!done) +        char buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];   /* Flawfinder ignore */ +        packet_size = receive_packet(socket, buffer); +        if (packet_size > 0)          { -            LLPacketBuffer *packetp; -            packetp = new LLPacketBuffer(socket); - -            if (packetp->getSize()) -            { -                mActualBitsIn += packetp->getSize() * 8; - -                // Fake packet loss -                if (mDropPercentage && (ll_frand(100.f) < mDropPercentage)) -                { -                    mPacketsToDrop++; -                } - -                if (mPacketsToDrop) -                { -                    delete packetp; -                    packetp = NULL; -                    packet_size = 0; -                    mPacketsToDrop--; -                } -            } +            mActualBytesIn += packet_size; +        } -            // If we faked packet loss, then we don't have a packet -            // to use for buffer overflow testing -            if (packetp) +        if (packet_size > SOCKS_HEADER_SIZE) +        { +            if (drop)              { -                if (mInBufferLength + packetp->getSize() > mMaxBufferLength) -                { -                    // Toss it. -                    LL_WARNS() << "Throwing away packet, overflowing buffer" << LL_ENDL; -                    delete packetp; -                    packetp = NULL; -                } -                else if (packetp->getSize()) -                { -                    mReceiveQueue.push(packetp); -                    mInBufferLength += packetp->getSize(); -                } -                else -                { -                    delete packetp; -                    packetp = NULL; -                    done = true; -                } +                packet_size = 0;              }              else              { -                // No packetp, keep going? - no packetp == faked packet loss -            } -        } - -        // Now, grab data off of the receive queue according to our -        // throttled bandwidth settings. -        packet_size = receiveFromRing(socket, datap); -    } -    else -    { -        // no delay, pull straight from net -        if (LLProxy::isSOCKSProxyEnabled()) -        { -            U8 buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; -            packet_size = receive_packet(socket, static_cast<char*>(static_cast<void*>(buffer))); - -            if (packet_size > SOCKS_HEADER_SIZE) -            {                  // *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6) -                memcpy(datap, buffer + SOCKS_HEADER_SIZE, packet_size - SOCKS_HEADER_SIZE); +                packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size +                memcpy(datap, buffer + SOCKS_HEADER_SIZE, packet_size);                  proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer));                  mLastSender.setAddress(header->addr);                  mLastSender.setPort(ntohs(header->port)); - -                packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size -            } -            else -            { -                packet_size = 0; +                mLastReceivingIF = ::get_receiving_interface();              }          }          else          { -            packet_size = receive_packet(socket, datap); -            mLastSender = ::get_sender(); +            packet_size = 0;          } - -        mLastReceivingIF = ::get_receiving_interface(); - -        if (packet_size)  // did we actually get a packet? +    } +    else +    { +        packet_size = receive_packet(socket, datap); +        if (packet_size > 0)          { -            if (mDropPercentage && (ll_frand(100.f) < mDropPercentage)) +            mActualBytesIn += packet_size; +            if (drop)              { -                mPacketsToDrop++; +                packet_size = 0;              } - -            if (mPacketsToDrop) +            else              { -                packet_size = 0; -                mPacketsToDrop--; +                mLastSender = ::get_sender(); +                mLastReceivingIF = ::get_receiving_interface();              }          }      } -      return packet_size;  } -bool LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host) +S32 LLPacketRing::receiveOrDropBufferedPacket(char *datap, bool drop)  { -    bool status = true; -    if (!mUseOutThrottle) +    assert(mNumBufferedPackets > 0); +    S32 packet_size = 0; + +    S16 ring_size = (S16)(mPacketRing.size()); +    S16 packet_index = (mHeadIndex + ring_size - mNumBufferedPackets) % ring_size; +    LLPacketBuffer* packet = mPacketRing[packet_index]; +    packet_size = packet->getSize(); +    mLastSender = packet->getHost(); +    mLastReceivingIF = packet->getReceivingInterface(); + +    --mNumBufferedPackets; +    mNumBufferedBytes -= packet_size; +    if (mNumBufferedPackets == 0)      { -        return sendPacketImpl(h_socket, send_buffer, buf_size, host ); +        assert(mNumBufferedBytes == 0); +    } + +    if (!drop) +    { +        assert(packet_size > 0); +        memcpy(datap, packet->getData(), packet_size);      }      else      { -        mActualBitsOut += buf_size * 8; -        LLPacketBuffer *packetp = NULL; -        // See if we've got enough throttle to send a packet. -        while (!mOutThrottle.checkOverflow(0.f)) -        { -            // While we have enough bandwidth, send a packet from the queue or the current packet +        packet_size = 0; +    } +    return packet_size; +} + +S32 LLPacketRing::bufferInboundPacket(S32 socket) +{ +    if (mNumBufferedPackets == mPacketRing.size() && mNumBufferedPackets < MAX_BUFFER_RING_SIZE) +    { +        expandRing(); +    } -            S32 packet_size = 0; -            if (!mSendQueue.empty()) +    LLPacketBuffer* packet = mPacketRing[mHeadIndex]; +    S32 old_packet_size = packet->getSize(); +    S32 packet_size = 0; +    if (LLProxy::isSOCKSProxyEnabled()) +    { +        char buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];   /* Flawfinder ignore */ +        packet_size = receive_packet(socket, buffer); +        if (packet_size > 0) +        { +            mActualBytesIn += packet_size; +            if (packet_size > SOCKS_HEADER_SIZE)              { -                // Send a packet off of the queue -                LLPacketBuffer *packetp = mSendQueue.front(); -                mSendQueue.pop(); +                // *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6) -                mOutBufferLength -= packetp->getSize(); -                packet_size = packetp->getSize(); +                proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer)); +                LLHost sender; +                sender.setAddress(header->addr); +                sender.setPort(ntohs(header->port)); -                status = sendPacketImpl(h_socket, packetp->getData(), packet_size, packetp->getHost()); +                packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size +                packet->init(buffer + SOCKS_HEADER_SIZE, packet_size, sender); -                delete packetp; -                // Update the throttle -                mOutThrottle.throttleOverflow(packet_size * 8.f); +                mHeadIndex = (mHeadIndex + 1) % (S16)(mPacketRing.size()); +                if (mNumBufferedPackets < MAX_BUFFER_RING_SIZE) +                { +                    ++mNumBufferedPackets; +                    mNumBufferedBytes += packet_size; +                } +                else +                { +                    // we overwrote an older packet +                    mNumBufferedBytes += packet_size - old_packet_size; +                }              }              else              { -                // If the queue's empty, we can just send this packet right away. -                status =  sendPacketImpl(h_socket, send_buffer, buf_size, host ); -                packet_size = buf_size; - -                // Update the throttle -                mOutThrottle.throttleOverflow(packet_size * 8.f); - -                // This was the packet we're sending now, there are no other packets -                // that we need to send -                return status; +                packet_size = 0;              } - -        } - -        // We haven't sent the incoming packet, add it to the queue -        if (mOutBufferLength + buf_size > mMaxBufferLength) -        { -            // Nuke this packet, we overflowed the buffer. -            // Toss it. -            LL_WARNS() << "Throwing away outbound packet, overflowing buffer" << LL_ENDL;          } -        else +    } +    else +    { +        packet->init(socket); +        packet_size = packet->getSize(); +        if (packet_size > 0)          { -            static LLTimer queue_timer; -            if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f) +            mActualBytesIn += packet_size; + +            mHeadIndex = (mHeadIndex + 1) % (S16)(mPacketRing.size()); +            if (mNumBufferedPackets < MAX_BUFFER_RING_SIZE)              { -                // Add it to the queue -                LL_INFOS() << "Outbound packet queue " << mOutBufferLength << " bytes" << LL_ENDL; -                queue_timer.reset(); +                ++mNumBufferedPackets; +                mNumBufferedBytes += packet_size; +            } +            else +            { +                // we overwrote an older packet +                mNumBufferedBytes += packet_size - old_packet_size;              } -            packetp = new LLPacketBuffer(host, send_buffer, buf_size); - -            mOutBufferLength += packetp->getSize(); -            mSendQueue.push(packetp);          }      } - -    return status; +    return packet_size;  } -bool LLPacketRing::sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host) +S32 LLPacketRing::drainSocket(S32 socket)  { - -    if (!LLProxy::isSOCKSProxyEnabled()) +    // drain into buffer +    S32 packet_size = 1; +    S32 num_loops = 0; +    S32 old_num_packets = mNumBufferedPackets; +    while (packet_size > 0)      { -        return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort()); +        packet_size = bufferInboundPacket(socket); +        ++num_loops;      } +    S32 num_dropped_packets = (num_loops - 1 + old_num_packets) - mNumBufferedPackets; +    if (num_dropped_packets > 0) +    { +        LL_WARNS("Messaging") << "dropped " << num_dropped_packets << " UDP packets" << LL_ENDL; +    } +    return (S32)(mNumBufferedPackets); +} -    char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE]; +bool LLPacketRing::expandRing() +{ +    // compute larger size +    constexpr S16 BUFFER_RING_EXPANSION = 256; +    S16 old_size = (S16)(mPacketRing.size()); +    S16 new_size = llmin(old_size + BUFFER_RING_EXPANSION, MAX_BUFFER_RING_SIZE); +    if (new_size == old_size) +    { +        // mPacketRing is already maxed out +        return false; +    } -    proxywrap_t *socks_header = static_cast<proxywrap_t*>(static_cast<void*>(&headered_send_buffer)); -    socks_header->rsv   = 0; -    socks_header->addr  = host.getAddress(); -    socks_header->port  = htons(host.getPort()); -    socks_header->atype = ADDRESS_IPV4; -    socks_header->frag  = 0; +    // make a larger ring and copy packet pointers +    std::vector<LLPacketBuffer*> new_ring(new_size, nullptr); +    for (S16 i = 0; i < old_size; ++i) +    { +        S16 j = (mHeadIndex + i) % old_size; +        new_ring[i] = mPacketRing[j]; +    } -    memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, send_buffer, buf_size); +    // allocate new packets for the remainder of new_ring +    LLHost invalid_host; +    for (S16 i = old_size; i < new_size; ++i) +    { +        new_ring[i] = new LLPacketBuffer(invalid_host, nullptr, 0); +    } -    return send_packet( h_socket, -                        headered_send_buffer, -                        buf_size + SOCKS_HEADER_SIZE, -                        LLProxy::getInstance()->getUDPProxy().getAddress(), -                        LLProxy::getInstance()->getUDPProxy().getPort()); +    // swap the rings and reset mHeadIndex +    mPacketRing.swap(new_ring); +    mHeadIndex = mNumBufferedPackets; +    return true;  } diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h index f0e95f8524..0dff2c63b1 100644 --- a/indra/llmessage/llpacketring.h +++ b/indra/llmessage/llpacketring.h @@ -25,16 +25,14 @@   * $/LicenseInfo$   */ -#ifndef LL_LLPACKETRING_H -#define LL_LLPACKETRING_H +#pragma once -#include <queue> +#include <vector>  #include "llhost.h"  #include "llpacketbuffer.h" -#include "llproxy.h"  #include "llthrottle.h" -#include "net.h" +  class LLPacketRing  { @@ -42,60 +40,65 @@ public:      LLPacketRing();      ~LLPacketRing(); -    void cleanup(); +    // receive one packet: either buffered or from the socket +    S32  receivePacket (S32 socket, char *datap); + +    // send one packet +    bool sendPacket(int h_socket, const char * send_buffer, S32 buf_size, LLHost host); + +    // drains packets from socket and returns final mNumBufferedPackets +    S32 drainSocket(S32 socket);      void dropPackets(U32);      void setDropPercentage (F32 percent_to_drop); -    void setUseInThrottle(const bool use_throttle); -    void setUseOutThrottle(const bool use_throttle); -    void setInBandwidth(const F32 bps); -    void setOutBandwidth(const F32 bps); -    S32  receivePacket (S32 socket, char *datap); -    S32  receiveFromRing (S32 socket, char *datap); -    bool sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host); +    inline LLHost getLastSender() const; +    inline LLHost getLastReceivingInterface() const; -    inline LLHost getLastSender(); -    inline LLHost getLastReceivingInterface(); +    S32 getActualInBytes() const { return mActualBytesIn; } +    S32 getActualOutBytes() const { return mActualBytesOut; } +    S32 getAndResetActualInBits()   { S32 bits = mActualBytesIn * 8; mActualBytesIn = 0; return bits;} +    S32 getAndResetActualOutBits()  { S32 bits = mActualBytesOut * 8; mActualBytesOut = 0; return bits;} -    S32 getAndResetActualInBits()               { S32 bits = mActualBitsIn; mActualBitsIn = 0; return bits;} -    S32 getAndResetActualOutBits()              { S32 bits = mActualBitsOut; mActualBitsOut = 0; return bits;} +    S32 getNumBufferedPackets() const { return (S32)(mNumBufferedPackets); } +    S32 getNumBufferedBytes() const { return mNumBufferedBytes; }  protected: -    bool mUseInThrottle; -    bool mUseOutThrottle; +    // returns 'true' if we should intentionally drop a packet +    bool computeDrop(); -    // For simulating a lower-bandwidth connection - BPS -    LLThrottle mInThrottle; -    LLThrottle mOutThrottle; +    // returns packet_size of received packet, zero or less if no packet found +    S32 receiveOrDropPacket(S32 socket, char *datap, bool drop); +    S32 receiveOrDropBufferedPacket(char *datap, bool drop); -    S32 mActualBitsIn; -    S32 mActualBitsOut; -    S32 mMaxBufferLength;           // How much data can we queue up before dropping data. -    S32 mInBufferLength;            // Current incoming buffer length -    S32 mOutBufferLength;           // Current outgoing buffer length +    // returns packet_size of packet buffered +    S32 bufferInboundPacket(S32 socket); -    F32 mDropPercentage;            // % of packets to drop -    U32 mPacketsToDrop;             // drop next n packets +    // returns 'true' if ring was expanded +    bool expandRing(); -    std::queue<LLPacketBuffer *> mReceiveQueue; -    std::queue<LLPacketBuffer *> mSendQueue; +protected: +    std::vector<LLPacketBuffer*> mPacketRing; +    S16 mHeadIndex { 0 }; +    S16 mNumBufferedPackets { 0 }; +    S32 mNumBufferedBytes { 0 }; + +    S32 mActualBytesIn { 0 }; +    S32 mActualBytesOut { 0 }; +    F32 mDropPercentage { 0.0f };   // % of inbound packets to drop +    U32 mPacketsToDrop { 0 };       // drop next inbound n packets +    // These are the sender and receiving_interface for the last packet delivered by receivePacket()      LLHost mLastSender;      LLHost mLastReceivingIF; - -private: -    bool sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host);  }; -inline LLHost LLPacketRing::getLastSender() +inline LLHost LLPacketRing::getLastSender() const  {      return mLastSender;  } -inline LLHost LLPacketRing::getLastReceivingInterface() +inline LLHost LLPacketRing::getLastReceivingInterface() const  {      return mLastReceivingIF;  } - -#endif diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index cfa5178fc6..c130b7a6db 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -656,8 +656,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )              // UseCircuitCode is allowed in even from an invalid circuit, so that              // we can toss circuits around. -            if( -                valid_packet && +            else if (                  !cdp &&                  (mTemplateMessageReader->getMessageName() !=                   _PREHASH_UseCircuitCode)) @@ -667,8 +666,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )                  valid_packet = false;              } -            if( -                valid_packet && +            if ( valid_packet &&                  cdp &&                  !cdp->getTrusted() &&                  mTemplateMessageReader->isTrusted()) @@ -680,7 +678,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count )                  valid_packet = false;              } -            if( valid_packet ) +            if ( valid_packet )              {                  logValidMsg(cdp, host, recv_reliable, recv_resent, acks>0 );                  valid_packet = mTemplateMessageReader->readMessage(buffer, host); @@ -821,6 +819,11 @@ void LLMessageSystem::processAcks(LockMessageChecker&, F32 collect_time)      }  } +S32 LLMessageSystem::drainUdpSocket() +{ +    return mPacketRing.drainSocket(mSocket); +} +  void LLMessageSystem::copyMessageReceivedToSend()  {      // NOTE: babbage: switch builder to match reader to avoid diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index b4b0d94021..1844d5e7cd 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -417,6 +417,9 @@ public:      bool    checkMessages(LockMessageChecker&, S64 frame_count = 0 );      void    processAcks(LockMessageChecker&, F32 collect_time = 0.f); +    // returns total number of buffered packets after the drain +    S32     drainUdpSocket(); +      bool    isMessageFast(const char *msg);      bool    isMessage(const char *msg)      { diff --git a/indra/llmessage/net.cpp b/indra/llmessage/net.cpp index f153c938cf..2be5a9e5b6 100644 --- a/indra/llmessage/net.cpp +++ b/indra/llmessage/net.cpp @@ -76,14 +76,8 @@ static U32 gsnReceivingIFAddr = INVALID_HOST_IP_ADDRESS; // Address to which dat  const char* LOOPBACK_ADDRESS_STRING = "127.0.0.1";  const char* BROADCAST_ADDRESS_STRING = "255.255.255.255"; -#if LL_DARWIN -    // macOS returns an error when trying to set these to 400000.  Smaller values succeed. -    const int   SEND_BUFFER_SIZE    = 200000; -    const int   RECEIVE_BUFFER_SIZE = 200000; -#else // LL_DARWIN -    const int   SEND_BUFFER_SIZE    = 400000; -    const int   RECEIVE_BUFFER_SIZE = 400000; -#endif // LL_DARWIN +const int   SEND_BUFFER_SIZE    = 200000; +const int   RECEIVE_BUFFER_SIZE = 800000;  // universal functions (cross-platform) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 689f21d29f..a05466b476 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -349,10 +349,6 @@ std::string gLastVersionChannel;  LLVector3 gWindVec(3.0, 3.0, 0.0);  LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); -U32 gPacketsIn = 0; - -bool gPrintMessagesThisFrame = false; -  bool gRandomizeFramerate = false;  bool gPeriodicSlowFrame = false; @@ -361,6 +357,7 @@ bool gLLErrorActivated = false;  bool gLogoutInProgress = false;  bool gSimulateMemLeak = false; +bool gDoDisconnect = false;  // We don't want anyone, especially threads working on the graphics pipeline,  // to have to block due to this WorkQueue being full. @@ -374,7 +371,6 @@ const std::string MARKER_FILE_NAME("SecondLife.exec_marker");  const std::string START_MARKER_FILE_NAME("SecondLife.start_marker");  const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker");  const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker"); -static bool gDoDisconnect = false;  static std::string gLaunchFileOnQuit;  // Used on Win32 for other apps to identify our window (eg, win_setup) @@ -1493,9 +1489,9 @@ bool LLAppViewer::doFrame()          {              LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df pauseMainloopTimeout"); -        pingMainloopTimeout("Main:Sleep"); +            pingMainloopTimeout("Main:Sleep"); -        pauseMainloopTimeout(); +            pauseMainloopTimeout();          }          // Sleep and run background threads @@ -4242,7 +4238,7 @@ U32 LLAppViewer::getTextureCacheVersion()  U32 LLAppViewer::getDiskCacheVersion()  {      // Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes. -    const U32 DISK_CACHE_VERSION = 1; +    const U32 DISK_CACHE_VERSION = 2;      return DISK_CACHE_VERSION ;  } @@ -4901,6 +4897,20 @@ void LLAppViewer::idle()      if (gTeleportDisplay)      { +        if (gAgent.getTeleportState() == LLAgent::TELEPORT_ARRIVING) +        { +            // Teleported, but waiting for things to load, start processing surface data +            { +                LL_RECORD_BLOCK_TIME(FTM_NETWORK); +                gVLManager.unpackData(); +            } +            { +                LL_RECORD_BLOCK_TIME(FTM_REGION_UPDATE); +                const F32 max_region_update_time = .001f; // 1ms +                LLWorld::getInstance()->updateRegions(max_region_update_time); +            } +        } +          return;      } @@ -5337,12 +5347,9 @@ void LLAppViewer::idleNameCache()  // Handle messages, and all message related stuff  // -#define TIME_THROTTLE_MESSAGES -#ifdef TIME_THROTTLE_MESSAGES -#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!) +constexpr F32 CHECK_MESSAGES_DEFAULT_MAX_TIME = 0.020f; // 50 ms = 50 fps (just for messages!)  static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -#endif  static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network");  static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks"); @@ -5369,6 +5376,7 @@ void LLAppViewer::idleNetwork()          F32 total_time = 0.0f;          { +            bool needs_drain = false;              LockMessageChecker lmc(gMessageSystem);              while (lmc.checkAllMessages(frame_count, gServicePump))              { @@ -5381,54 +5389,44 @@ void LLAppViewer::idleNetwork()                  }                  total_decoded++; -                gPacketsIn++;                  if (total_decoded > MESSAGE_MAX_PER_FRAME)                  { +                    needs_drain = true;                      break;                  } -#ifdef TIME_THROTTLE_MESSAGES                  // Prevent slow packets from completely destroying the frame rate.                  // This usually happens due to clumps of avatars taking huge amount                  // of network processing time (which needs to be fixed, but this is                  // a good limit anyway).                  total_time = check_message_timer.getElapsedTimeF32();                  if (total_time >= CheckMessagesMaxTime) +                { +                    needs_drain = true;                      break; -#endif +                } +            } +            if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0) +            { +                // Rather than allow packets to silently backup on the socket +                // we drain them into our own buffer so we know how many exist. +                S32 num_buffered_packets = gMessageSystem->drainUdpSocket(); +                if (num_buffered_packets > 0) +                { +                    // Increase CheckMessagesMaxTime so that we will eventually catch up +                    CheckMessagesMaxTime *= 1.035f; // 3.5% ~= 2x in 20 frames, ~8x in 60 frames +                } +            } +            else +            { +                // Reset CheckMessagesMaxTime to default value +                CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME;              }              // Handle per-frame message system processing.              lmc.processAcks(gSavedSettings.getF32("AckCollectTime"));          } - -#ifdef TIME_THROTTLE_MESSAGES -        if (total_time >= CheckMessagesMaxTime) -        { -            // Increase CheckMessagesMaxTime so that we will eventually catch up -            CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames -        } -        else -        { -            // Reset CheckMessagesMaxTime to default value -            CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -        } -#endif - -        // Decode enqueued messages... -        S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded; - -        if( remaining_possible_decodes <= 0 ) -        { -            LL_INFOS() << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << LL_ENDL; -        } - -        if (gPrintMessagesThisFrame) -        { -            LL_INFOS() << "Decoded " << total_decoded << " msgs this frame!" << LL_ENDL; -            gPrintMessagesThisFrame = false; -        }      }      add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects); diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 542379c828..10d71c9d42 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -413,11 +413,10 @@ extern std::string gLastVersionChannel;  extern LLVector3 gWindVec;  extern LLVector3 gRelativeWindVec; -extern U32  gPacketsIn; -extern bool gPrintMessagesThisFrame;  extern bool gRandomizeFramerate;  extern bool gPeriodicSlowFrame; +extern bool gDoDisconnect;  extern bool gSimulateMemLeak; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 93453f507c..e1fa84b4d8 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -255,12 +255,12 @@  //     mSkinInfoQ               mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0])  //     mDecompositionRequests   mMutex        rw.repo.mMutex, ro.repo.none [5]  //     mPhysicsShapeRequests    mMutex        rw.repo.mMutex, ro.repo.none [5] -//     mDecompositionQ          mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0]) +//     mDecompositionQ          mMutex        rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was:  [0])  //     mHeaderReqQ              mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex  //     mLODReqQ                 mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex -//     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [5], rw.main.mMutex -//     mLoadedQ                 mMutex        rw.repo.mMutex, ro.main.none [5], rw.main.mMutex -//     mPendingLOD              mMutex        rw.repo.mMutex, rw.any.mMutex +//     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [5], rw.main.mLoadedMutex +//     mLoadedQ                 mMutex        rw.repo.mLoadedMutex, ro.main.none [5], rw.main.mLoadedMutex +//     mPendingLOD              mMutex        rw.repo.mPendingMutex, rw.any.mPendingMutex  //     mGetMeshCapability       mMutex        rw.main.mMutex, ro.repo.mMutex (was:  [0])  //     mGetMesh2Capability      mMutex        rw.main.mMutex, ro.repo.mMutex (was:  [0])  //     mGetMeshVersion          mMutex        rw.main.mMutex, ro.repo.mMutex @@ -343,20 +343,22 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");  LLMeshRepository gMeshRepo; -const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space +constexpr U32 CACHE_PREAMBLE_VERSION = 1; +constexpr S32 CACHE_PREAMBLE_SIZE = sizeof(U32) * 3; //version, header_size, flags +constexpr S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space -const S32 REQUEST2_HIGH_WATER_MIN = 32;                 // Limits for GetMesh2 regions -const S32 REQUEST2_HIGH_WATER_MAX = 100; -const S32 REQUEST2_LOW_WATER_MIN = 16; -const S32 REQUEST2_LOW_WATER_MAX = 50; +constexpr S32 REQUEST2_HIGH_WATER_MIN = 32;                 // Limits for GetMesh2 regions +constexpr S32 REQUEST2_HIGH_WATER_MAX = 100; +constexpr S32 REQUEST2_LOW_WATER_MIN = 16; +constexpr S32 REQUEST2_LOW_WATER_MAX = 50; -const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;        // Size at which requests goes to narrow/slow queue -const long SMALL_MESH_XFER_TIMEOUT = 120L;              // Seconds to complete xfer, small mesh downloads -const long LARGE_MESH_XFER_TIMEOUT = 600L;              // Seconds to complete xfer, large downloads +constexpr U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;        // Size at which requests goes to narrow/slow queue +constexpr long SMALL_MESH_XFER_TIMEOUT = 120L;              // Seconds to complete xfer, small mesh downloads +constexpr long LARGE_MESH_XFER_TIMEOUT = 600L;              // Seconds to complete xfer, large downloads -const U32 DOWNLOAD_RETRY_LIMIT = 8; -const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds +constexpr U32 DOWNLOAD_RETRY_LIMIT = 8; +constexpr F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds  // Would normally like to retry on uploads as some  // retryable failures would be recoverable.  Unfortunately, @@ -366,7 +368,7 @@ const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds  // cap which then produces a 404 on retry destroying some  // (occasionally) useful error information.  We'll leave  // upload retries to the user as in the past.  SH-4667. -const long UPLOAD_RETRY_LIMIT = 0L; +constexpr long UPLOAD_RETRY_LIMIT = 0L;  // Maximum mesh version to support.  Three least significant digits are reserved for the minor version,  // with major version changes indicating a format change that is not backwards compatible and should not @@ -374,7 +376,7 @@ const long UPLOAD_RETRY_LIMIT = 0L;  // present, the version is 0.001. A viewer that can parse version 0.001 can also parse versions up to 0.999,  // but not 1.0 (integer 1000).  // See wiki at https://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format -const S32 MAX_MESH_VERSION = 999; +constexpr S32 MAX_MESH_VERSION = 999;  U32 LLMeshRepository::sBytesReceived = 0;  U32 LLMeshRepository::sMeshRequestCount = 0; @@ -386,12 +388,12 @@ U32 LLMeshRepository::sLODProcessing = 0;  U32 LLMeshRepository::sLODPending = 0;  U32 LLMeshRepository::sCacheBytesRead = 0; -U32 LLMeshRepository::sCacheBytesWritten = 0; +std::atomic<U32> LLMeshRepository::sCacheBytesWritten = 0;  U32 LLMeshRepository::sCacheBytesHeaders = 0;  U32 LLMeshRepository::sCacheBytesSkins = 0;  U32 LLMeshRepository::sCacheBytesDecomps = 0;  U32 LLMeshRepository::sCacheReads = 0; -U32 LLMeshRepository::sCacheWrites = 0; +std::atomic<U32> LLMeshRepository::sCacheWrites = 0;  U32 LLMeshRepository::sMaxLockHoldoffs = 0;  LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false);  // true -> gather cpu metrics @@ -550,6 +552,7 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate  std::atomic<S32> LLMeshRepoThread::sActiveHeaderRequests = 0;  std::atomic<S32> LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic<S32> LLMeshRepoThread::sActiveSkinRequests = 0;  U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;  S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN;  S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; @@ -584,6 +587,7 @@ public:          : LLCore::HttpHandler(),            mMeshParams(),            mProcessed(false), +          mHasDataOwnership(true),            mHttpHandle(LLCORE_HTTP_HANDLE_INVALID),            mOffset(offset),            mRequestedBytes(requested_bytes) @@ -607,6 +611,9 @@ public:      LLCore::HttpHandle mHttpHandle;      U32 mOffset;      U32 mRequestedBytes; + +protected: +    bool mHasDataOwnership = true;  }; @@ -659,6 +666,9 @@ public:      virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);      virtual void processFailure(LLCore::HttpStatus status); +private: +    void processLod(U8* data, S32 data_size); +  public:      S32 mLOD;  }; @@ -674,13 +684,17 @@ public:      LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes)          : LLMeshHandlerBase(offset, requested_bytes),            mMeshID(id) -    {} +    { +        LLMeshRepoThread::incActiveSkinRequests(); +    }      virtual ~LLMeshSkinInfoHandler();  protected:      LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &);       // Not defined      void operator=(const LLMeshSkinInfoHandler &);              // Not defined +    void processSkin(U8* data, S32 data_size); +  public:      virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);      virtual void processFailure(LLCore::HttpStatus status); @@ -823,6 +837,14 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,      gMeshRepo.uploadError(args);  } +void write_preamble(LLFileSystem &file, S32 header_bytes, S32 flags) +{ +    LLMeshRepository::sCacheBytesWritten += CACHE_PREAMBLE_SIZE; +    file.write((U8*)&CACHE_PREAMBLE_VERSION, sizeof(U32)); +    file.write((U8*)&header_bytes, sizeof(U32)); +    file.write((U8*)&flags, sizeof(U32)); +} +  LLMeshRepoThread::LLMeshRepoThread()  : LLThread("mesh repo"),    mHttpRequest(NULL), @@ -837,6 +859,9 @@ LLMeshRepoThread::LLMeshRepoThread()      mMutex = new LLMutex();      mHeaderMutex = new LLMutex(); +    mLoadedMutex = new LLMutex(); +    mPendingMutex = new LLMutex(); +    mSkinMapMutex = new LLMutex();      mSignal = new LLCondition();      mHttpRequest = new LLCore::HttpRequest;      mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); @@ -849,6 +874,11 @@ LLMeshRepoThread::LLMeshRepoThread()      mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH);      mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2);      mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH); + +    // Lod processing is expensive due to the number of requests +    // and a need to do expensive cacheOptimize(). +    mMeshThreadPool.reset(new LL::ThreadPool("MeshLodProcessing", 2)); +    mMeshThreadPool->start();  } @@ -875,13 +905,21 @@ LLMeshRepoThread::~LLMeshRepoThread()      }      delete mHttpRequest; -    mHttpRequest = NULL; +    mHttpRequest = nullptr;      delete mMutex; -    mMutex = NULL; +    mMutex = nullptr;      delete mHeaderMutex; -    mHeaderMutex = NULL; +    mHeaderMutex = nullptr; +    delete mLoadedMutex; +    mLoadedMutex = nullptr; +    delete mPendingMutex; +    mPendingMutex = nullptr; +    delete mSkinMapMutex; +    mSkinMapMutex = nullptr;      delete mSignal; -    mSignal = NULL; +    mSignal = nullptr; +    delete[] mDiskCacheBuffer; +    mDiskCacheBuffer = nullptr;  }  void LLMeshRepoThread::run() @@ -908,6 +946,7 @@ void LLMeshRepoThread::run()          // On the other hand, this may actually be an effective and efficient scheme...          mSignal->wait(); +        LL_PROFILE_ZONE_NAMED("mesh_thread_loop")          if (LLApp::isExiting())          { @@ -925,11 +964,55 @@ void LLMeshRepoThread::run()          }          sRequestWaterLevel = static_cast<S32>(mHttpRequestSet.size());            // Stats data update -        // NOTE: order of queue processing intentionally favors LOD requests over header requests +        // NOTE: order of queue processing intentionally favors LOD and Skin requests over header requests          // Todo: we are processing mLODReqQ, mHeaderReqQ, mSkinRequests, mDecompositionRequests and mPhysicsShapeRequests          // in relatively similar manners, remake code to simplify/unify the process,          // like processRequests(&requestQ, fetchFunction); which does same thing for each element +        if (mHttpRequestSet.size() < sRequestHighWater +            && !mSkinRequests.empty()) +        { +            if (!mSkinRequests.empty()) +            { +                std::list<UUIDBasedRequest> incomplete; +                while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) +                { + +                    mMutex->lock(); +                    auto req = mSkinRequests.front(); +                    mSkinRequests.pop_front(); +                    mMutex->unlock(); +                    if (req.isDelayed()) +                    { +                        incomplete.emplace_back(req); +                    } +                    else if (!fetchMeshSkinInfo(req.mId)) +                    { +                        if (req.canRetry()) +                        { +                            req.updateTime(); +                            incomplete.emplace_back(req); +                        } +                        else +                        { +                            LLMutexLock locker(mLoadedMutex); +                            mSkinUnavailableQ.push_back(req); +                            LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; +                        } +                    } +                } + +                if (!incomplete.empty()) +                { +                    LLMutexLock locker(mMutex); +                    for (const auto& req : incomplete) +                    { +                        mSkinRequests.push_back(req); +                    } +                } +            } +        } +          if (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)          {              std::list<LODRequest> incomplete; @@ -950,7 +1033,7 @@ void LLMeshRepoThread::run()                      // failed to load before, wait a bit                      incomplete.push_front(req);                  } -                else if (!fetchMeshLOD(req.mMeshParams, req.mLOD, req.canRetry())) +                else if (!fetchMeshLOD(req.mMeshParams, req.mLOD))                  {                      if (req.canRetry())                      { @@ -961,7 +1044,7 @@ void LLMeshRepoThread::run()                      else                      {                          // too many fails -                        LLMutexLock lock(mMutex); +                        LLMutexLock lock(mLoadedMutex);                          mUnavailableQ.push_back(req);                          LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL;                      } @@ -998,7 +1081,7 @@ void LLMeshRepoThread::run()                      // failed to load before, wait a bit                      incomplete.push_front(req);                  } -                else if (!fetchMeshHeader(req.mMeshParams, req.canRetry())) +                else if (!fetchMeshHeader(req.mMeshParams))                  {                      if (req.canRetry())                      { @@ -1028,54 +1111,13 @@ void LLMeshRepoThread::run()          // performing long-duration actions.          if (mHttpRequestSet.size() < sRequestHighWater -            && (!mSkinRequests.empty() -            || !mDecompositionRequests.empty() +            && (!mDecompositionRequests.empty()              || !mPhysicsShapeRequests.empty()))          {              // Something to do probably, lock and double-check.  We don't want              // to hold the lock long here.  That will stall main thread activities              // so we bounce it. -            if (!mSkinRequests.empty()) -            { -                std::list<UUIDBasedRequest> incomplete; -                while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) -                { - -                    mMutex->lock(); -                    auto req = mSkinRequests.front(); -                    mSkinRequests.pop_front(); -                    mMutex->unlock(); -                    if (req.isDelayed()) -                    { -                        incomplete.emplace_back(req); -                    } -                    else if (!fetchMeshSkinInfo(req.mId, req.canRetry())) -                    { -                        if (req.canRetry()) -                        { -                            req.updateTime(); -                            incomplete.emplace_back(req); -                        } -                        else -                        { -                            LLMutexLock locker(mMutex); -                            mSkinUnavailableQ.push_back(req); -                            LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; -                        } -                    } -                } - -                if (!incomplete.empty()) -                { -                    LLMutexLock locker(mMutex); -                    for (const auto& req : incomplete) -                    { -                        mSkinRequests.push_back(req); -                    } -                } -            } -              // holding lock, try next list              // *TODO:  For UI/debug-oriented lists, we might drop the fine-              // grained locking as there's a lowered expectation of smoothness @@ -1103,7 +1145,7 @@ void LLMeshRepoThread::run()                          }                          else                          { -                            LL_DEBUGS() << "mDecompositionRequests failed: " << req.mId << LL_ENDL; +                            LL_DEBUGS(LOG_MESH) << "mDecompositionRequests failed: " << req.mId << LL_ENDL;                          }                      }                  } @@ -1139,7 +1181,7 @@ void LLMeshRepoThread::run()                          }                          else                          { -                            LL_DEBUGS() << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL; +                            LL_DEBUGS(LOG_MESH) << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL;                          }                      }                  } @@ -1195,40 +1237,91 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32      }  } -  void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)  { //could be called from any thread      const LLUUID& mesh_id = mesh_params.getSculptID(); -    LLMutexLock lock(mMutex); -    LLMutexLock header_lock(mHeaderMutex); -    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); -    if (iter != mMeshHeader.end()) +    loadMeshLOD(mesh_id, mesh_params, lod); +} + +void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& mesh_params, S32 lod) +{ +    if (hasHeader(mesh_id))      { //if we have the header, request LOD byte range          LODRequest req(mesh_params, lod);          { +            LLMutexLock lock(mMutex);              mLODReqQ.push(req);              LLMeshRepository::sLODProcessing++;          }      }      else      { +        LLMutexLock lock(mPendingMutex);          HeaderRequest req(mesh_params);          pending_lod_map::iterator pending = mPendingLOD.find(mesh_id);          if (pending != mPendingLOD.end()) -        { //append this lod request to existing header request -            pending->second.push_back(lod); -            llassert(pending->second.size() <= LLModel::NUM_LODS); +        { +            //append this lod request to existing header request +            if (lod < LLModel::NUM_LODS && lod >= 0) +            { +                pending->second[lod]++; +            } +            else +            { +                LL_WARNS(LOG_MESH) << "Invalid LOD request: " << lod << "for mesh" << mesh_id << LL_ENDL; +            } +            llassert_msg(lod < LLModel::NUM_LODS, "Requested lod is out of bounds");          }          else -        { //if no header request is pending, fetch header +        { +            //if no header request is pending, fetch header +            auto& array = mPendingLOD[mesh_id]; +            std::fill(array.begin(), array.end(), 0); +            array[lod]++; + +            LLMutexLock lock(mMutex);              mHeaderReqQ.push(req); -            mPendingLOD[mesh_id].push_back(lod);          }      }  } +U8* LLMeshRepoThread::getDiskCacheBuffer(S32 size) +{ +    if (mDiskCacheBufferSize < size) +    { +        const S32 MINIMUM_BUFFER_SIZE = 8192; // a minimum to avoid frequent early reallocations +        size = llmax(MINIMUM_BUFFER_SIZE, size); +        delete[] mDiskCacheBuffer; +        try +        { +            mDiskCacheBuffer = new U8[size]; +        } +        catch (std::bad_alloc&) +        { +            LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh thread's buffer, size: " << size << LL_ENDL; +            mDiskCacheBuffer = NULL; + +            // Not sure what size is reasonable +            // but if 30MB allocation failed, we definitely have issues +            const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB +            if (size < MAX_SIZE) +            { +                LLAppViewer::instance()->outOfMemorySoftQuit(); +            } // else ignore failures for anomalously large data +        } +        mDiskCacheBufferSize = size; +    } +    else +    { +        // reusing old buffer, reset heading bytes to ensure +        // old content won't be parsable if something fails. +        memset(mDiskCacheBuffer, 0, 16); +    } +    return mDiskCacheBuffer; +} +  // Mutex:  must be holding mMutex when called  void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap)  { @@ -1327,7 +1420,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,  } -bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)  {      LL_PROFILE_ZONE_SCOPED;      if (!mHeaderMutex) @@ -1337,7 +1430,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)      mHeaderMutex->lock(); -    auto header_it = mMeshHeader.find(mesh_id); +    mesh_header_map::const_iterator header_it = mMeshHeader.find(mesh_id);      if (header_it == mMeshHeader.end())      { //we have no header info for this mesh, do nothing          mHeaderMutex->unlock(); @@ -1346,23 +1439,24 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)      ++LLMeshRepository::sMeshRequestCount;      bool ret = true; -    U32 header_size = header_it->second.first; +    const LLMeshHeader& header = header_it->second; +    U32 header_size = header.mHeaderSize;      if (header_size > 0)      { -        const LLMeshHeader& header = header_it->second.second; -          S32 version = header.mVersion;          S32 offset = header_size + header.mSkinOffset;          S32 size = header.mSkinSize; +        bool in_cache = header.mSkinInCache;          mHeaderMutex->unlock();          if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)          {              //check cache for mesh skin info +            S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;              LLFileSystem file(mesh_id, LLAssetType::AT_MESH); -            if (file.getSize() >= offset + size) +            if (in_cache && file.getSize() >= disk_ofset + size)              {                  U8* buffer = new(std::nothrow) U8[size];                  if (!buffer) @@ -1370,19 +1464,19 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)                      LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL;                      // Not sure what size is reasonable for skin info, -                    // but if 20MB allocation failed, we definetely have issues +                    // but if 30MB allocation failed, we definitely have issues                      const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB                      if (size < MAX_SIZE)                      {                          LLAppViewer::instance()->outOfMemorySoftQuit();                      } // else ignore failures for anomalously large data -                    LLMutexLock locker(mMutex); +                    LLMutexLock locker(mLoadedMutex);                      mSkinUnavailableQ.emplace_back(mesh_id);                      return true;                  }                  LLMeshRepository::sCacheBytesRead += size;                  ++LLMeshRepository::sCacheReads; -                file.seek(offset); +                file.seek(disk_ofset);                  file.read(buffer, size);                  //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1393,14 +1487,67 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)                  }                  if (!zero) -                { //attempt to parse -                    if (skinInfoReceived(mesh_id, buffer, size)) +                { +                    //attempt to parse +                    bool posted = mMeshThreadPool->getQueue().post( +                        [mesh_id, buffer, size] +                        () +                    { +                        if (!gMeshRepo.mThread->skinInfoReceived(mesh_id, buffer, size)) +                        { +                            // either header is faulty or something else overwrote the cache +                            S32 header_size = 0; +                            U32 header_flags = 0; +                            { +                                LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL; + +                                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                                auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id); +                                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                                { +                                    LLMeshHeader& header = header_it->second; +                                    // for safety just mark everything as missing +                                    header.mSkinInCache = false; +                                    header.mPhysicsConvexInCache = false; +                                    header.mPhysicsMeshInCache = false; +                                    for (S32 i = 0; i < LLModel::NUM_LODS; ++i) +                                    { +                                        header.mLodInCache[i] = false; +                                    } +                                    header_size = header.mHeaderSize; +                                    header_flags = header.getFlags(); +                                } +                            } + +                            if (header_size > 0) +                            { +                                LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); +                                if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE) +                                { +                                    write_preamble(file, header_size, header_flags); +                                } +                            } + +                            { +                                LLMutexLock lock(gMeshRepo.mThread->mMutex); +                                UUIDBasedRequest req(mesh_id); +                                gMeshRepo.mThread->mSkinRequests.push_back(req); +                            } +                        } +                        delete[] buffer; +                    }); +                    if (posted) +                    { +                        // lambda owns buffer +                        return true; +                    } +                    else if (skinInfoReceived(mesh_id, buffer, size))                      {                          delete[] buffer;                          return true;                      }                  } -                  delete[] buffer;              } @@ -1420,26 +1567,21 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)                                         << LL_ENDL;                      ret = false;                  } -                else if(can_retry) +                else                  {                      handler->mHttpHandle = handle;                      mHttpRequestSet.insert(handler);                  } -                else -                { -                    LLMutexLock locker(mMutex); -                    mSkinUnavailableQ.emplace_back(mesh_id); -                }              }              else              { -                LLMutexLock locker(mMutex); +                LLMutexLock locker(mLoadedMutex);                  mSkinUnavailableQ.emplace_back(mesh_id);              }          }          else          { -            LLMutexLock locker(mMutex); +            LLMutexLock locker(mLoadedMutex);              mSkinUnavailableQ.emplace_back(mesh_id);          }      } @@ -1470,42 +1612,35 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)      }      ++LLMeshRepository::sMeshRequestCount; -    U32 header_size = header_it->second.first; +    const auto& header = header_it->second; +    U32 header_size = header.mHeaderSize;      bool ret = true;      if (header_size > 0)      { -        const auto& header = header_it->second.second;          S32 version = header.mVersion;          S32 offset = header_size + header.mPhysicsConvexOffset;          S32 size = header.mPhysicsConvexSize; +        bool in_cache = header.mPhysicsConvexInCache;          mHeaderMutex->unlock();          if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)          { -            //check cache for mesh skin info +            // check cache for mesh decomposition +            S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;              LLFileSystem file(mesh_id, LLAssetType::AT_MESH); -            if (file.getSize() >= offset+size) +            if (in_cache && file.getSize() >= disk_ofset + size)              { -                U8* buffer = new(std::nothrow) U8[size]; +                U8* buffer = getDiskCacheBuffer(size);                  if (!buffer)                  { -                    LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL; - -                    // Not sure what size is reasonable for decomposition -                    // but if 20MB allocation failed, we definetely have issues -                    const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB -                    if (size < MAX_SIZE) -                    { -                        LLAppViewer::instance()->outOfMemorySoftQuit(); -                    } // else ignore failures for anomalously large decompositiions                      return true;                  }                  LLMeshRepository::sCacheBytesRead += size;                  ++LLMeshRepository::sCacheReads; -                file.seek(offset); +                file.seek(disk_ofset);                  file.read(buffer, size);                  //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1519,12 +1654,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)                  { //attempt to parse                      if (decompositionReceived(mesh_id, buffer, size))                      { -                        delete[] buffer;                          return true;                      }                  } - -                delete[] buffer;              }              //reading from cache failed for whatever reason, fetch from sim @@ -1578,41 +1710,36 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)      }      ++LLMeshRepository::sMeshRequestCount; -    U32 header_size = header_it->second.first; +    const auto& header = header_it->second; +    U32 header_size = header.mHeaderSize;      bool ret = true;      if (header_size > 0)      { -        const auto& header = header_it->second.second;          S32 version = header.mVersion;          S32 offset = header_size + header.mPhysicsMeshOffset;          S32 size = header.mPhysicsMeshSize; +        bool in_cache = header.mPhysicsMeshInCache;          mHeaderMutex->unlock(); +        // todo: check header.mHasPhysicsMesh          if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)          {              //check cache for mesh physics shape info +            S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;              LLFileSystem file(mesh_id, LLAssetType::AT_MESH); -            if (file.getSize() >= offset+size) +            if (in_cache && file.getSize() >= disk_ofset +size)              {                  LLMeshRepository::sCacheBytesRead += size;                  ++LLMeshRepository::sCacheReads; -                file.seek(offset); -                U8* buffer = new(std::nothrow) U8[size]; + +                U8* buffer = getDiskCacheBuffer(size);                  if (!buffer)                  { -                    LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL; - -                    // Not sure what size is reasonable for physcis -                    // but if 20MB allocation failed, we definetely have issues -                    const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB -                    if (size < MAX_SIZE) -                    { -                        LLAppViewer::instance()->outOfMemorySoftQuit(); -                    } // else ignore failures for anomalously large data                      return true;                  } +                file.seek(disk_ofset);                  file.read(buffer, size);                  //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1626,12 +1753,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)                  { //attempt to parse                      if (physicsShapeReceived(mesh_id, buffer, size) == MESH_OK)                      { -                        delete[] buffer;                          return true;                      }                  } - -                delete[] buffer;              }              //reading from cache failed for whatever reason, fetch from sim @@ -1699,8 +1823,22 @@ void LLMeshRepoThread::decActiveHeaderRequests()      --LLMeshRepoThread::sActiveHeaderRequests;  } +//static +void LLMeshRepoThread::incActiveSkinRequests() +{ +    LLMutexLock lock(gMeshRepo.mThread->mMutex); +    ++LLMeshRepoThread::sActiveSkinRequests; +} + +//static +void LLMeshRepoThread::decActiveSkinRequests() +{ +    LLMutexLock lock(gMeshRepo.mThread->mMutex); +    --LLMeshRepoThread::sActiveSkinRequests; +} +  //return false if failed to get header -bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry) +bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)  {      LL_PROFILE_ZONE_SCOPED;      ++LLMeshRepository::sMeshRequestCount; @@ -1714,17 +1852,34 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c          if (size > 0)          {              // *NOTE:  if the header size is ever more than 4KB, this will break -            U8 buffer[MESH_HEADER_SIZE]; -            S32 bytes = llmin(size, MESH_HEADER_SIZE); +            constexpr S32 DISK_MINIMAL_READ = 4096; +            U8 buffer[DISK_MINIMAL_READ * 2]; +            S32 bytes = llmin(size, DISK_MINIMAL_READ);              LLMeshRepository::sCacheBytesRead += bytes;              ++LLMeshRepository::sCacheReads; +              file.read(buffer, bytes); -            if (headerReceived(mesh_params, buffer, bytes) == MESH_OK) + +            U32 version = 0; +            memcpy(&version, buffer, sizeof(U32)); +            if (version == CACHE_PREAMBLE_VERSION)              { -                LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL; +                S32 header_size = 0; +                memcpy(&header_size, buffer + sizeof(U32), sizeof(S32)); +                if (header_size + CACHE_PREAMBLE_SIZE > DISK_MINIMAL_READ) +                { +                    bytes = llmin(size , DISK_MINIMAL_READ * 2); +                    file.read(buffer + DISK_MINIMAL_READ, bytes - DISK_MINIMAL_READ); +                } +                U32 flags = 0; +                memcpy(&flags, buffer + 2 * sizeof(U32), sizeof(U32)); +                if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes - CACHE_PREAMBLE_SIZE, flags) == MESH_OK) +                { +                    LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL; -                // Found mesh in cache -                return true; +                    // Found mesh in cache +                    return true; +                }              }          }      } @@ -1753,7 +1908,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c                                 << LL_ENDL;              retval = false;          } -        else if (can_retry) +        else          {              handler->mHttpHandle = handle;              mHttpRequestSet.insert(handler); @@ -1764,7 +1919,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c  }  //return false if failed to get mesh lod. -bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry) +bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)  {      LL_PROFILE_ZONE_SCOPED;      if (!mHeaderMutex) @@ -1784,41 +1939,43 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,      ++LLMeshRepository::sMeshRequestCount;      bool retval = true; -    U32 header_size = header_it->second.first; +    const auto& header = header_it->second; +    U32 header_size = header.mHeaderSize;      if (header_size > 0)      { -        const auto& header = header_it->second.second;          S32 version = header.mVersion;          S32 offset = header_size + header.mLodOffset[lod];          S32 size = header.mLodSize[lod]; +        bool in_cache = header.mLodInCache[lod];          mHeaderMutex->unlock();          if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)          { - +            S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE;              //check cache for mesh asset              LLFileSystem file(mesh_id, LLAssetType::AT_MESH); -            if (file.getSize() >= offset+size) +            if (in_cache && (file.getSize() >= disk_ofset + size))              { -                U8* buffer = new(std::nothrow) U8[size]; +                U8* buffer = new(std::nothrow) U8[size]; // todo, make buffer thread local and read in thread?                  if (!buffer)                  {                      LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL;                      // Not sure what size is reasonable for a mesh, -                    // but if 20MB allocation failed, we definetely have issues +                    // but if 30MB allocation failed, we definitely have issues                      const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB                      if (size < MAX_SIZE)                      {                          LLAppViewer::instance()->outOfMemorySoftQuit();                      } // else ignore failures for anomalously large data -                    LLMutexLock lock(mMutex); + +                    LLMutexLock lock(mLoadedMutex);                      mUnavailableQ.push_back(LODRequest(mesh_params, lod));                      return true;                  }                  LLMeshRepository::sCacheBytesRead += size;                  ++LLMeshRepository::sCacheReads; -                file.seek(offset); +                file.seek(disk_ofset);                  file.read(buffer, size);                  //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1829,15 +1986,76 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,                  }                  if (!zero) -                { //attempt to parse -                    if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) +                { +                    //attempt to parse +                    const LLVolumeParams params(mesh_params); +                    bool posted = mMeshThreadPool->getQueue().post( +                        [params, mesh_id, lod, buffer, size] +                        ()                      { +                        if (gMeshRepo.mThread->lodReceived(params, lod, buffer, size) == MESH_OK) +                        { +                            LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL; +                        } +                        else +                        { +                            // either header is faulty or something else overwrote the cache +                            S32 header_size = 0; +                            U32 header_flags = 0; +                            { +                                LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL; + +                                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                                auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id); +                                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                                { +                                    LLMeshHeader& header = header_it->second; +                                    // for safety just mark everything as missing +                                    header.mSkinInCache = false; +                                    header.mPhysicsConvexInCache = false; +                                    header.mPhysicsMeshInCache = false; +                                    for (S32 i = 0; i < LLModel::NUM_LODS; ++i) +                                    { +                                        header.mLodInCache[i] = false; +                                    } +                                    header_size = header.mHeaderSize; +                                    header_flags = header.getFlags(); +                                } +                            } + +                            if (header_size > 0) +                            { +                                LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); +                                if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE) +                                { +                                    write_preamble(file, header_size, header_flags); +                                } +                            } + +                            { +                                LLMutexLock lock(gMeshRepo.mThread->mMutex); +                                LODRequest req(params, lod); +                                gMeshRepo.mThread->mLODReqQ.push(req); +                                LLMeshRepository::sLODProcessing++; +                            } +                        }                          delete[] buffer; +                    }); +                    if (posted) +                    { +                        // now lambda owns buffer +                        return true; +                    } +                    else if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) +                    { +                        delete[] buffer;                          LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL;                          return true;                      } +                  }                  delete[] buffer; @@ -1861,27 +2079,22 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,                                         << LL_ENDL;                      retval = false;                  } -                else if (can_retry) +                else                  { +                    // we already made a request, store the handle                      handler->mHttpHandle = handle;                      mHttpRequestSet.insert(handler); -                    // *NOTE:  Allowing a re-request, not marking as unavailable.  Is that correct? -                } -                else -                { -                    LLMutexLock lock(mMutex); -                    mUnavailableQ.push_back(LODRequest(mesh_params, lod));                  }              }              else              { -                LLMutexLock lock(mMutex); +                LLMutexLock lock(mLoadedMutex);                  mUnavailableQ.push_back(LODRequest(mesh_params, lod));              }          }          else          { -            LLMutexLock lock(mMutex); +            LLMutexLock lock(mLoadedMutex);              mUnavailableQ.push_back(LODRequest(mesh_params, lod));          }      } @@ -1893,14 +2106,19 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,      return retval;  } -EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) +EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags)  { +    LL_PROFILE_ZONE_SCOPED;      const LLUUID mesh_id = mesh_params.getSculptID();      LLSD header_data;      LLMeshHeader header;      llssize header_size = 0; +    S32 skin_offset = -1; +    S32 skin_size = -1; +    S32 lod_offset[LLModel::NUM_LODS] = { -1 }; +    S32 lod_size[LLModel::NUM_LODS] = { -1 };      if (data_size > 0)      {          llssize dsize = data_size; @@ -1933,7 +2151,40 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes          // make sure there is at least one lod, function returns -1 and marks as 404 otherwise          else if (LLMeshRepository::getActualMeshLOD(header, 0) >= 0)          { -            header_size += stream.tellg(); +            header.mHeaderSize = (S32)stream.tellg(); +            header_size += header.mHeaderSize; +            skin_offset = header.mSkinOffset; +            skin_size = header.mSkinSize; + +            memcpy(lod_offset, header.mLodOffset, sizeof(lod_offset)); +            memcpy(lod_size, header.mLodSize, sizeof(lod_size)); + +            if (flags != 0) +            { +                header.setFromFlags(flags); +            } +            else +            { +                if (header.mSkinSize > 0 && header_size + header.mSkinOffset + header.mSkinSize < data_size) +                { +                    header.mSkinInCache = true; +                } +                if (header.mPhysicsConvexSize > 0 && header_size + header.mPhysicsConvexOffset + header.mPhysicsConvexSize < data_size) +                { +                    header.mPhysicsConvexInCache = true; +                } +                if (header.mPhysicsMeshSize > 0 && header_size + header.mPhysicsMeshOffset + header.mPhysicsMeshSize < data_size) +                { +                    header.mPhysicsMeshInCache = true; +                } +                for (S32 i = 0; i < LLModel::NUM_LODS; ++i) +                { +                    if (lod_size[i] > 0 && header_size + lod_offset[i] + lod_size[i] < data_size) +                    { +                        header.mLodInCache[i] = true; +                    } +                } +            }          }      }      else @@ -1947,35 +2198,84 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes          {              LLMutexLock lock(mHeaderMutex); -            mMeshHeader[mesh_id] = { (U32)header_size, header }; +            mMeshHeader[mesh_id] = header;              LLMeshRepository::sCacheBytesHeaders += (U32)header_size;          }          // immediately request SkinInfo since we'll need it before we can render any LoD if it is present +        if (skin_offset >= 0 && skin_size > 0)          { -            LLMutexLock lock(gMeshRepo.mMeshMutex); +            { +                LLMutexLock lock(gMeshRepo.mMeshMutex); -            if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end()) +                if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end()) +                { +                    gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info +                } +            } + +            S32 offset = (S32)header_size + skin_offset; +            bool request_skin = true; +            if (offset + skin_size < data_size) +            { +                request_skin = !skinInfoReceived(mesh_id, data + offset, skin_size); +            } +            if (request_skin)              { -                gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info +                mSkinRequests.push_back(UUIDBasedRequest(mesh_id));              }          } -        fetchMeshSkinInfo(mesh_id); - -        LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. +        std::array<S32, LLModel::NUM_LODS> pending_lods; +        bool has_pending_lods = false; +        { +            LLMutexLock lock(mPendingMutex); // make sure only one thread access mPendingLOD at the same time. +            pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); +            if (iter != mPendingLOD.end()) +            { +                pending_lods = iter->second; +                mPendingLOD.erase(iter); +                has_pending_lods = true; +            } +        }          //check for pending requests -        pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); -        if (iter != mPendingLOD.end()) +        if (has_pending_lods)          { -            for (U32 i = 0; i < iter->second.size(); ++i) +            for (S32 i = 0; i < pending_lods.size(); ++i)              { -                LODRequest req(mesh_params, iter->second[i]); -                mLODReqQ.push(req); -                LLMeshRepository::sLODProcessing++; +                if (pending_lods[i] > 1) +                { +                    // mLoadingMeshes should be protecting from dupplciates, but looks +                    // like this is possible if object rezzes, unregisterMesh, then +                    // rezzes again before first request completes. +                    // mLoadingMeshes might need to change a bit to not rerequest if +                    // mesh is already pending. +                    // +                    // Todo: Improve mLoadingMeshes and once done turn this into an assert. +                    // Low priority since such situation should be relatively rare +                    LL_INFOS(LOG_MESH) << "Multiple dupplicate requests for mesd ID:  " << mesh_id << " LOD: " << i +                        << LL_ENDL; +                } +                if (pending_lods[i] > 0 && lod_size[i] > 0) +                { +                    // try to load from data we just received +                    bool request_lod = true; +                    S32 offset = (S32)header_size + lod_offset[i]; +                    if (offset + lod_size[i] <= data_size) +                    { +                        // initial request is 4096 bytes, it's big enough to fit this lod +                        request_lod = lodReceived(mesh_params, i, data + offset, lod_size[i]) != MESH_OK; +                    } +                    if (request_lod) +                    { +                        LLMutexLock lock(mMutex); +                        LODRequest req(mesh_params, i); +                        mLODReqQ.push(req); +                        LLMeshRepository::sLODProcessing++; +                    } +                }              } -            mPendingLOD.erase(iter);          }      } @@ -1995,8 +2295,16 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p          if (volume->getNumFaces() > 0)          {              // if we have a valid SkinInfo, cache per-joint bounding boxes for this LOD -            LLMeshSkinInfo* skin_info = mSkinMap[mesh_params.getSculptID()]; -            if (skin_info && isAgentAvatarValid()) +            LLPointer<LLMeshSkinInfo> skin_info = nullptr; +            { +                LLMutexLock lock(mSkinMapMutex); +                skin_map::iterator iter = mSkinMap.find(mesh_params.getSculptID()); +                if (iter != mSkinMap.end()) +                { +                    skin_info = iter->second; +                } +            } +            if (skin_info.notNull() && isAgentAvatarValid())              {                  for (S32 i = 0; i < volume->getNumFaces(); ++i)                  { @@ -2008,7 +2316,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p              LoadedMesh mesh(volume, mesh_params, lod);              { -                LLMutexLock lock(mMutex); +                LLMutexLock lock(mLoadedMutex);                  mLoadedQ.push_back(mesh);                  // LLPointer is not thread safe, since we added this pointer into                  // threaded list, make sure counter gets decreased inside mutex lock @@ -2026,6 +2334,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p  bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size)  { +    LL_PROFILE_ZONE_SCOPED;      LLSD skin;      if (data_size > 0) @@ -2060,12 +2369,15 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat          // copy the skin info for the background thread so we can use it          // to calculate per-joint bounding boxes when volumes are loaded -        mSkinMap[mesh_id] = new LLMeshSkinInfo(*info); +        { +            LLMutexLock lock(mSkinMapMutex); +            mSkinMap[mesh_id] = new LLMeshSkinInfo(*info); +        }          {              // Move the LLPointer in to the skin info queue to avoid reference              // count modification after we leave the lock -            LLMutexLock lock(mMutex); +            LLMutexLock lock(mLoadedMutex);              mSkinInfoQ.emplace_back(std::move(info));          }      } @@ -2075,6 +2387,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat  bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)  { +    LL_PROFILE_ZONE_SCOPED;      LLSD decomp;      if (data_size > 0) @@ -2101,7 +2414,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3          LLModel::Decomposition* d = new LLModel::Decomposition(decomp);          d->mMeshID = mesh_id;          { -            LLMutexLock lock(mMutex); +            LLMutexLock lock(mLoadedMutex);              mDecompositionQ.push_back(d);          }      } @@ -2111,6 +2424,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3  EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size)  { +    LL_PROFILE_ZONE_SCOPED;      LLSD physics_shape;      LLModel::Decomposition* d = new LLModel::Decomposition(); @@ -2150,7 +2464,7 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_      }      { -        LLMutexLock lock(mMutex); +        LLMutexLock lock(mLoadedMutex);          mDecompositionQ.push_back(d);      }      return MESH_OK; @@ -2202,7 +2516,6 @@ LLMeshUploadThread::~LLMeshUploadThread()      mHttpRequest = NULL;      delete mMutex;      mMutex = NULL; -  }  LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread) @@ -2943,15 +3256,17 @@ void LLMeshRepoThread::notifyLoadedMeshes()          return;      } +    LL_PROFILE_ZONE_SCOPED; +      if (!mLoadedQ.empty())      {          std::deque<LoadedMesh> loaded_queue; -        mMutex->lock(); +        mLoadedMutex->lock();          if (!mLoadedQ.empty())          {              loaded_queue.swap(mLoadedQ); -            mMutex->unlock(); +            mLoadedMutex->unlock();              update_metrics = true; @@ -2960,40 +3275,47 @@ void LLMeshRepoThread::notifyLoadedMeshes()              {                  if (mesh.mVolume->getNumVolumeFaces() > 0)                  { -                    gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); +                    gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume, mesh.mLOD);                  }                  else                  { -                    gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, -                        LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); +                    gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, mesh.mLOD, LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));                  }              }          } +        else +        { +            mLoadedMutex->unlock(); +        }      }      if (!mUnavailableQ.empty())      {          std::deque<LODRequest> unavil_queue; -        mMutex->lock(); +        mLoadedMutex->lock();          if (!mUnavailableQ.empty())          {              unavil_queue.swap(mUnavailableQ); -            mMutex->unlock(); +            mLoadedMutex->unlock();              update_metrics = true;              // Process the elements free of the lock              for (const auto& req : unavil_queue)              { -                gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); +                gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD, req.mLOD);              }          } +        else +        { +            mLoadedMutex->unlock(); +        }      }      if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty())      { -        if (mMutex->trylock()) +        if (mLoadedMutex->trylock())          {              std::deque<LLPointer<LLMeshSkinInfo>> skin_info_q;              std::deque<UUIDBasedRequest> skin_info_unavail_q; @@ -3014,7 +3336,7 @@ void LLMeshRepoThread::notifyLoadedMeshes()                  decomp_q.swap(mDecompositionQ);              } -            mMutex->unlock(); +            mLoadedMutex->unlock();              // Process the elements free of the lock              while (! skin_info_q.empty()) @@ -3051,9 +3373,11 @@ S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo      if (iter != mMeshHeader.end())      { -        auto& header = iter->second.second; - -        return LLMeshRepository::getActualMeshLOD(header, lod); +        auto& header = iter->second; +        if (header.mHeaderSize > 0) +        { +            return LLMeshRepository::getActualMeshLOD(header, lod); +        }      }      return lod; @@ -3220,7 +3544,10 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo          processData(body, body_offset, data, static_cast<S32>(data_size) - body_offset); -        delete [] data; +        if (mHasDataOwnership) +        { +            delete [] data; +        }      }      // Release handler @@ -3253,7 +3580,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)                         << LL_ENDL;      // Can't get the header so none of the LODs will be available -    LLMutexLock lock(gMeshRepo.mThread->mMutex); +    LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);      for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)      {          gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3263,6 +3590,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)  void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,                                        U8 * data, S32 data_size)  { +    LL_PROFILE_ZONE_SCOPED;      LLUUID mesh_id = mMeshParams.getSculptID();      bool success = (!MESH_HEADER_PROCESS_FAILED)          && ((data != NULL) == (data_size > 0)); // if we have data but no size or have size but no data, something is wrong; @@ -3282,7 +3610,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b                             << LL_ENDL;          // Can't get the header so none of the LODs will be available -        LLMutexLock lock(gMeshRepo.mThread->mMutex); +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);          for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)          {              gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3298,8 +3626,8 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b          LLMeshRepoThread::mesh_header_map::iterator iter = gMeshRepo.mThread->mMeshHeader.find(mesh_id);          if (iter != gMeshRepo.mThread->mMeshHeader.end())          { -            header_bytes = (S32)iter->second.first; -            header = iter->second.second; +            header = iter->second; +            header_bytes = header.mHeaderSize;          }          if (header_bytes > 0 @@ -3324,7 +3652,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b              // LLSD is smart and can work like smart pointer, is not thread safe.              gMeshRepo.mThread->mHeaderMutex->unlock(); -            S32 bytes = lod_bytes + header_bytes; +            S32 bytes = lod_bytes + header_bytes + CACHE_PREAMBLE_SIZE;              // It's possible for the remote asset to have more data than is needed for the local cache @@ -3337,6 +3665,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b                  LLMeshRepository::sCacheBytesWritten += data_size;                  ++LLMeshRepository::sCacheWrites; +                // write preamble +                U32 flags = header.getFlags(); +                write_preamble(file, header_bytes, flags); + +                // write header                  file.write(data, data_size);                  S32 remaining = bytes - file.tell(); @@ -3359,7 +3692,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b              gMeshRepo.mThread->mHeaderMutex->unlock();              // headerReceived() parsed header, but header's data is invalid so none of the LODs will be available -            LLMutexLock lock(gMeshRepo.mThread->mMutex); +            LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);              for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i)              {                  gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3388,9 +3721,64 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)                         << " (" << status.toTerseString() << ").  Not retrying."                         << LL_ENDL; -    LLMutexLock lock(gMeshRepo.mThread->mMutex); +    LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);      gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));  } +void LLMeshLODHandler::processLod(U8* data, S32 data_size) +{ +    EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size); +    if (result == MESH_OK) +    { +        // good fetch from sim, write to cache +        LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + +        S32 offset = mOffset + CACHE_PREAMBLE_SIZE; +        S32 size = mRequestedBytes; + +        if (file.getSize() >= offset + size) +        { +            S32 header_bytes = 0; +            U32 flags = 0; +            { +                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); +                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                { +                    LLMeshHeader& header = header_it->second; +                    // update header +                    if (!header.mLodInCache[mLOD]) +                    { +                        header.mLodInCache[mLOD] = true; +                        header_bytes = header.mHeaderSize; +                        flags = header.getFlags(); +                    } +                    // todo: handle else because we shouldn't have requested twice? +                } +            } +            if (flags > 0) +            { +                write_preamble(file, header_bytes, flags); +            } + +            file.seek(offset, 0); +            file.write(data, size); +            LLMeshRepository::sCacheBytesWritten += size; +            ++LLMeshRepository::sCacheWrites; +        } +    } +    else +    { +        LL_WARNS(LOG_MESH) << "Error during mesh LOD processing.  ID:  " << mMeshParams.getSculptID() +            << ", Reason: " << result +            << " LOD: " << mLOD +            << " Data size: " << data_size +            << " Not retrying." +            << LL_ENDL; +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); +        gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); +    } +}  void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,                                     U8 * data, S32 data_size) @@ -3399,33 +3787,26 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body      if ((!MESH_LOD_PROCESS_FAILED)          && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong      { -        EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size); -        if (result == MESH_OK) +        LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this(); +        bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post( +            [shrd_handler, data, data_size] +            ()          { -            // good fetch from sim, write to cache -            LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - -            S32 offset = mOffset; -            S32 size = mRequestedBytes; +            LLMeshLODHandler* handler = (LLMeshLODHandler * )shrd_handler.get(); +            handler->processLod(data, data_size); +            delete[] data; +        }); -            if (file.getSize() >= offset+size) -            { -                file.seek(offset); -                file.write(data, size); -                LLMeshRepository::sCacheBytesWritten += size; -                ++LLMeshRepository::sCacheWrites; -            } +        if (posted) +        { +            // ownership of data was passed to the lambda +            mHasDataOwnership = false;          }          else          { -            LL_WARNS(LOG_MESH) << "Error during mesh LOD processing.  ID:  " << mMeshParams.getSculptID() -                               << ", Reason: " << result -                               << " LOD: " << mLOD -                               << " Data size: " << data_size -                               << " Not retrying." -                               << LL_ENDL; -            LLMutexLock lock(gMeshRepo.mThread->mMutex); -            gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); +            // mesh thread dies later than event queue, so this is normal +            LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL; +            processLod(data, data_size);          }      }      else @@ -3435,7 +3816,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body                             << " LOD: " << mLOD                             << " Data size: " << data_size                             << LL_ENDL; -        LLMutexLock lock(gMeshRepo.mThread->mMutex); +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);          gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));      }  } @@ -3446,6 +3827,7 @@ LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()      {          LL_WARNS(LOG_MESH) << "deleting unprocessed request handler (may be ok on exit)" << LL_ENDL;      } +    LLMeshRepoThread::decActiveSkinRequests();  }  void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) @@ -3454,38 +3836,98 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)                         << ", Reason:  " << status.toString()                         << " (" << status.toTerseString() << ").  Not retrying."                         << LL_ENDL; -        LLMutexLock lock(gMeshRepo.mThread->mMutex); +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);          gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);  } -void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, -                                        U8 * data, S32 data_size) +void LLMeshSkinInfoHandler::processSkin(U8* data, S32 data_size)  { -    LL_PROFILE_ZONE_SCOPED; -    if ((!MESH_SKIN_INFO_PROCESS_FAILED) -        && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong -        && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) +    if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))      {          // good fetch from sim, write to cache          LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); -        S32 offset = mOffset; +        S32 offset = mOffset + CACHE_PREAMBLE_SIZE;          S32 size = mRequestedBytes; -        if (file.getSize() >= offset+size) +        if (file.getSize() >= offset + size)          {              LLMeshRepository::sCacheBytesWritten += size;              ++LLMeshRepository::sCacheWrites; -            file.seek(offset); + +            S32 header_bytes = 0; +            U32 flags = 0; +            { +                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); +                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                { +                    LLMeshHeader& header = header_it->second; +                    // update header +                    if (!header.mSkinInCache) +                    { +                        header.mSkinInCache = true; +                        header_bytes = header.mHeaderSize; +                        flags = header.getFlags(); +                    } +                    // todo: handle else because we shouldn't have requested twice? +                } +            } +            if (flags > 0) +            { +                write_preamble(file, header_bytes, flags); +            } + +            file.seek(offset, 0);              file.write(data, size);          }      }      else      {          LL_WARNS(LOG_MESH) << "Error during mesh skin info processing.  ID:  " << mMeshID +            << ", Unknown reason.  Not retrying." +            << LL_ENDL; +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); +        gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); +    } +} + +void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, +                                        U8 * data, S32 data_size) +{ +    LL_PROFILE_ZONE_SCOPED; +    if ((!MESH_SKIN_INFO_PROCESS_FAILED) +        && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong +    { +        LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this(); +        bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post( +            [shrd_handler, data, data_size] +            () +        { +            LLMeshSkinInfoHandler* handler = (LLMeshSkinInfoHandler*)shrd_handler.get(); +            handler->processSkin(data, data_size); +            delete[] data; +        }); + +        if (posted) +        { +            // ownership of data was passed to the lambda +            mHasDataOwnership = false; +        } +        else +        { +            // mesh thread dies later than event queue, so this is normal +            LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL; +            processSkin(data, data_size); +        } +    } +    else +    { +        LL_WARNS(LOG_MESH) << "Error during mesh skin info processing.  ID:  " << mMeshID                             << ", Unknown reason.  Not retrying."                             << LL_ENDL; -        LLMutexLock lock(gMeshRepo.mThread->mMutex); +        LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex);          gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);      }  } @@ -3519,14 +3961,39 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S          // good fetch from sim, write to cache          LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); -        S32 offset = mOffset; +        S32 offset = mOffset + CACHE_PREAMBLE_SIZE;          S32 size = mRequestedBytes;          if (file.getSize() >= offset+size)          {              LLMeshRepository::sCacheBytesWritten += size;              ++LLMeshRepository::sCacheWrites; -            file.seek(offset); + +            S32 header_bytes = 0; +            U32 flags = 0; +            { +                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); +                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                { +                    LLMeshHeader& header = header_it->second; +                    // update header +                    if (!header.mPhysicsConvexInCache) +                    { +                        header.mPhysicsConvexInCache = true; +                        header_bytes = header.mHeaderSize; +                        flags = header.getFlags(); +                    } +                    // todo: handle else because we shouldn't have requested twice? +                } +            } +            if (flags > 0) +            { +                write_preamble(file, header_bytes, flags); +            } + +            file.seek(offset, 0);              file.write(data, size);          }      } @@ -3567,14 +4034,39 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3          // good fetch from sim, write to cache for caching          LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); -        S32 offset = mOffset; +        S32 offset = mOffset + CACHE_PREAMBLE_SIZE;          S32 size = mRequestedBytes;          if (file.getSize() >= offset+size)          {              LLMeshRepository::sCacheBytesWritten += size;              ++LLMeshRepository::sCacheWrites; -            file.seek(offset); + +            S32 header_bytes = 0; +            U32 flags = 0; +            { +                LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + +                LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); +                if (header_it != gMeshRepo.mThread->mMeshHeader.end()) +                { +                    LLMeshHeader& header = header_it->second; +                    // update header +                    if (!header.mPhysicsMeshInCache) +                    { +                        header.mPhysicsMeshInCache = true; +                        header_bytes = header.mHeaderSize; +                        flags = header.getFlags(); +                    } +                    // todo: handle else because we shouldn't have requested twice? +                } +            } +            if (flags > 0) +            { +                write_preamble(file, header_bytes, flags); +            } + +            file.seek(offset, 0);              file.write(data, size);          }      } @@ -3636,6 +4128,7 @@ void LLMeshRepository::shutdown()      }      mThread->mSignal->broadcast(); +    mThread->mMeshThreadPool->close();      while (!mThread->isStopped())      { @@ -3710,24 +4203,24 @@ void LLMeshRepository::unregisterMesh(LLVOVolume* vobj)      }  } -S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod) +S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 new_lod, S32 last_lod)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);      // Manage time-to-load metrics for mesh download operations.      metricsProgress(1); -    if (detail < 0 || detail >= LLVolumeLODGroup::NUM_LODS) +    if (new_lod < 0 || new_lod >= LLVolumeLODGroup::NUM_LODS)      { -        return detail; +        return new_lod;      }      {          LLMutexLock lock(mMeshMutex);          //add volume to list of loading meshes          const auto& mesh_id = mesh_params.getSculptID(); -        mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id); -        if (iter != mLoadingMeshes[detail].end()) +        mesh_load_map::iterator iter = mLoadingMeshes[new_lod].find(mesh_id); +        if (iter != mLoadingMeshes[new_lod].end())          { //request pending for this mesh, append volume id to list              auto it = std::find(iter->second.begin(), iter->second.end(), vobj);              if (it == iter->second.end()) { @@ -3737,8 +4230,8 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para          else          {              //first request for this mesh -            mLoadingMeshes[detail][mesh_id].push_back(vobj); -            mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail)); +            mLoadingMeshes[new_lod][mesh_id].push_back(vobj); +            mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, new_lod));              LLMeshRepository::sLODPending++;          }      } @@ -3767,7 +4260,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para              }              //next, see what the next lowest LOD available might be -            for (S32 i = detail-1; i >= 0; --i) +            for (S32 i = new_lod -1; i >= 0; --i)              {                  LLVolume* lod = group->refLOD(i);                  if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) @@ -3780,7 +4273,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para              }              //no lower LOD is a available, is a higher lod available? -            for (S32 i = detail+1; i < LLVolumeLODGroup::NUM_LODS; ++i) +            for (S32 i = new_lod+1; i < LLVolumeLODGroup::NUM_LODS; ++i)              {                  LLVolume* lod = group->refLOD(i);                  if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) @@ -3794,7 +4287,51 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para          }      } -    return detail; +    return new_lod; +} + +F32 calculate_score(LLVOVolume* object) +{ +    if (!object) +    { +        return -1.f; +    } +    LLDrawable* drawable = object->mDrawable; +    if (!drawable) +    { +        return -1; +    } +    if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment()) +    { +        LLVOAvatar* avatar = object->getAvatar(); +        LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr; +        if (avatar && av_drawable) +        { +            // See LLVOVolume::calcLOD() +            F32 radius; +            if (avatar->isControlAvatar()) +            { +                const LLVector3* box = avatar->getLastAnimExtents(); +                LLVector3 diag = box[1] - box[0]; +                radius = diag.magVec() * 0.5f; +            } +            else +            { +                // Volume in a rigged mesh attached to a regular avatar. +                const LLVector3* box = avatar->getLastAnimExtents(); +                LLVector3 diag = box[1] - box[0]; +                radius = diag.magVec(); + +                if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData()) +                { +                    // slightly deprioritize avatars that are still receiving data +                    radius *= 0.9f; +                } +            } +            return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f); +        } +    } +    return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f);  }  void LLMeshRepository::notifyLoadedMeshes() @@ -3918,6 +4455,7 @@ void LLMeshRepository::notifyLoadedMeshes()              // erase from background thread              mThread->mWorkQueue.post([=, this]()                  { +                    LLMutexLock(mThread->mSkinMapMutex);                      mThread->mSkinMap.erase(id);                  });          } @@ -3974,8 +4512,12 @@ void LLMeshRepository::notifyLoadedMeshes()              mUploadErrorQ.pop();          } -        S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests; -        if (active_count < LLMeshRepoThread::sRequestLowWater) +        // mPendingRequests go into queues, queues go into active http requests. +        // Checking sRequestHighWater to keep queues at least somewhat populated +        // for faster transition into http +        S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests + LLMeshRepoThread::sActiveSkinRequests; +        active_count += (S32)(mThread->mLODReqQ.size() + mThread->mHeaderReqQ.size() + mThread->mSkinInfoQ.size()); +        if (active_count < LLMeshRepoThread::sRequestHighWater)          {              S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; @@ -3996,50 +4538,69 @@ void LLMeshRepository::notifyLoadedMeshes()                          F32 max_score = 0.f;                          for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)                          { -                            LLVOVolume* object = *obj_iter; -                            if (object) +                            F32 cur_score = calculate_score(*obj_iter); +                            if (cur_score >= 0.f)                              { -                                LLDrawable* drawable = object->mDrawable; -                                if (drawable) -                                { -                                    F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); -                                    max_score = llmax(max_score, cur_score); -                                } +                                max_score = llmax(max_score, cur_score);                              }                          }                          score_map[iter->first] = max_score;                      }                  } +                for (mesh_load_map::iterator iter = mLoadingSkins.begin(); iter != mLoadingSkins.end(); ++iter) +                { +                    F32 max_score = 0.f; +                    for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) +                    { +                        F32 cur_score = calculate_score(*obj_iter); +                        if (cur_score >= 0.f) +                        { +                            max_score = llmax(max_score, cur_score); +                        } +                    } + +                    score_map[iter->first] = max_score; +                }                  //set "score" for pending requests -                for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) +                for (std::unique_ptr<PendingRequestBase>& req_p : mPendingRequests)                  { -                    iter->mScore = score_map[iter->mMeshParams.getSculptID()]; +                    req_p->setScore(score_map[req_p->getId()]);                  }                  //sort by "score"                  std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, -                                  mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); +                                  mPendingRequests.end(), PendingRequestBase::CompareScoreGreater());              } -              while (!mPendingRequests.empty() && push_count > 0)              { -                LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); -                mThread->loadMeshLOD(request.mMeshParams, request.mLOD); +                std::unique_ptr<PendingRequestBase>& req_p = mPendingRequests.front(); +                switch (req_p->getRequestType()) +                { +                case MESH_REQUEST_LOD: +                    { +                        PendingRequestLOD* lod = (PendingRequestLOD*)req_p.get(); +                        mThread->loadMeshLOD(lod->mMeshParams, lod->mLOD); +                        LLMeshRepository::sLODPending--; +                        break; +                    } +                case MESH_REQUEST_SKIN: +                    { +                        PendingRequestUUID* skin = (PendingRequestUUID*)req_p.get(); +                        mThread->loadMeshSkinInfo(skin->getId()); +                        break; +                    } + +                default: +                    LL_ERRS() << "Unknown request type in LLMeshRepository::notifyLoadedMeshes" << LL_ENDL; +                    break; +                }                  mPendingRequests.erase(mPendingRequests.begin()); -                LLMeshRepository::sLODPending--;                  push_count--;              }          } -        //send skin info requests -        while (!mPendingSkinRequests.empty()) -        { -            mThread->loadMeshSkinInfo(mPendingSkinRequests.front()); -            mPendingSkinRequests.pop(); -        } -          //send decomposition requests          while (!mPendingDecompositionRequests.empty())          { @@ -4116,15 +4677,14 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom      }  } -void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) +void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod)  { //called from main thread -    S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());      //get list of objects waiting to be notified this mesh is loaded      const auto& mesh_id = mesh_params.getSculptID(); -    mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_id); +    mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id); -    if (volume && obj_iter != mLoadingMeshes[detail].end()) +    if (volume && obj_iter != mLoadingMeshes[lod].end())      {          //make sure target volume is still valid          if (volume->getNumVolumeFaces() <= 0) @@ -4134,6 +4694,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol          }          { //update system volume +            S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());              LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail);              if (sys_volume)              { @@ -4157,22 +4718,22 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol              }          } -        mLoadingMeshes[detail].erase(obj_iter); +        mLoadingMeshes[lod].erase(obj_iter);          LLViewerStatsRecorder::instance().meshLoaded();      }  } -void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod) +void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod)  { //called from main thread      //get list of objects waiting to be notified this mesh is loaded      const auto& mesh_id = mesh_params.getSculptID(); -    mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id); -    if (obj_iter != mLoadingMeshes[lod].end()) +    mesh_load_map::iterator obj_iter = mLoadingMeshes[request_lod].find(mesh_id); +    if (obj_iter != mLoadingMeshes[request_lod].end())      { -        F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); +        F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(volume_lod); -        LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, lod); +        LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, volume_lod);          if (sys_volume)          {              sys_volume->setMeshAssetUnavaliable(true); @@ -4189,12 +4750,12 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,                      obj_volume->getDetail() == detail &&                      obj_volume->getParams() == mesh_params)                  { //should force volume to find most appropriate LOD -                    vobj->setVolume(obj_volume->getParams(), lod); +                    vobj->setVolume(obj_volume->getParams(), volume_lod);                  }              }          } -        mLoadingMeshes[lod].erase(obj_iter); +        mLoadingMeshes[request_lod].erase(obj_iter);      }  } @@ -4231,7 +4792,7 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV              {                  //first request for this mesh                  mLoadingSkins[mesh_id].push_back(requesting_obj); -                mPendingSkinRequests.push(mesh_id); +                mPendingRequests.emplace_back(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN));              }          }      } @@ -4358,7 +4919,7 @@ bool LLMeshRepository::hasSkinInfo(const LLUUID& mesh_id)      return false;  } -bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) +bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) const  {      if (mesh_id.isNull())      { @@ -4368,13 +4929,13 @@ bool LLMeshRepository::hasHeader(const LLUUID& mesh_id)      return mThread->hasHeader(mesh_id);  } -bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) const  {      LLMutexLock lock(mHeaderMutex); -    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); -    if (iter != mMeshHeader.end() && iter->second.first > 0) +    mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id); +    if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0)      { -        LLMeshHeader &mesh = iter->second.second; +        const LLMeshHeader &mesh = iter->second;          if (mesh.mPhysicsMeshSize > 0)          {              return true; @@ -4384,13 +4945,13 @@ bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id)      return false;  } -bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) const  {      LLMutexLock lock(mHeaderMutex); -    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); -    if (iter != mMeshHeader.end() && iter->second.first > 0) +    mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id); +    if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0)      { -        LLMeshHeader& mesh = iter->second.second; +        const LLMeshHeader& mesh = iter->second;          if (mesh.mSkinOffset >= 0              && mesh.mSkinSize > 0)          { @@ -4401,10 +4962,10 @@ bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id)      return false;  } -bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) const  {      LLMutexLock lock(mHeaderMutex); -    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); +    mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id);      return iter != mMeshHeader.end();  } @@ -4419,16 +4980,16 @@ void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3      mUploadWaitList.push_back(thread);  } -S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) +S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) const  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;      if (mThread && mesh_id.notNull() && LLPrimitive::NO_LOD != lod)      {          LLMutexLock lock(mThread->mHeaderMutex); -        LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); -        if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) +        LLMeshRepoThread::mesh_header_map::const_iterator iter = mThread->mMeshHeader.find(mesh_id); +        if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)          { -            const LLMeshHeader& header = iter->second.second; +            const LLMeshHeader& header = iter->second;              if (header.m404)              { @@ -4532,9 +5093,9 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by      {          LLMutexLock lock(mThread->mHeaderMutex);          LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); -        if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) +        if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)          { -            result  = getStreamingCostLegacy(iter->second.second, radius, bytes, bytes_visible, lod, unscaled_value); +            result  = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);          }      }      if (result > 0.f) @@ -4726,7 +5287,7 @@ bool LLMeshCostData::init(const LLMeshHeader& header)  } -S32 LLMeshCostData::getSizeByLOD(S32 lod) +S32 LLMeshCostData::getSizeByLOD(S32 lod) const  {      if (llclamp(lod,0,3) != lod)      { @@ -4735,12 +5296,12 @@ S32 LLMeshCostData::getSizeByLOD(S32 lod)      return mSizeByLOD[lod];  } -S32 LLMeshCostData::getSizeTotal() +S32 LLMeshCostData::getSizeTotal() const  {      return mSizeByLOD[0] + mSizeByLOD[1] + mSizeByLOD[2] + mSizeByLOD[3];  } -F32 LLMeshCostData::getEstTrisByLOD(S32 lod) +F32 LLMeshCostData::getEstTrisByLOD(S32 lod) const  {      if (llclamp(lod,0,3) != lod)      { @@ -4749,12 +5310,12 @@ F32 LLMeshCostData::getEstTrisByLOD(S32 lod)      return mEstTrisByLOD[lod];  } -F32 LLMeshCostData::getEstTrisMax() +F32 LLMeshCostData::getEstTrisMax() const  {      return llmax(mEstTrisByLOD[0], mEstTrisByLOD[1], mEstTrisByLOD[2], mEstTrisByLOD[3]);  } -F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) +F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) const  {      F32 max_distance = 512.f; @@ -4798,7 +5359,7 @@ F32 LLMeshCostData::getRadiusWeightedTris(F32 radius)      return weighted_avg;  } -F32 LLMeshCostData::getEstTrisForStreamingCost() +F32 LLMeshCostData::getEstTrisForStreamingCost() const  {      LL_DEBUGS("StreamingCost") << "tris_by_lod: "                                 << mEstTrisByLOD[0] << ", " @@ -4808,7 +5369,7 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()      F32 charged_tris = mEstTrisByLOD[3];      F32 allowed_tris = mEstTrisByLOD[3]; -    const F32 ENFORCE_FLOOR = 64.0f; +    constexpr F32 ENFORCE_FLOOR = 64.0f;      for (S32 i=2; i>=0; i--)      {          // How many tris can we have in this LOD without affecting land impact? @@ -4825,13 +5386,13 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()      return charged_tris;  } -F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) +F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) const  {      static LLCachedControl<U32> mesh_triangle_budget(gSavedSettings, "MeshTriangleBudget");      return getRadiusWeightedTris(radius)/mesh_triangle_budget*15000.f;  } -F32 LLMeshCostData::getTriangleBasedStreamingCost() +F32 LLMeshCostData::getTriangleBasedStreamingCost() const  {      F32 result = ANIMATED_OBJECT_COST_PER_KTRI * 0.001f * getEstTrisForStreamingCost();      return result; @@ -4846,9 +5407,9 @@ bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data)      {          LLMutexLock lock(mThread->mHeaderMutex);          LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); -        if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) +        if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0)          { -            LLMeshHeader& header = iter->second.second; +            LLMeshHeader& header = iter->second;              bool header_invalid = (header.m404                                     || header.mLodSize[0] <= 0 diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 3d25a4dd78..0d9da32e27 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -63,6 +63,16 @@ typedef enum e_mesh_processing_result_enum      MESH_UNKNOWN  } EMeshProcessingResult; +typedef enum e_mesh_request_type_enum +{ +    MESH_REQUEST_HEADER, +    MESH_REQUEST_LOD, +    MESH_REQUEST_SKIN, +    MESH_REQUEST_DECOMPOSITION, +    MESH_REQUEST_PHYSICS, +    MESH_REQUEST_UKNOWN +} EMeshRequestType; +  class LLMeshUploadData  {  public: @@ -183,7 +193,8 @@ public:  class RequestStats  {  public: -    RequestStats() : mRetries(0) {}; + +    RequestStats() :mRetries(0) {};      void updateTime();      bool canRetry() const; @@ -195,6 +206,67 @@ private:      LLFrameTimer mTimer;  }; + +class PendingRequestBase +{ +public: +    struct CompareScoreGreater +    { +        bool operator()(const std::unique_ptr<PendingRequestBase>& lhs, const std::unique_ptr<PendingRequestBase>& rhs) +        { +            return lhs->mScore > rhs->mScore; // greatest = first +        } +    }; + +    PendingRequestBase() : mScore(0.f) {}; +    virtual ~PendingRequestBase() {} + +    bool operator<(const PendingRequestBase& rhs) const +    { +        return mId < rhs.mId; +    } + +    void setScore(F32 score) { mScore = score; } +    F32 getScore() const { return mScore; } +    LLUUID getId() const { return mId; } +    virtual EMeshRequestType getRequestType() const = 0; + +protected: +    F32 mScore; +    LLUUID mId; +}; + +class PendingRequestLOD : public PendingRequestBase +{ +public: +    LLVolumeParams  mMeshParams; +    S32 mLOD; + +    PendingRequestLOD(const LLVolumeParams& mesh_params, S32 lod) +        : PendingRequestBase(), mMeshParams(mesh_params), mLOD(lod) +    { +        mId = mMeshParams.getSculptID(); +    } + +    EMeshRequestType getRequestType() const override { return MESH_REQUEST_LOD; } +}; + +class PendingRequestUUID : public PendingRequestBase +{ +public: + +    PendingRequestUUID(const LLUUID& id, EMeshRequestType type) +        : PendingRequestBase(), mRequestType(type) +    { +        mId = id; +    } + +    EMeshRequestType getRequestType() const override { return mRequestType; } + +private: +    EMeshRequestType mRequestType; +}; +  class LLMeshHeader  {  public: @@ -235,19 +307,67 @@ public:          m404 = header.has("404");      } +private: + +    enum EDiskCacheFlags { +        FLAG_SKIN = 1 << LLModel::NUM_LODS, +        FLAG_PHYSCONVEX = 1 << (LLModel::NUM_LODS + 1), +        FLAG_PHYSMESH = 1 << (LLModel::NUM_LODS + 2), +    }; +public: +    U32 getFlags() +    { +        U32 flags = 0; +        for (U32 i = 0; i < LLModel::NUM_LODS; i++) +        { +            if (mLodInCache[i]) +            { +                flags |= 1 << i; +            } +        } +        if (mSkinInCache) +        { +            flags |= FLAG_SKIN; +        } +        if (mPhysicsConvexInCache) +        { +            flags |= FLAG_PHYSCONVEX; +        } +        if (mPhysicsMeshInCache) +        { +            flags |= FLAG_PHYSMESH; +        } +        return flags; +    } + +    void setFromFlags(U32 flags) +    { +        for (U32 i = 0; i < LLModel::NUM_LODS; i++) +        { +            mLodInCache[i] = (flags & (1 << i)) != 0; +        } +        mSkinInCache          = (flags & FLAG_SKIN) != 0; +        mPhysicsConvexInCache = (flags & FLAG_PHYSCONVEX) != 0; +        mPhysicsMeshInCache   = (flags & FLAG_PHYSMESH) != 0; +    }      S32 mVersion = -1;      S32 mSkinOffset = -1;      S32 mSkinSize = -1; +    bool mSkinInCache = false;      S32 mPhysicsConvexOffset = -1;      S32 mPhysicsConvexSize = -1; +    bool mPhysicsConvexInCache = false;      S32 mPhysicsMeshOffset = -1;      S32 mPhysicsMeshSize = -1; +    bool mPhysicsMeshInCache = false; -    S32 mLodOffset[4] = { -1 }; -    S32 mLodSize[4] = { -1 }; +    S32 mLodOffset[LLModel::NUM_LODS] = { -1 }; +    S32 mLodSize[LLModel::NUM_LODS] = { -1 }; +    bool mLodInCache[LLModel::NUM_LODS] = { false }; +    S32 mHeaderSize = -1;      bool m404 = false;  }; @@ -258,6 +378,7 @@ public:      static std::atomic<S32> sActiveHeaderRequests;      static std::atomic<S32> sActiveLODRequests; +    static std::atomic<S32> sActiveSkinRequests;      static U32 sMaxConcurrentRequests;      static S32 sRequestLowWater;      static S32 sRequestHighWater; @@ -265,10 +386,13 @@ public:      LLMutex*    mMutex;      LLMutex*    mHeaderMutex; +    LLMutex*    mLoadedMutex; +    LLMutex*    mPendingMutex; +    LLMutex*    mSkinMapMutex;      LLCondition* mSignal;      //map of known mesh headers -    typedef boost::unordered_map<LLUUID, std::pair<U32, LLMeshHeader>> mesh_header_map; // pair is header_size and data +    typedef boost::unordered_map<LLUUID, LLMeshHeader> mesh_header_map; // pair is header_size and data      mesh_header_map mMeshHeader;      class HeaderRequest : public RequestStats @@ -292,19 +416,10 @@ public:      public:          LLVolumeParams  mMeshParams;          S32 mLOD; -        F32 mScore;          LODRequest(const LLVolumeParams&  mesh_params, S32 lod) -            : RequestStats(), mMeshParams(mesh_params), mLOD(lod), mScore(0.f) -        { -        } -    }; - -    struct CompareScoreGreater -    { -        bool operator()(const LODRequest& lhs, const LODRequest& rhs) +            : RequestStats(), mMeshParams(mesh_params), mLOD(lod)          { -            return lhs.mScore > rhs.mScore; // greatest = first          }      }; @@ -369,7 +484,7 @@ public:      std::deque<LoadedMesh> mLoadedQ;      //map of pending header requests and currently desired LODs -    typedef std::unordered_map<LLUUID, std::vector<S32> > pending_lod_map; +    typedef std::unordered_map<LLUUID, std::array<S32, LLModel::NUM_LODS> > pending_lod_map;      pending_lod_map mPendingLOD;      // map of mesh ID to skin info (mirrors LLMeshRepository::mSkinMap) @@ -379,6 +494,8 @@ public:      // workqueue for processing generic requests      LL::WorkQueue mWorkQueue; +    // lods have their own thread due to costly cacheOptimize() calls +    std::unique_ptr<LL::ThreadPool> mMeshThreadPool;      // llcorehttp library interface objects.      LLCore::HttpStatus                  mHttpStatus; @@ -402,16 +519,16 @@ public:      void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);      void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); -    bool fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry = true); -    bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true); -    EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); +    bool fetchMeshHeader(const LLVolumeParams& mesh_params); +    bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); +    EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags = 0);      EMeshProcessingResult lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);      bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);      bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size);      EMeshProcessingResult physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size); -    bool hasPhysicsShapeInHeader(const LLUUID& mesh_id); -    bool hasSkinInfoInHeader(const LLUUID& mesh_id); -    bool hasHeader(const LLUUID& mesh_id); +    bool hasPhysicsShapeInHeader(const LLUUID& mesh_id) const; +    bool hasSkinInfoInHeader(const LLUUID& mesh_id) const; +    bool hasHeader(const LLUUID& mesh_id) const;      void notifyLoadedMeshes();      S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); @@ -422,7 +539,7 @@ public:      //send request for skin info, returns true if header info exists      //  (should hold onto mesh_id and try again later if header info does not exist) -    bool fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry = true); +    bool fetchMeshSkinInfo(const LLUUID& mesh_id);      //send request for decomposition, returns true if header info exists      //  (should hold onto mesh_id and try again later if header info does not exist) @@ -436,6 +553,8 @@ public:      static void decActiveLODRequests();      static void incActiveHeaderRequests();      static void decActiveHeaderRequests(); +    static void incActiveSkinRequests(); +    static void decActiveSkinRequests();      // Set the caps strings and preferred version for constructing      // mesh fetch URLs. @@ -456,6 +575,14 @@ private:      LLCore::HttpHandle getByteRange(const std::string & url,                                      size_t offset, size_t len,                                      const LLCore::HttpHandler::ptr_t &handler); + +    // Mutex: acquires mPendingMutex, mMutex and mHeaderMutex as needed +    void loadMeshLOD(const LLUUID &mesh_id, const LLVolumeParams& mesh_params, S32 lod); + +    // Threads:  Repo thread only +    U8* getDiskCacheBuffer(S32 size); +    S32 mDiskCacheBufferSize = 0; +    U8* mDiskCacheBuffer = nullptr;  }; @@ -568,35 +695,35 @@ public:      bool init(const LLMeshHeader& header);      // Size for given LOD -    S32 getSizeByLOD(S32 lod); +    S32 getSizeByLOD(S32 lod) const;      // Sum of all LOD sizes. -    S32 getSizeTotal(); +    S32 getSizeTotal() const;      // Estimated triangle counts for the given LOD. -    F32 getEstTrisByLOD(S32 lod); +    F32 getEstTrisByLOD(S32 lod) const;      // Estimated triangle counts for the largest LOD. Typically this      // is also the "high" LOD, but not necessarily. -    F32 getEstTrisMax(); +    F32 getEstTrisMax() const;      // Triangle count as computed by original streaming cost      // formula. Triangles in each LOD are weighted based on how      // frequently they will be seen.      // This was called "unscaled_value" in the original getStreamingCost() functions. -    F32 getRadiusWeightedTris(F32 radius); +    F32 getRadiusWeightedTris(F32 radius) const;      // Triangle count used by triangle-based cost formula. Based on      // triangles in highest LOD plus potentially partial charges for      // lower LODs depending on complexity. -    F32 getEstTrisForStreamingCost(); +    F32 getEstTrisForStreamingCost() const;      // Streaming cost. This should match the server-side calculation      // for the corresponding volume. -    F32 getRadiusBasedStreamingCost(F32 radius); +    F32 getRadiusBasedStreamingCost(F32 radius) const;      // New streaming cost formula, currently only used for animated objects. -    F32 getTriangleBasedStreamingCost(); +    F32 getTriangleBasedStreamingCost() const;  private:      // From the "size" field of the mesh header. LOD 0=lowest, 3=highest. @@ -620,12 +747,12 @@ public:      static U32 sLODPending;      static U32 sLODProcessing;      static U32 sCacheBytesRead; -    static U32 sCacheBytesWritten; +    static std::atomic<U32> sCacheBytesWritten;      static U32 sCacheBytesHeaders;      static U32 sCacheBytesSkins;      static U32 sCacheBytesDecomps;      static U32 sCacheReads; -    static U32 sCacheWrites; +    static std::atomic<U32> sCacheWrites;      static U32 sMaxLockHoldoffs;                // Maximum sequential locking failures      static LLDeadmanTimer sQuiescentTimer;      // Time-to-complete-mesh-downloads after significant events @@ -646,11 +773,11 @@ public:      void unregisterMesh(LLVOVolume* volume);      //mesh management functions -    S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); +    S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 new_lod = 0, S32 last_lod = -1);      void notifyLoadedMeshes(); -    void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume); -    void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod); +    void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod); +    void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod);      void notifySkinInfoReceived(LLMeshSkinInfo* info);      void notifySkinInfoUnavailable(const LLUUID& info);      void notifyDecompositionReceived(LLModel::Decomposition* info); @@ -662,7 +789,7 @@ public:      void fetchPhysicsShape(const LLUUID& mesh_id);      bool hasPhysicsShape(const LLUUID& mesh_id);      bool hasSkinInfo(const LLUUID& mesh_id); -    bool hasHeader(const LLUUID& mesh_id); +    bool hasHeader(const LLUUID& mesh_id) const;      void buildHull(const LLVolumeParams& params, S32 detail);      void buildPhysicsMesh(LLModel::Decomposition& decomp); @@ -676,7 +803,7 @@ public:                       LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()),                       LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>())); -    S32 getMeshSize(const LLUUID& mesh_id, S32 lod); +    S32 getMeshSize(const LLUUID& mesh_id, S32 lod) const;      // Quiescent timer management, main thread only.      static void metricsStart(); @@ -684,7 +811,7 @@ public:      static void metricsProgress(unsigned int count);      static void metricsUpdate(); -    typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map; +    typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map;      mesh_load_map mLoadingMeshes[4];      typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map; @@ -695,15 +822,13 @@ public:      LLMutex*                    mMeshMutex; -    std::vector<LLMeshRepoThread::LODRequest> mPendingRequests; +    typedef std::vector <std::unique_ptr<PendingRequestBase> > pending_requests_vec; +    pending_requests_vec mPendingRequests;      //list of mesh ids awaiting skin info -    typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map; +    typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map;      skin_load_map mLoadingSkins; -    //list of mesh ids that need to send skin info fetch requests -    std::queue<LLUUID> mPendingSkinRequests; -      //list of mesh ids awaiting decompositions      std::unordered_set<LLUUID> mLoadingDecompositions; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 6c3f3849ce..eb0e9ef4bc 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -297,16 +297,62 @@ void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is  // exported functionality  // +void do_startup_frame() +{ +    // Until after STATE_AGENT_SEND we don't get very many UDP packets to poll the socket, +    // and after STATE_PRECACHE the LLAppViewer::idleNetwork() will do UDP processing, +    // so we only bother to process between those two states. +    EStartupState state = LLStartUp::getStartupState(); +    if (state > STATE_AGENT_SEND && state < STATE_PRECACHE) +    { +        // drain the UDP socket... +        U64 t0 = totalTime(); +        constexpr U64 MAX_STARTUP_FRAME_TIME = 2000; // usec +        constexpr U64 MAX_STARTUP_FRAME_MESSAGES = 100; +        S32 num_messages = 0; +        bool needs_drain = false; +        LockMessageChecker lmc(gMessageSystem); +        while (lmc.checkAllMessages(gFrameCount, gServicePump)) +        { +            if (gDoDisconnect) +            { +                // We're disconnecting, don't process any more messages from the server +                // We're usually disconnecting due to either network corruption or a +                // server going down, so this is OK. +                break; +            } +            if (++num_messages >= MAX_STARTUP_FRAME_MESSAGES +                || (totalTime() - t0) > MAX_STARTUP_FRAME_TIME) +            { +                needs_drain = true; +                break; +            } +        } +        if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0) +        { +             gMessageSystem->drainUdpSocket(); +        } +        lmc.processAcks(); +    } +    // ...then call display_startup() +    display_startup(); +} +  void pump_idle_startup_network(void)  { +    // while there are message to process: +    //     process one then call display_startup() +    S32 num_messages = 0;      {          LockMessageChecker lmc(gMessageSystem);          while (lmc.checkAllMessages(gFrameCount, gServicePump))          {              display_startup(); +            ++num_messages;          }          lmc.processAcks();      } +    // finally call one last display_startup()      display_startup();  } @@ -623,21 +669,6 @@ bool idle_startup()              F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage");              msg->mPacketRing.setDropPercentage(dropPercent); - -            F32 inBandwidth = gSavedSettings.getF32("InBandwidth"); -            F32 outBandwidth = gSavedSettings.getF32("OutBandwidth"); -            if (inBandwidth != 0.f) -            { -                LL_DEBUGS("AppInit") << "Setting packetring incoming bandwidth to " << inBandwidth << LL_ENDL; -                msg->mPacketRing.setUseInThrottle(true); -                msg->mPacketRing.setInBandwidth(inBandwidth); -            } -            if (outBandwidth != 0.f) -            { -                LL_DEBUGS("AppInit") << "Setting packetring outgoing bandwidth to " << outBandwidth << LL_ENDL; -                msg->mPacketRing.setUseOutThrottle(true); -                msg->mPacketRing.setOutBandwidth(outBandwidth); -            }          }          LL_INFOS("AppInit") << "Message System Initialized." << LL_ENDL; @@ -743,7 +774,7 @@ bool idle_startup()          LL_DEBUGS("AppInit") << "STATE_BROWSER_INIT" << LL_ENDL;          std::string msg = LLTrans::getString("LoginInitializingBrowser");          set_startup_status(0.03f, msg.c_str(), gAgent.mMOTD.c_str()); -        display_startup(); +        do_startup_frame();          // LLViewerMedia::initBrowser();          LLStartUp::setStartupState( STATE_LOGIN_SHOW );          return false; @@ -808,7 +839,7 @@ bool idle_startup()                      LL_DEBUGS("AppInit") << "FirstLoginThisInstall off" << LL_ENDL;                  }              } -            display_startup(); +            do_startup_frame();              LLStartUp::setStartupState( STATE_LOGIN_WAIT );     // Wait for user input          }          else @@ -839,7 +870,7 @@ bool idle_startup()          }          LL_DEBUGS("AppInit") << "PeekMessage processed" << LL_ENDL;  #endif -        display_startup(); +        do_startup_frame();          timeout.reset();          return false;      } @@ -854,7 +885,7 @@ bool idle_startup()          // Don't do anything.  Wait for the login view to call the login_callback,          // which will push us to the next state. -        // display() function will be the one to run display_startup() +        // display() function will be the one to run do_startup_frame()          // Sleep so we don't spin the CPU          ms_sleep(1);          return false; @@ -1036,7 +1067,7 @@ bool idle_startup()          auth_desc = LLTrans::getString("LoginInProgress");          set_startup_status(progress, auth_desc, auth_message);          progress += 0.02f; -        display_startup(); +        do_startup_frame();          // Setting initial values...          LLLoginInstance* login = LLLoginInstance::getInstance(); @@ -1073,7 +1104,7 @@ bool idle_startup()          emsg << LLTrans::getString("LoginFailedHeader") << "\n";          if(LLLoginInstance::getInstance()->authFailure())          { -            LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): " +            LL_INFOS("LLStartUp") << "Login failed, LLLoginInstance::getResponse(): "                                    << LLLoginInstance::getInstance()->getResponse() << LL_ENDL;              LLSD response = LLLoginInstance::getInstance()->getResponse();              // Still have error conditions that may need some @@ -1145,7 +1176,7 @@ bool idle_startup()                  // If optional was skipped this case shouldn't                  // be reached. -                LL_INFOS("LLStartup") << "Forcing a quit due to update." << LL_ENDL; +                LL_INFOS("LLStartUp") << "Forcing a quit due to update." << LL_ENDL;                  LLLoginInstance::getInstance()->disconnect();                  LLAppViewer::instance()->forceQuit();              } @@ -1173,7 +1204,7 @@ bool idle_startup()                          }                          catch (LLCertException &cert_exception)                          { -                            LL_WARNS("LLStartup", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL; +                            LL_WARNS("LLStartUp", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL;                              LLSD args;                              args["REASON"] = LLTrans::getString(cert_exception.what()); @@ -1225,7 +1256,7 @@ bool idle_startup()                          // notificatioin message.                          LLSD args;                          args["ERROR_MESSAGE"] = emsg.str(); -                        LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; +                        LL_INFOS("LLStartUp") << "Notification: " << args << LL_ENDL;                          LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done);                      }                  } @@ -1248,7 +1279,7 @@ bool idle_startup()              {                  LLSD args;                  args["ERROR_MESSAGE"] = emsg.str(); -                LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; +                LL_INFOS("LLStartUp") << "Notification: " << args << LL_ENDL;                  LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done);                  transition_back_to_login_panel(emsg.str());                  show_connect_box = true; @@ -1264,71 +1295,71 @@ bool idle_startup()      if (STATE_WORLD_INIT == LLStartUp::getStartupState())      {          set_startup_status(0.30f, LLTrans::getString("LoginInitializingWorld"), gAgent.mMOTD); -        display_startup(); +        do_startup_frame();          // We should have an agent id by this point.          llassert(!(gAgentID == LLUUID::null));          // Finish agent initialization.  (Requires gSavedSettings, builds camera)          gAgent.init(); -        display_startup(); +        do_startup_frame();          gAgentCamera.init(); -        display_startup(); -        display_startup(); +        do_startup_frame(); +        do_startup_frame();          // Since we connected, save off the settings so the user doesn't have to          // type the name/password again if we crash.          gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true);          LLUIColorTable::instance().saveUserSettings(); -        display_startup(); +        do_startup_frame();          //          // Initialize classes w/graphics stuff.          //          LLSurface::initClasses(); -        display_startup(); +        do_startup_frame(); -        display_startup(); +        do_startup_frame();          LLDrawable::initClass(); -        display_startup(); +        do_startup_frame();          // init the shader managers          LLPostProcess::initClass(); -        display_startup(); +        do_startup_frame();          LLAvatarAppearance::initClass("avatar_lad.xml","avatar_skeleton.xml"); -        display_startup(); +        do_startup_frame();          LLViewerObject::initVOClasses(); -        display_startup(); +        do_startup_frame();          // Initialize all our tools.  Must be done after saved settings loaded.          // NOTE: This also is where gToolMgr used to be instantiated before being turned into a singleton.          LLToolMgr::getInstance()->initTools(); -        display_startup(); +        do_startup_frame();          // Pre-load floaters, like the world map, that are slow to spawn          // due to XML complexity.          gViewerWindow->initWorldUI(); -        display_startup(); +        do_startup_frame();          // This is where we used to initialize gWorldp. Original comment said:          // World initialization must be done after above window init          // User might have overridden far clip          LLWorld::getInstance()->setLandFarClip(gAgentCamera.mDrawDistance); -        display_startup(); +        do_startup_frame();          // Before we create the first region, we need to set the agent's mOriginGlobal          // This is necessary because creating objects before this is set will result in a          // bad mPositionAgent cache.          gAgent.initOriginGlobal(from_region_handle(gFirstSimHandle)); -        display_startup(); +        do_startup_frame();          LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim); -        display_startup(); +        do_startup_frame();          LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(gFirstSimHandle);          LL_INFOS("AppInit") << "Adding initial simulator " << regionp->getOriginGlobal() << LL_ENDL; @@ -1337,18 +1368,18 @@ bool idle_startup()          << gFirstSimSeedCap << LL_ENDL;          regionp->setSeedCapability(gFirstSimSeedCap);          LL_DEBUGS("AppInit") << "Waiting for seed grant ...." << LL_ENDL; -        display_startup(); +        do_startup_frame();          // Set agent's initial region to be the one we just created.          gAgent.setRegion(regionp); -        display_startup(); +        do_startup_frame();          // Set agent's initial position, which will be read by LLVOAvatar when the avatar          // object is created.  I think this must be done after setting the region.  JC          gAgent.setPositionAgent(agent_start_position_region); -        display_startup(); +        do_startup_frame();          LLStartUp::initExperiences(); -        display_startup(); +        do_startup_frame();          // If logging should be enebled, turns it on and loads history from disk          // Note: does not happen on init of singleton because preferences can use @@ -1369,7 +1400,7 @@ bool idle_startup()      {          LLStartUp::multimediaInit();          LLStartUp::setStartupState( STATE_FONT_INIT ); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1378,7 +1409,7 @@ bool idle_startup()      {          LLStartUp::fontInit();          LLStartUp::setStartupState( STATE_SEED_GRANTED_WAIT ); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1450,7 +1481,7 @@ bool idle_startup()                  set_startup_status(0.4f, LLTrans::getString("LoginRequestSeedCapGrant"), gAgent.mMOTD.c_str());              }          } -        display_startup(); +        do_startup_frame();          return false;      } @@ -1461,7 +1492,7 @@ bool idle_startup()      //---------------------------------------------------------------------      if (STATE_SEED_CAP_GRANTED == LLStartUp::getStartupState())      { -        display_startup(); +        do_startup_frame();          // These textures are not warrantied to be cached, so needs          // to hapen with caps granted @@ -1470,9 +1501,9 @@ bool idle_startup()          // will init images, should be done with caps, but before gSky.init()          LLEnvironment::getInstance()->initSingleton(); -        display_startup(); +        do_startup_frame();          update_texture_fetch(); -        display_startup(); +        do_startup_frame();          if ( gViewerWindow != NULL)          {   // This isn't the first logon attempt, so show the UI @@ -1480,15 +1511,15 @@ bool idle_startup()          }          gLoginMenuBarView->setVisible( false );          gLoginMenuBarView->setEnabled( false ); -        display_startup(); +        do_startup_frame();          // direct logging to the debug console's line buffer          LLError::logToFixedBuffer(gDebugView->mDebugConsolep); -        display_startup(); +        do_startup_frame();          // set initial visibility of debug console          gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole")); -        display_startup(); +        do_startup_frame();          //          // Set message handlers @@ -1497,23 +1528,23 @@ bool idle_startup()          // register callbacks for messages. . . do this after initial handshake to make sure that we don't catch any unwanted          register_viewer_callbacks(gMessageSystem); -        display_startup(); +        do_startup_frame();          // Debugging info parameters          gMessageSystem->setMaxMessageTime( 0.5f );          // Spam if decoding all msgs takes more than 500 ms -        display_startup(); +        do_startup_frame();          #ifndef LL_RELEASE_FOR_DOWNLOAD              gMessageSystem->setTimeDecodes( true );             // Time the decode of each msg              gMessageSystem->setTimeDecodesSpamThreshold( 0.05f );  // Spam if a single msg takes over 50ms to decode          #endif -        display_startup(); +        do_startup_frame();          gXferManager->registerCallbacks(gMessageSystem); -        display_startup(); +        do_startup_frame();          LLStartUp::initNameCache(); -        display_startup(); +        do_startup_frame();          // update the voice settings *after* gCacheName initialization          // so that we can construct voice UI that relies on the name cache @@ -1521,7 +1552,7 @@ bool idle_startup()          {              LLVoiceClient::getInstance()->updateSettings();          } -        display_startup(); +        do_startup_frame();          // create a container's instance for start a controlling conversation windows          // by the voice's events @@ -1542,12 +1573,12 @@ bool idle_startup()          // register null callbacks for audio until the audio system is initialized          gMessageSystem->setHandlerFuncFast(_PREHASH_SoundTrigger, null_message_callback, NULL);          gMessageSystem->setHandlerFuncFast(_PREHASH_AttachedSound, null_message_callback, NULL); -        display_startup(); +        do_startup_frame();          //reset statistics          LLViewerStats::instance().resetStats(); -        display_startup(); +        do_startup_frame();          //          // Set up region and surface defaults          // @@ -1572,7 +1603,7 @@ bool idle_startup()          LLViewerCamera::getInstance()->setAspect(gViewerWindow->getWorldViewAspectRatio());          // Initialize FOV          LLViewerCamera::getInstance()->setDefaultFOV(gSavedSettings.getF32("CameraAngle")); -        display_startup(); +        do_startup_frame();          // Move agent to starting location. The position handed to us by          // the space server is in global coordinates, but the agent frame @@ -1583,7 +1614,7 @@ bool idle_startup()          gAgent.resetAxes(gAgentStartLookAt);          gAgentCamera.stopCameraAnimation();          gAgentCamera.resetCamera(); -        display_startup(); +        do_startup_frame();          // Initialize global class data needed for surfaces (i.e. textures)          LL_DEBUGS("AppInit") << "Initializing sky..." << LL_ENDL; @@ -1594,7 +1625,7 @@ bool idle_startup()          LLGLState::checkStates(); -        display_startup(); +        do_startup_frame();          LL_DEBUGS("AppInit") << "Decoding images..." << LL_ENDL;          // For all images pre-loaded into viewer cache, init @@ -1608,12 +1639,12 @@ bool idle_startup()          {              F32 frac = (F32)i / (F32)DECODE_TIME_SEC;              set_startup_status(0.45f + frac*0.1f, LLTrans::getString("LoginDecodingImages"), gAgent.mMOTD); -            display_startup(); +            do_startup_frame();              gTextureList.decodeAllImages(1.f);          }          LLStartUp::setStartupState( STATE_WORLD_WAIT ); -        display_startup(); +        do_startup_frame();          // JC - Do this as late as possible to increase likelihood Purify          // will run. @@ -1642,7 +1673,7 @@ bool idle_startup()              NULL);          timeout.reset(); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1669,7 +1700,7 @@ bool idle_startup()      {          LL_DEBUGS("AppInit") << "Connecting to region..." << LL_ENDL;          set_startup_status(0.60f, LLTrans::getString("LoginConnectingToRegion"), gAgent.mMOTD); -        display_startup(); +        do_startup_frame();          // register with the message system so it knows we're          // expecting this message          LLMessageSystem* msg = gMessageSystem; @@ -1683,7 +1714,7 @@ bool idle_startup()              gAssetStorage->setUpstream(regionp->getHost());              gCacheName->setUpstream(regionp->getHost());          } -        display_startup(); +        do_startup_frame();          // Create login effect          // But not on first login, because you can't see your avatar then @@ -1698,7 +1729,7 @@ bool idle_startup()          LLStartUp::setStartupState( STATE_AGENT_WAIT );     // Go to STATE_AGENT_WAIT          timeout.reset(); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1707,35 +1738,13 @@ bool idle_startup()      //---------------------------------------------------------------------      if (STATE_AGENT_WAIT == LLStartUp::getStartupState())      { -        { -            LockMessageChecker lmc(gMessageSystem); -            while (lmc.checkAllMessages(gFrameCount, gServicePump)) -            { -                if (gAgentMovementCompleted) -                { -                    // Sometimes we have more than one message in the -                    // queue. break out of this loop and continue -                    // processing. If we don't, then this could skip one -                    // or more login steps. -                    break; -                } -                else -                { -                    LL_DEBUGS("AppInit") << "Awaiting AvatarInitComplete, got " -                                         << gMessageSystem->getMessageName() << LL_ENDL; -                } -                display_startup(); -            } -            lmc.processAcks(); -        } - -        display_startup(); +        do_startup_frame();          if (gAgentMovementCompleted)          {              LLStartUp::setStartupState( STATE_INVENTORY_SEND );          } -        display_startup(); +        do_startup_frame();          if (!gAgentMovementCompleted && timeout.getElapsedTimeF32() > STATE_AGENT_WAIT_TIMEOUT)          { @@ -1768,7 +1777,7 @@ bool idle_startup()      if (STATE_INVENTORY_SEND == LLStartUp::getStartupState())      {          LL_PROFILE_ZONE_NAMED("State inventory send") -        display_startup(); +        do_startup_frame();          // request mute list          LL_INFOS() << "Requesting Mute List" << LL_ENDL; @@ -1778,12 +1787,12 @@ bool idle_startup()          LL_INFOS() << "Requesting Money Balance" << LL_ENDL;          LLStatusBar::sendMoneyBalanceRequest(); -        display_startup(); +        do_startup_frame();          // Inform simulator of our language preference          LLAgentLanguage::update(); -        display_startup(); +        do_startup_frame();          // unpack thin inventory          LLSD response = LLLoginInstance::getInstance()->getResponse();          //bool dump_buffer = false; @@ -1798,7 +1807,7 @@ bool idle_startup()                  gInventory.setLibraryRootFolderID(id.asUUID());              }          } -        display_startup(); +        do_startup_frame();          LLSD inv_lib_owner = response["inventory-lib-owner"];          if(inv_lib_owner.isDefined()) @@ -1810,9 +1819,9 @@ bool idle_startup()                  gInventory.setLibraryOwnerID(LLUUID(id.asUUID()));              }          } -        display_startup(); +        do_startup_frame();          LLStartUp::setStartupState(STATE_INVENTORY_SKEL); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1831,7 +1840,7 @@ bool idle_startup()                  LL_WARNS("AppInit") << "Problem loading inventory-skel-lib" << LL_ENDL;              }          } -        display_startup(); +        do_startup_frame();          LLSD inv_skeleton = response["inventory-skeleton"];          if (inv_skeleton.isDefined()) @@ -1842,9 +1851,9 @@ bool idle_startup()                  LL_WARNS("AppInit") << "Problem loading inventory-skel-targets" << LL_ENDL;              }          } -        display_startup(); +        do_startup_frame();          LLStartUp::setStartupState(STATE_INVENTORY_SEND2); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1890,7 +1899,7 @@ bool idle_startup()                  list[agent_id] = new LLRelationship(given_rights, has_rights, false);              }              LLAvatarTracker::instance().addBuddyList(list); -            display_startup(); +            do_startup_frame();          }          bool show_hud = false; @@ -1918,7 +1927,7 @@ bool idle_startup()                  //}              }          } -        display_startup(); +        do_startup_frame();          // Either we want to show tutorial because this is the first login          // to a Linden Help Island or the user quit with the tutorial @@ -1927,21 +1936,21 @@ bool idle_startup()          {              LLFloaterReg::showInstance("hud", LLSD(), false);          } -        display_startup(); +        do_startup_frame();          LLSD event_notifications = response["event_notifications"];          if(event_notifications.isDefined())          {              gEventNotifier.load(event_notifications);          } -        display_startup(); +        do_startup_frame();          LLSD classified_categories = response["classified_categories"];          if(classified_categories.isDefined())          {              LLClassifiedInfo::loadCategories(classified_categories);          } -        display_startup(); +        do_startup_frame();          // This method MUST be called before gInventory.findCategoryUUIDForType because of          // gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap. @@ -1960,7 +1969,7 @@ bool idle_startup()          LLInventoryModelBackgroundFetch::instance().start();          gInventory.createCommonSystemCategories();          LLStartUp::setStartupState(STATE_INVENTORY_CALLBACKS ); -        display_startup(); +        do_startup_frame();          return false;      } @@ -1972,7 +1981,7 @@ bool idle_startup()      {          if (!LLInventoryModel::isSysFoldersReady())          { -            display_startup(); +            do_startup_frame();              return false;          } @@ -1997,7 +2006,7 @@ bool idle_startup()          gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null);          gInventory.notifyObservers(); -        display_startup(); +        do_startup_frame();          // set up callbacks          LL_INFOS() << "Registering Callbacks" << LL_ENDL; @@ -2008,18 +2017,18 @@ bool idle_startup()          LLAvatarTracker::instance().registerCallbacks(msg);          LL_INFOS() << " Landmark" << LL_ENDL;          LLLandmark::registerCallbacks(msg); -        display_startup(); +        do_startup_frame();          // request all group information          LL_INFOS() << "Requesting Agent Data" << LL_ENDL;          gAgent.sendAgentDataUpdateRequest(); -        display_startup(); +        do_startup_frame();          // Create the inventory views          LL_INFOS() << "Creating Inventory Views" << LL_ENDL;          LLFloaterReg::getInstance("inventory"); -        display_startup(); +        do_startup_frame();          LLStartUp::setStartupState( STATE_MISC ); -        display_startup(); +        do_startup_frame();          return false;      } @@ -2069,7 +2078,7 @@ bool idle_startup()              gSavedSettings.setBOOL("ShowStartLocation", true);          } -        display_startup(); +        do_startup_frame();          // Load stored local environment if needed.          LLEnvironment::instance().loadFromSettings(); @@ -2077,7 +2086,7 @@ bool idle_startup()          // *TODO : Uncomment that line once the whole grid migrated to SLM and suppress it from LLAgent::handleTeleportFinished() (llagent.cpp)          //check_merchant_status(); -        display_startup(); +        do_startup_frame();          if (gSavedSettings.getBOOL("HelpFloaterOpen"))          { @@ -2085,7 +2094,7 @@ bool idle_startup()              LLViewerHelp::instance().showTopic("");          } -        display_startup(); +        do_startup_frame();          // We're successfully logged in.          gSavedSettings.setBOOL("FirstLoginThisInstall", false); @@ -2094,12 +2103,12 @@ bool idle_startup()          LLFloaterGridStatus::getInstance()->startGridStatusTimer(); -        display_startup(); +        do_startup_frame(); -        display_startup(); +        do_startup_frame();          // JC: Initializing audio requests many sounds for download.          init_audio(); -        display_startup(); +        do_startup_frame();          // JC: Initialize "active" gestures.  This may also trigger          // many gesture downloads, if this is the user's first @@ -2137,7 +2146,7 @@ bool idle_startup()              LLGestureMgr::instance().startFetch();          }          gDisplaySwapBuffers = true; -        display_startup(); +        do_startup_frame();          LLMessageSystem* msg = gMessageSystem;          msg->setHandlerFuncFast(_PREHASH_SoundTrigger,              process_sound_trigger); @@ -2215,10 +2224,10 @@ bool idle_startup()              }          } -        display_startup(); +        do_startup_frame();          //DEV-17797.  get null folder.  Any items found here moved to Lost and Found          LLInventoryModelBackgroundFetch::instance().findLostItems(); -        display_startup(); +        do_startup_frame();          LLStartUp::setStartupState( STATE_PRECACHE );          timeout.reset(); @@ -2227,7 +2236,7 @@ bool idle_startup()      if (STATE_PRECACHE == LLStartUp::getStartupState())      { -        display_startup(); +        do_startup_frame();          F32 timeout_frac = timeout.getElapsedTimeF32()/PRECACHING_DELAY;          // We now have an inventory skeleton, so if this is a user's first @@ -2253,7 +2262,7 @@ bool idle_startup()              callAfterCOFFetch(set_flags_and_update_appearance);          } -        display_startup(); +        do_startup_frame();          // wait precache-delay and for agent's avatar or a lot longer.          if ((timeout_frac > 1.f) && isAgentAvatarValid()) @@ -2278,7 +2287,7 @@ bool idle_startup()              set_startup_status(0.60f + 0.30f * timeout_frac,                  LLTrans::getString("LoginPrecaching"),                      gAgent.mMOTD.c_str()); -            display_startup(); +            do_startup_frame();          }          return true; @@ -2306,7 +2315,7 @@ bool idle_startup()              LLStartUp::setStartupState( STATE_CLEANUP );          } -        display_startup(); +        do_startup_frame();          if (gAgent.isOutfitChosen() && (wearables_time > MAX_WEARABLES_TIME))          { @@ -2347,7 +2356,7 @@ bool idle_startup()      if (STATE_CLEANUP == LLStartUp::getStartupState())      {          set_startup_status(1.0, "", ""); -        display_startup(); +        do_startup_frame();          if (!mBenefitsSuccessfullyInit)          { @@ -2368,7 +2377,7 @@ bool idle_startup()          //gViewerWindow->revealIntroPanel();          gViewerWindow->setStartupComplete();          gViewerWindow->setProgressCancelButtonVisible(false); -        display_startup(); +        do_startup_frame();          // We're not away from keyboard, even though login might have taken          // a while. JC @@ -2400,7 +2409,7 @@ bool idle_startup()          // LLUserAuth::getInstance()->reset();          LLStartUp::setStartupState( STATE_STARTED ); -        display_startup(); +        do_startup_frame();          // Unmute audio if desired and setup volumes.          // This is a not-uncommon crash site, so surround it with @@ -2416,7 +2425,7 @@ bool idle_startup()          LLAgentPicksInfo::getInstance()->requestNumberOfPicks(); -        display_startup(); +        do_startup_frame();          llassert(LLPathfindingManager::getInstance() != NULL);          LLPathfindingManager::getInstance()->initSystem(); @@ -3021,9 +3030,7 @@ std::string LLStartUp::startupStateToString(EStartupState state)  // static  void LLStartUp::setStartupState( EStartupState state )  { -    LL_INFOS("AppInit") << "Startup state changing from " << -        getStartupStateString() << " to " << -        startupStateToString(state) << LL_ENDL; +    LL_INFOS("AppInit") << getStartupStateString() << " --> " << startupStateToString(state) << LL_ENDL;      getPhases().stopPhase(getStartupStateString());      gStartupState = state; @@ -3087,7 +3094,7 @@ void LLStartUp::multimediaInit()      LL_DEBUGS("AppInit") << "Initializing Multimedia...." << LL_ENDL;      std::string msg = LLTrans::getString("LoginInitializingMultimedia");      set_startup_status(0.42f, msg.c_str(), gAgent.mMOTD.c_str()); -    display_startup(); +    do_startup_frame();  }  void LLStartUp::fontInit() @@ -3095,7 +3102,7 @@ void LLStartUp::fontInit()      LL_DEBUGS("AppInit") << "Initializing fonts...." << LL_ENDL;      std::string msg = LLTrans::getString("LoginInitializingFonts");      set_startup_status(0.45f, msg.c_str(), gAgent.mMOTD.c_str()); -    display_startup(); +    do_startup_frame();      LLFontGL::loadDefaultFonts();  } @@ -3792,14 +3799,14 @@ bool process_login_success_response()      {          // We got an answer from the grid -> use that for map for the current session          gSavedSettings.setString("CurrentMapServerURL", map_server_url); -        LL_INFOS("LLStartup") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL; +        LL_INFOS("LLStartUp") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL;      }      else      {          // No answer from the grid -> use the default setting for current session          map_server_url = gSavedSettings.getString("MapServerURL");          gSavedSettings.setString("CurrentMapServerURL", map_server_url); -        LL_INFOS("LLStartup") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL; +        LL_INFOS("LLStartUp") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL;      }      // Default male and female avatars allowing the user to choose their avatar on first login. diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index bda53f66eb..78d930c05c 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -642,7 +642,7 @@ void LLGLTexMemBar::draw()      text = llformat("Mesh: Reqs(Tot/Htp/Big): %u/%u/%u Rtr/Err: %u/%u Cread/Cwrite: %u/%u Low/At/High: %d/%d/%d",                      LLMeshRepository::sMeshRequestCount, LLMeshRepository::sHTTPRequestCount, LLMeshRepository::sHTTPLargeRequestCount,                      LLMeshRepository::sHTTPRetryCount, LLMeshRepository::sHTTPErrorCount, -                    LLMeshRepository::sCacheReads, LLMeshRepository::sCacheWrites, +                    (U32)LLMeshRepository::sCacheReads, (U32)LLMeshRepository::sCacheWrites,                      LLMeshRepoThread::sRequestLowWater, LLMeshRepoThread::sRequestWaterLevel, LLMeshRepoThread::sRequestHighWater);      LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2,                                               text_color, LLFontGL::LEFT, LLFontGL::TOP); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1d4828fd33..5fd820f91d 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3147,7 +3147,9 @@ void send_agent_update(bool force_send, bool send_reliable)      LL_PROFILE_ZONE_SCOPED;      llassert(!gCubeSnapshot); -    if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) +    LLAgent::ETeleportState tp_state = gAgent.getTeleportState(); +    if (tp_state != LLAgent::TELEPORT_NONE +        && tp_state != LLAgent::TELEPORT_ARRIVING)      {          // We don't care if they want to send an agent update, they're not allowed          // until the target simulator is ready to receive them @@ -3322,7 +3324,36 @@ void send_agent_update(bool force_send, bool send_reliable)      msg->addVector3Fast(_PREHASH_CameraAtAxis, camera_at);      msg->addVector3Fast(_PREHASH_CameraLeftAxis, LLViewerCamera::getInstance()->getLeftAxis());      msg->addVector3Fast(_PREHASH_CameraUpAxis, LLViewerCamera::getInstance()->getUpAxis()); -    msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); + +    static F32 last_draw_disatance_step = 1024; +    if (tp_state == LLAgent::TELEPORT_ARRIVING || LLStartUp::getStartupState() < STATE_MISC) +    { +        // Inform interest list, prioritize closer area. +        // Reason: currently server doesn't distance sort attachments, by restricting range +        // we reduce the number of attachments sent to the viewer, thus prioritizing +        // closer ones. +        // Todo: revise and remove once server gets distance sorting. +        last_draw_disatance_step = llmax((F32)(gAgentCamera.mDrawDistance / 2.f), 50.f); +        msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step); +    } +    else if (last_draw_disatance_step < gAgentCamera.mDrawDistance) +    { +        static LLFrameTimer last_step_time; +        if (last_step_time.getElapsedTimeF32() > 1.f) +        { +            // gradually increase draw distance +            // Idealy this should be not per second, but based on how loaded +            // mesh thread is, but hopefully this is temporary. +            last_step_time.reset(); +            F32 step = gAgentCamera.mDrawDistance * 0.1f; +            last_draw_disatance_step = llmin(last_draw_disatance_step + step, gAgentCamera.mDrawDistance); +        } +        msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step); +    } +    else +    { +        msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); +    }      msg->addU32Fast(_PREHASH_ControlFlags, control_flags); diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 12543089c9..f5bbff144b 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -30,6 +30,7 @@  #include "llviewertexturelist.h" +#include "llagent.h"  #include "llgl.h" // fot gathering stats from GL  #include "llimagegl.h"  #include "llimagebmp.h" @@ -815,10 +816,19 @@ void LLViewerTextureList::updateImages(F32 max_time)              clearFetchingRequests();              gPipeline.clearRebuildGroups();              cleared = true; +            return;          } -        return; +        // ARRIVING is a delay to let things decode, cache and process, +        // so process textures like normal despite gTeleportDisplay +        if (gAgent.getTeleportState() != LLAgent::TELEPORT_ARRIVING) +        { +            return; +        } +    } +    else +    { +        cleared = false;      } -    cleared = false;      LLAppViewer::getTextureFetch()->setTextureBandwidth((F32)LLTrace::get_frame_recording().getPeriodMeanPerSec(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED).value()); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 7bf9c88b99..40312b7f4e 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -682,7 +682,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,      mFullyLoaded(false),      mPreviousFullyLoaded(false),      mFullyLoadedInitialized(false), -    mLastCloudAttachmentCount(0), +    mLastCloudAttachmentCount(-1),      mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN),      mLoadedCallbacksPaused(false),      mLoadedCallbackTextures(0), @@ -2794,7 +2794,10 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)      }      // attach objects that were waiting for a drawable -    lazyAttach(); +    if (!mPendingAttachment.empty()) +    { +        lazyAttach(); +    }      // animate the character      // store off last frame's root position to be consistent with camera position @@ -3226,6 +3229,7 @@ void LLVOAvatar::idleUpdateLoadingEffect()              if (mFirstFullyVisible)              {                  mFirstFullyVisible = false; +                mLastCloudAttachmentCount = (S32)mSimAttachments.size();                  mFirstDecloudTime = mFirstAppearanceMessageTimer.getElapsedTimeF32();                  if (isSelf())                  { @@ -7710,6 +7714,64 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )      }  } +bool check_object_for_mesh_loading(LLViewerObject* objectp) +{ +    if (!objectp || !objectp->getVolume()) +    { +        return false; +    } +    LLVolume* volp = objectp->getVolume(); +    const LLUUID& mesh_id = volp->getParams().getSculptID(); +    if (mesh_id.isNull()) +    { +        // No mesh nor skin info needed +        return false; +    } + +    if (volp->isMeshAssetUnavaliable()) +    { +        // Mesh failed to load, do not expect it +        return false; +    } + +    if (!objectp->mDrawable) +    { +        return false; +    } + +    LLVOVolume* pvobj = objectp->mDrawable->getVOVolume(); +    if (pvobj) +    { +        if (!pvobj->isMesh()) +        { +            // Not a mesh +            return false; +        } + +        if (!volp->isMeshAssetLoaded()) +        { +            // Waiting for mesh +            return true; +        } + +        const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo(); +        if (skin_data) +        { +            // Skin info present, done +            return false; +        } + +        if (pvobj->isSkinInfoUnavaliable()) +        { +            // Load failed or info not present, don't expect it +            return false; +        } +    } + +    // object is not ready +    return true; +} +  bool LLVOAvatar::hasPendingAttachedMeshes()  {      for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); @@ -7724,62 +7786,20 @@ bool LLVOAvatar::hasPendingAttachedMeshes()                   ++attachment_iter)              {                  LLViewerObject* objectp = attachment_iter->get(); -                if (objectp) +                if (objectp && !objectp->isDead())                  { +                    if (check_object_for_mesh_loading(objectp)) +                    { +                        return true; +                    }                      LLViewerObject::const_child_list_t& child_list = objectp->getChildren();                      for (LLViewerObject::child_list_t::const_iterator iter1 = child_list.begin();                           iter1 != child_list.end(); ++iter1)                      {                          LLViewerObject* objectchild = *iter1; -                        if (objectchild && objectchild->getVolume()) +                        if (check_object_for_mesh_loading(objectchild))                          { -                            const LLUUID& mesh_id = objectchild->getVolume()->getParams().getSculptID(); -                            if (mesh_id.isNull()) -                            { -                                // No mesh nor skin info needed -                                continue; -                            } - -                            if (objectchild->getVolume()->isMeshAssetUnavaliable()) -                            { -                                // Mesh failed to load, do not expect it -                                continue; -                            } - -                            if (objectchild->mDrawable) -                            { -                                LLVOVolume* pvobj = objectchild->mDrawable->getVOVolume(); -                                if (pvobj) -                                { -                                    if (!pvobj->isMesh()) -                                    { -                                        // Not a mesh -                                        continue; -                                    } - -                                    if (!objectchild->getVolume()->isMeshAssetLoaded()) -                                    { -                                        // Waiting for mesh -                                        return true; -                                    } - -                                    const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo(); -                                    if (skin_data) -                                    { -                                        // Skin info present, done -                                        continue; -                                    } - -                                    if (pvobj->isSkinInfoUnavaliable()) -                                    { -                                        // Load failed or info not present, don't expect it -                                        continue; -                                    } -                                } - -                                // objectchild is not ready -                                return true; -                            } +                            return true;                          }                      }                  } @@ -8382,7 +8402,7 @@ bool LLVOAvatar::updateIsFullyLoaded()                    );          // compare amount of attachments to one reported by simulator -        if (!loading && !isSelf() && rez_status < 4 && mLastCloudAttachmentCount < mSimAttachments.size()) +        if (!isSelf() && mLastCloudAttachmentCount < mSimAttachments.size() && mSimAttachments.size() > 0)          {              S32 attachment_count = getAttachmentCount();              if (mLastCloudAttachmentCount != attachment_count) @@ -8400,6 +8420,11 @@ bool LLVOAvatar::updateIsFullyLoaded()                  // waiting                  loading = true;              } +            else if (!loading) +            { +                // for hasFirstFullAttachmentData +                mLastCloudAttachmentCount = (S32)mSimAttachments.size(); +            }          }      }      updateRezzedStatusTimers(rez_status); @@ -8513,6 +8538,12 @@ bool LLVOAvatar::isFullyLoaded() const      return (mRenderUnloadedAvatar || mFullyLoaded);  } +bool LLVOAvatar::hasFirstFullAttachmentData() const +{ +    return !mFirstFullyVisible // Avatar is fully visible, have all data +        || mLastCloudAttachmentCount >= (S32)mSimAttachments.size(); +} +  bool LLVOAvatar::isTooComplex() const  {      bool too_complex; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index dd1725c322..263c3dadf6 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -385,6 +385,7 @@ public:      //--------------------------------------------------------------------  public:      bool            isFullyLoaded() const; +    bool            hasFirstFullAttachmentData() const;      F32             getFirstDecloudTime() const {return mFirstDecloudTime;}      // check and return current state relative to limits | 
