Merge branch 'DRTVWR-548-maint-N' of https://bitbucket.org/lindenlab/viewer

# Conflicts:
#	indra/llaudio/llaudiodecodemgr.cpp
#	indra/llaudio/llaudioengine.cpp
#	indra/llaudio/llaudioengine.h
#	indra/llaudio/llaudioengine_fmodstudio.cpp
#	indra/llrender/llrender.cpp
#	indra/llwindow/llwindowwin32.cpp
#	indra/newview/app_settings/settings.xml
#	indra/newview/llviewerwindow.cpp
#	indra/newview/pipeline.cpp
master
Ansariel 2022-06-15 10:38:52 +02:00
commit 7185e97905
27 changed files with 511 additions and 283 deletions

View File

@ -35,6 +35,8 @@
#include "llendianswizzle.h"
#include "llassetstorage.h"
#include "llrefcount.h"
#include "threadpool.h"
#include "workqueue.h"
#include "llvorbisencode.h"
@ -45,15 +47,13 @@
extern LLAudioEngine *gAudiop;
LLAudioDecodeMgr *gAudioDecodeMgrp = NULL;
static const S32 WAV_HEADER_SIZE = 44;
//////////////////////////////////////////////////////////////////////////////
class LLVorbisDecodeState : public LLRefCount
class LLVorbisDecodeState : public LLThreadSafeRefCount
{
public:
class WriteResponder : public LLLFSThread::Responder
@ -537,156 +537,256 @@ void LLVorbisDecodeState::flushBadFile()
class LLAudioDecodeMgr::Impl
{
friend class LLAudioDecodeMgr;
public:
Impl() {};
~Impl() {};
friend class LLAudioDecodeMgr;
Impl();
public:
void processQueue(const F32 num_secs = 0.005);
void processQueue();
protected:
std::deque<LLUUID> mDecodeQueue;
LLPointer<LLVorbisDecodeState> mCurrentDecodep;
void startMoreDecodes();
void enqueueFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState>& decode_state);
void checkDecodesFinished();
protected:
std::deque<LLUUID> mDecodeQueue;
std::map<LLUUID, LLPointer<LLVorbisDecodeState>> mDecodes;
};
void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
LLAudioDecodeMgr::Impl::Impl()
{
LLUUID uuid;
}
LLTimer decode_timer;
// Returns the in-progress decode_state, which may be an empty LLPointer if
// there was an error and there is no more work to be done.
LLPointer<LLVorbisDecodeState> beginDecodingAndWritingAudio(const LLUUID &decode_id);
BOOL done = FALSE;
while (!done)
{
if (mCurrentDecodep)
{
BOOL res;
// Return true if finished
bool tryFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState> decode_state);
// Decode in a loop until we're done or have run out of time.
while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs))
{
// decodeSection does all of the work above
}
void LLAudioDecodeMgr::Impl::processQueue()
{
// First, check if any audio from in-progress decodes are ready to play. If
// so, mark them ready for playback (or errored, in case of error).
checkDecodesFinished();
if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid())
{
// We had an error when decoding, abort.
LL_WARNS("AudioEngine") << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << LL_ENDL;
mCurrentDecodep->flushBadFile();
// Second, start as many decodes from the queue as permitted
startMoreDecodes();
}
if (gAudiop)
{
LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
adp->setHasValidData(false);
adp->setHasCompletedDecode(true);
}
void LLAudioDecodeMgr::Impl::startMoreDecodes()
{
llassert_always(gAudiop);
mCurrentDecodep = NULL;
done = TRUE;
}
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
// *NOTE: main_queue->postTo casts this refcounted smart pointer to a weak
// pointer
LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General");
const LL::ThreadPool::ptr_t general_thread_pool = LL::ThreadPool::getInstance("General");
llassert_always(main_queue);
llassert_always(general_queue);
llassert_always(general_thread_pool);
// Set max decodes to double the thread count of the general work queue.
// This ensures the general work queue is full, but prevents theoretical
// buildup of buffers in memory due to disk writes once the
// LLVorbisDecodeState leaves the worker thread (see
// LLLFSThread::sLocal->write). This is probably as fast as we can get it
// without modifying/removing LLVorbisDecodeState, at which point we should
// consider decoding the audio during the asset download process.
// -Cosmic,2022-05-11
const size_t max_decodes = general_thread_pool->getWidth() * 2;
if (!res)
{
// We've used up out time slice, bail...
done = TRUE;
}
else if (mCurrentDecodep)
{
if (gAudiop && mCurrentDecodep->finishDecode())
{
// We finished!
LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
if (!adp)
{
LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << mCurrentDecodep->getUUID() << LL_ENDL;
}
else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
{
adp->setHasCompletedDecode(true);
adp->setHasDecodedData(true);
adp->setHasValidData(true);
while (!mDecodeQueue.empty() && mDecodes.size() < max_decodes)
{
const LLUUID decode_id = mDecodeQueue.front();
mDecodeQueue.pop_front();
// At this point, we could see if anyone needs this sound immediately, but
// I'm not sure that there's a reason to - we need to poll all of the playing
// sounds anyway.
//LL_INFOS("AudioEngine") << "Finished the vorbis decode, now what?" << LL_ENDL;
}
else
{
adp->setHasCompletedDecode(true);
LL_INFOS("AudioEngine") << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << LL_ENDL;
}
mCurrentDecodep = NULL;
}
done = TRUE; // done for now
}
}
// Don't decode the same file twice
if (mDecodes.find(decode_id) != mDecodes.end())
{
continue;
}
if (gAudiop->hasDecodedFile(decode_id))
{
continue;
}
if (!done)
{
if (mDecodeQueue.empty())
{
// Nothing else on the queue.
done = TRUE;
}
else
{
LLUUID uuid;
uuid = mDecodeQueue.front();
mDecodeQueue.pop_front();
if (!gAudiop || gAudiop->hasDecodedFile(uuid))
{
// This file has already been decoded, don't decode it again.
continue;
}
// Kick off a decode
mDecodes[decode_id] = LLPointer<LLVorbisDecodeState>(NULL);
main_queue->postTo(
general_queue,
[decode_id]() // Work done on general queue
{
LLPointer<LLVorbisDecodeState> decode_state = beginDecodingAndWritingAudio(decode_id);
LL_DEBUGS() << "Decoding " << uuid << " from audio queue!" << LL_ENDL;
if (!decode_state)
{
if (gAudiop)
gAudiop->markSoundCorrupt(decode_id);
std::string uuid_str;
std::string d_path;
// Audio decode has errored
return decode_state;
}
LLTimer timer;
timer.reset();
// Disk write of decoded audio is now in progress off-thread
return decode_state;
},
[decode_id, this](LLPointer<LLVorbisDecodeState> decode_state) // Callback to main thread
mutable {
if (!gAudiop)
{
// There is no LLAudioEngine anymore. This might happen if
// an audio decode is enqueued just before shutdown.
return;
}
uuid.toString(uuid_str);
// <FS:Ansariel> Sound cache
//d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf";
d_path = gDirUtilp->getExpandedFilename(LL_PATH_FS_SOUND_CACHE,uuid_str) + ".dsf";
// </FS:Ansariel>
// At this point, we can be certain that the pointer to "this"
// is valid because the lifetime of "this" is dependent upon
// the lifetime of gAudiop.
mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
if (!mCurrentDecodep->initDecode())
{
gAudiop->markSoundCorrupt( uuid );
mCurrentDecodep = NULL;
}
}
}
}
enqueueFinishAudio(decode_id, decode_state);
});
}
}
LLPointer<LLVorbisDecodeState> beginDecodingAndWritingAudio(const LLUUID &decode_id)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
LL_DEBUGS() << "Decoding " << decode_id << " from audio queue!" << LL_ENDL;
// <FS:Ansariel> Sound cache
//std::string d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, decode_id.asString()) + ".dsf";
std::string d_path = gDirUtilp->getExpandedFilename(LL_PATH_FS_SOUND_CACHE, decode_id.asString()) + ".dsf";
// </FS:Ansariel>
LLPointer<LLVorbisDecodeState> decode_state = new LLVorbisDecodeState(decode_id, d_path);
if (!decode_state->initDecode())
{
return NULL;
}
// Decode in a loop until we're done
while (!decode_state->decodeSection())
{
// decodeSection does all of the work above
}
if (!decode_state->isDone())
{
// Decode stopped early, or something bad happened to the file
// during decoding.
LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data or decode has been canceled, aborting decode" << LL_ENDL;
decode_state->flushBadFile();
return NULL;
}
if (!decode_state->isValid())
{
// We had an error when decoding, abort.
LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data, aborting decode" << LL_ENDL;
decode_state->flushBadFile();
return NULL;
}
// Kick off the writing of the decoded audio to the disk cache.
// The receiving thread can then cheaply call finishDecode() again to check
// if writing has finished. Someone has to hold on to the refcounted
// decode_state to prevent it from getting destroyed during write.
decode_state->finishDecode();
return decode_state;
}
void LLAudioDecodeMgr::Impl::enqueueFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState>& decode_state)
{
// Assumed fast
if (tryFinishAudio(decode_id, decode_state))
{
// Done early!
auto decode_iter = mDecodes.find(decode_id);
llassert(decode_iter != mDecodes.end());
mDecodes.erase(decode_iter);
return;
}
// Not done yet... enqueue it
mDecodes[decode_id] = decode_state;
}
void LLAudioDecodeMgr::Impl::checkDecodesFinished()
{
auto decode_iter = mDecodes.begin();
while (decode_iter != mDecodes.end())
{
const LLUUID& decode_id = decode_iter->first;
const LLPointer<LLVorbisDecodeState>& decode_state = decode_iter->second;
if (tryFinishAudio(decode_id, decode_state))
{
decode_iter = mDecodes.erase(decode_iter);
}
else
{
++decode_iter;
}
}
}
bool tryFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState> decode_state)
{
// decode_state is a file write in progress unless finished is true
bool finished = decode_state && decode_state->finishDecode();
if (!finished)
{
return false;
}
llassert_always(gAudiop);
LLAudioData *adp = gAudiop->getAudioData(decode_id);
if (!adp)
{
LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << decode_id << LL_ENDL;
return true;
}
bool valid = decode_state && decode_state->isValid();
// Mark current decode finished regardless of success or failure
adp->setHasCompletedDecode(true);
// Flip flags for decoded data
adp->setHasDecodeFailed(!valid);
adp->setHasDecodedData(valid);
// When finished decoding, there will also be a decoded wav file cached on
// disk with the .dsf extension
if (valid)
{
adp->setHasWAVLoadFailed(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////////
LLAudioDecodeMgr::LLAudioDecodeMgr()
{
mImpl = new Impl;
mImpl = new Impl();
}
LLAudioDecodeMgr::~LLAudioDecodeMgr()
{
delete mImpl;
delete mImpl;
mImpl = nullptr;
}
void LLAudioDecodeMgr::processQueue(const F32 num_secs)
void LLAudioDecodeMgr::processQueue()
{
mImpl->processQueue(num_secs);
mImpl->processQueue();
}
BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
{
// <FS:ND> Protect against corrupted sounds. Just do a quit exit instead of trying to decode over and over.
if( gAudiop->isCorruptSound( uuid ) )
if (gAudiop && gAudiop->isCorruptSound(uuid))
return FALSE;
// </FS:ND>

View File

@ -32,24 +32,23 @@
#include "llassettype.h"
#include "llframetimer.h"
#include "llsingleton.h"
template<class T> class LLPointer;
class LLVorbisDecodeState;
class LLAudioDecodeMgr
class LLAudioDecodeMgr : public LLSingleton<LLAudioDecodeMgr>
{
LLSINGLETON(LLAudioDecodeMgr);
~LLAudioDecodeMgr();
public:
LLAudioDecodeMgr();
~LLAudioDecodeMgr();
void processQueue(const F32 num_secs = 0.005);
void processQueue();
BOOL addDecodeRequest(const LLUUID &uuid);
void addAudioRequest(const LLUUID &uuid);
protected:
class Impl;
Impl* mImpl;
Impl* mImpl;
};
extern LLAudioDecodeMgr *gAudioDecodeMgrp;
#endif

View File

@ -90,18 +90,10 @@ void LLAudioEngine::setDefaults()
mLastStatus = 0;
mNumChannels = 0;
mEnableWind = false;
S32 i;
for (i = 0; i < MAX_CHANNELS; i++)
{
mChannels[i] = NULL;
}
for (i = 0; i < MAX_BUFFERS; i++)
{
mBuffers[i] = NULL;
}
mChannels.fill(nullptr);
mBuffers.fill(nullptr);
mMasterGain = 1.f;
// Setting mInternalGain to an out of range value fixes the issue reported in STORM-830.
@ -118,18 +110,14 @@ void LLAudioEngine::setDefaults()
}
bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::string &app_title)
bool LLAudioEngine::init(void* userdata, const std::string &app_title)
{
setDefaults();
mNumChannels = num_channels;
mUserData = userdata;
allocateListener();
// Initialize the decode manager
gAudioDecodeMgrp = new LLAudioDecodeMgr;
LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << LL_ENDL;
return true;
@ -144,10 +132,6 @@ void LLAudioEngine::shutdown()
mStreamingAudioImpl = NULL;
// </FS>
// Clean up decode manager
delete gAudioDecodeMgrp;
gAudioDecodeMgrp = NULL;
// Clean up wind source
cleanupWind();
@ -169,14 +153,14 @@ void LLAudioEngine::shutdown()
// Clean up channels
S32 i;
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
{
delete mChannels[i];
mChannels[i] = NULL;
}
// Clean up buffers
for (i = 0; i < MAX_BUFFERS; i++)
for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
{
delete mBuffers[i];
mBuffers[i] = NULL;
@ -242,7 +226,7 @@ std::string LLAudioEngine::getInternetStreamURL()
void LLAudioEngine::updateChannels()
{
S32 i;
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
{
if (mChannels[i])
{
@ -253,20 +237,14 @@ void LLAudioEngine::updateChannels()
}
}
static const F32 default_max_decode_time = .002f; // 2 ms
void LLAudioEngine::idle(F32 max_decode_time)
void LLAudioEngine::idle()
{
if (max_decode_time <= 0.f)
{
max_decode_time = default_max_decode_time;
}
// "Update" all of our audio sources, clean up dead ones.
// Primarily does position updating, cleanup of unused audio sources.
// Also does regeneration of the current priority of each audio source.
S32 i;
for (i = 0; i < MAX_BUFFERS; i++)
for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
{
if (mBuffers[i])
{
@ -486,7 +464,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
commitDeferredChanges();
// Flush unused buffers that are stale enough
for (i = 0; i < MAX_BUFFERS; i++)
for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
{
if (mBuffers[i])
{
@ -502,7 +480,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
// Clear all of the looped flags for the channels
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
{
if (mChannels[i])
{
@ -511,7 +489,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
}
// Decode audio files
gAudioDecodeMgrp->processQueue(max_decode_time);
LLAudioDecodeMgr::getInstance()->processQueue();
// Call this every frame, just in case we somehow
// missed picking it up in all the places that can add
@ -545,7 +523,7 @@ bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uu
{
if (audio_uuid.notNull())
{
gAudioDecodeMgrp->addDecodeRequest(audio_uuid);
LLAudioDecodeMgr::getInstance()->addDecodeRequest(audio_uuid);
}
}
else
@ -574,7 +552,7 @@ void LLAudioEngine::enableWind(bool enable)
LLAudioBuffer * LLAudioEngine::getFreeBuffer()
{
S32 i;
for (i = 0; i < MAX_BUFFERS; i++)
for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
{
if (!mBuffers[i])
{
@ -587,7 +565,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer()
// Grab the oldest unused buffer
F32 max_age = -1.f;
S32 buffer_id = -1;
for (i = 0; i < MAX_BUFFERS; i++)
for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
{
if (mBuffers[i])
{
@ -618,7 +596,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer()
LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
{
S32 i;
for (i = 0; i < mNumChannels; i++)
for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
{
if (!mChannels[i])
{
@ -646,7 +624,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
F32 min_priority = 10000.f;
LLAudioChannel *min_channelp = NULL;
for (i = 0; i < mNumChannels; i++)
for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
{
LLAudioChannel *channelp = mChannels[i];
LLAudioSource *sourcep = channelp->getSource();
@ -673,7 +651,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp)
{
S32 i;
for (i = 0; i < MAX_BUFFERS; i++)
for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
{
if (mBuffers[i] == bufferp)
{
@ -696,7 +674,7 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid)
getAudioData(uuid); // We don't care about the return value, this is just to make sure
// that we have an entry, which will mean that the audio engine knows about this
if (gAudioDecodeMgrp->addDecodeRequest(uuid))
if (LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid))
{
// This means that we do have a local copy, and we're working on decoding it.
return true;
@ -980,6 +958,7 @@ LLAudioSource * LLAudioEngine::findAudioSource(const LLUUID &source_id)
LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
// <FS:ND> Protect against corrupted sounds. Just do a quick exit instead of trying to decode over and over again.
if( isCorruptSound( audio_uuid ) )
return 0;
@ -1074,7 +1053,7 @@ void LLAudioEngine::startNextTransfer()
// Check all channels for currently playing sounds.
F32 max_pri = -1.f;
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
{
if (!mChannels[i])
{
@ -1102,7 +1081,7 @@ void LLAudioEngine::startNextTransfer()
continue;
}
if (!adp->hasLocalData() && adp->hasValidData())
if (!adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1113,7 +1092,7 @@ void LLAudioEngine::startNextTransfer()
if (asset_id.isNull())
{
max_pri = -1.f;
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
{
if (!mChannels[i])
{
@ -1138,7 +1117,7 @@ void LLAudioEngine::startNextTransfer()
continue;
}
if (!adp->hasLocalData() && adp->hasValidData())
if (!adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1150,7 +1129,7 @@ void LLAudioEngine::startNextTransfer()
if (asset_id.isNull())
{
max_pri = -1.f;
for (i = 0; i < MAX_CHANNELS; i++)
for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
{
if (!mChannels[i])
{
@ -1178,7 +1157,7 @@ void LLAudioEngine::startNextTransfer()
continue;
}
if (!adp->hasLocalData() && adp->hasValidData())
if (!adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1206,7 +1185,7 @@ void LLAudioEngine::startNextTransfer()
}
adp = asp->getCurrentData();
if (adp && !adp->hasLocalData() && adp->hasValidData())
if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1214,7 +1193,7 @@ void LLAudioEngine::startNextTransfer()
}
adp = asp->getQueuedData();
if (adp && !adp->hasLocalData() && adp->hasValidData())
if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1229,7 +1208,7 @@ void LLAudioEngine::startNextTransfer()
continue;
}
if (!adp->hasLocalData() && adp->hasValidData())
if (!adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1270,7 +1249,7 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v
LLAudioData *adp = gAudiop->getAudioData(uuid);
if (adp)
{ // Make sure everything is cleared
adp->setHasValidData(false);
adp->setHasDecodeFailed(true);
adp->setHasLocalData(false);
adp->setHasDecodedData(false);
adp->setHasCompletedDecode(true);
@ -1288,9 +1267,9 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v
else
{
// LL_INFOS() << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL;
adp->setHasValidData(true);
adp->setHasDecodeFailed(false);
adp->setHasLocalData(true);
gAudioDecodeMgrp->addDecodeRequest(uuid);
LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid);
}
}
gAudiop->mCurrentTransfer = LLUUID::null;
@ -1454,11 +1433,15 @@ void LLAudioSource::update()
{
// Hack - try and load the sound. Will do this as a callback
// on decode later.
if (adp->load() && adp->getBuffer())
if (adp->getBuffer())
{
play(adp->getID());
}
else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done
else if (adp->hasDecodedData() && !adp->hasWAVLoadFailed())
{
adp->load();
}
else if (adp->hasCompletedDecode() && adp->hasDecodeFailed()) // Only mark corrupted after decode is done
{
LL_WARNS() << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL;
mCorrupted = true ;
@ -1766,12 +1749,12 @@ bool LLAudioSource::hasPendingPreloads() const
{
LLAudioData *adp = iter->second;
// note: a bad UUID will forever be !hasDecodedData()
// but also !hasValidData(), hence the check for hasValidData()
// but also hasDecodeFailed(), hence the check for hasDecodeFailed()
if (!adp)
{
continue;
}
if (!adp->hasDecodedData() && adp->hasValidData())
if (!adp->hasDecodedData() && !adp->hasDecodeFailed())
{
// This source is still waiting for a preload
return true;
@ -1928,7 +1911,8 @@ LLAudioData::LLAudioData(const LLUUID &uuid) :
mHasLocalData(false),
mHasDecodedData(false),
mHasCompletedDecode(false),
mHasValidData(true)
mHasDecodeFailed(false),
mHasWAVLoadFailed(false)
{
if (uuid.isNull())
{
@ -1963,12 +1947,14 @@ bool LLAudioData::load()
{
// We already have this sound in a buffer, don't do anything.
LL_INFOS() << "Already have a buffer for this sound, don't bother loading!" << LL_ENDL;
mHasWAVLoadFailed = false;
return true;
}
if (!gAudiop)
{
LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
mHasWAVLoadFailed = true;
return false;
}
@ -1977,6 +1963,8 @@ bool LLAudioData::load()
{
// No free buffers, abort.
LL_INFOS() << "Not able to allocate a new audio buffer, aborting." << LL_ENDL;
// *TODO: Mark this failure differently so the audio engine could retry loading this buffer in the future
mHasWAVLoadFailed = true;
return true;
}
@ -1988,7 +1976,8 @@ bool LLAudioData::load()
wav_path= gDirUtilp->getExpandedFilename(LL_PATH_FS_SOUND_CACHE,uuid_str) + ".dsf";
// </FS:Ansariel>
if (!mBufferp->loadWAV(wav_path))
mHasWAVLoadFailed = !mBufferp->loadWAV(wav_path);
if (mHasWAVLoadFailed)
{
// Hrm. Right now, let's unset the buffer, since it's empty.
gAudiop->cleanupBuffer(mBufferp);
@ -2000,7 +1989,8 @@ bool LLAudioData::load()
mHasLocalData = false;
mHasDecodedData = false;
mHasCompletedDecode = false;
mHasValidData = true;
mHasDecodeFailed = false;
mHasWAVLoadFailed = false;
gAudiop->preloadSound(mID);
}
// </FS:Ansariel>
@ -2077,7 +2067,7 @@ void LLAudioEngine::removeAudioData(const LLUUID& audio_uuid)
if (buf)
{
S32 i;
for (i = 0; i < MAX_BUFFERS; ++i)
for (i = 0; i < LL_MAX_AUDIO_BUFFERS; ++i)
{
if (mBuffers[i] == buf)
{

View File

@ -49,10 +49,10 @@ const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f;
const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f;
const F32 DEFAULT_MIN_DISTANCE = 2.0f;
#define MAX_CHANNELS 30
#define LL_MAX_AUDIO_CHANNELS 30
// <FS:Ansariel> FIRE-4276: Increase number of audio buffers
//#define MAX_BUFFERS 40 // Some extra for preloading, maybe?
#define MAX_BUFFERS 60
//#define LL_MAX_AUDIO_BUFFERS 40 // Some extra for preloading, maybe?
#define LL_MAX_AUDIO_BUFFERS 60
// </FS:Ansariel>
class LLAudioSource;
@ -93,7 +93,7 @@ public:
virtual ~LLAudioEngine();
// initialization/startup/shutdown
virtual bool init(const S32 num_channels, void *userdata, const std::string &app_title);
virtual bool init(void *userdata, const std::string &app_title);
virtual std::string getDriverName(bool verbose) = 0;
virtual void shutdown();
@ -101,7 +101,7 @@ public:
//virtual void processQueue(const LLUUID &sound_guid);
virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at);
virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0;
virtual void idle(F32 max_decode_time = 0.f);
virtual void idle();
virtual void updateChannels();
//
@ -238,7 +238,6 @@ protected:
S32 mLastStatus;
S32 mNumChannels;
bool mEnableWind;
LLUUID mCurrentTransfer; // Audio file currently being transferred by the system
@ -253,11 +252,11 @@ protected:
source_map mAllSources;
data_map mAllData;
LLAudioChannel *mChannels[MAX_CHANNELS];
std::array<LLAudioChannel*, LL_MAX_AUDIO_CHANNELS> mChannels;
// Buffers needs to change into a different data structure, as the number of buffers
// that we have active should be limited by RAM usage, not count.
LLAudioBuffer *mBuffers[MAX_BUFFERS];
std::array<LLAudioBuffer*, LL_MAX_AUDIO_BUFFERS> mBuffers;
F32 mMasterGain;
F32 mInternalGain; // Actual gain set; either mMasterGain or 0 when mMuted is true.
@ -418,32 +417,36 @@ protected:
class LLAudioData
{
public:
LLAudioData(const LLUUID &uuid);
bool load();
public:
LLAudioData(const LLUUID &uuid);
bool load();
LLUUID getID() const { return mID; }
LLAudioBuffer *getBuffer() const { return mBufferp; }
LLUUID getID() const { return mID; }
LLAudioBuffer *getBuffer() const { return mBufferp; }
bool hasLocalData() const { return mHasLocalData; }
bool hasDecodedData() const { return mHasDecodedData; }
bool hasCompletedDecode() const { return mHasCompletedDecode; }
bool hasValidData() const { return mHasValidData; }
bool hasLocalData() const { return mHasLocalData; }
bool hasDecodedData() const { return mHasDecodedData; }
bool hasCompletedDecode() const { return mHasCompletedDecode; }
bool hasDecodeFailed() const { return mHasDecodeFailed; }
bool hasWAVLoadFailed() const { return mHasWAVLoadFailed; }
void setHasLocalData(const bool hld) { mHasLocalData = hld; }
void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; }
void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; }
void setHasValidData(const bool hvd) { mHasValidData = hvd; }
void setHasLocalData(const bool hld) { mHasLocalData = hld; }
void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; }
void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; }
void setHasDecodeFailed(const bool hdf) { mHasDecodeFailed = hdf; }
void setHasWAVLoadFailed(const bool hwlf) { mHasWAVLoadFailed = hwlf; }
friend class LLAudioEngine; // Severe laziness, bad.
friend class LLAudioEngine; // Severe laziness, bad.
protected:
LLUUID mID;
LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here.
bool mHasLocalData; // Set true if the sound asset file is available locally
bool mHasDecodedData; // Set true if the sound file has been decoded
bool mHasCompletedDecode; // Set true when the sound is decoded
bool mHasValidData; // Set false if decoding failed, meaning the sound asset is bad
protected:
LLUUID mID;
LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here.
bool mHasLocalData; // Set true if the encoded sound asset file is available locally
bool mHasDecodedData; // Set true if the decoded sound file is available on disk
bool mHasCompletedDecode; // Set true when the sound is decoded
bool mHasDecodeFailed; // Set true if decoding failed, meaning the sound asset is bad
bool mHasWAVLoadFailed; // Set true if loading the decoded WAV file failed, meaning the sound asset should be decoded instead if
// possible
};

View File

@ -152,7 +152,7 @@ LLAudioEngine_FMODSTUDIO::~LLAudioEngine_FMODSTUDIO()
// mSystem gets cleaned up at shutdown()
}
bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, const std::string &app_title)
bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title)
{
U32 version;
FMOD_RESULT result;
@ -164,7 +164,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
return false;
//will call LLAudioEngine_FMODSTUDIO::allocateListener, which needs a valid mSystem pointer.
LLAudioEngine::init(num_channels, userdata, app_title);
LLAudioEngine::init(userdata, app_title);
result = mSystem->getVersion(&version);
Check_FMOD_Error(result, "FMOD::System::getVersion");
@ -176,7 +176,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
}
// In this case, all sounds, PLUS wind and stream will be software.
result = mSystem->setSoftwareChannels(num_channels + EXTRA_SOUND_CHANNELS);
result = mSystem->setSoftwareChannels(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS);
Check_FMOD_Error(result, "FMOD::System::setSoftwareChannels");
Check_FMOD_Error(mSystem->setCallback(systemCallback), "FMOD::System::setCallback");
@ -219,7 +219,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
{
LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL;
if ((result = mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO)) == FMOD_OK &&
(result = mSystem->init(num_channels + EXTRA_SOUND_CHANNELS, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK)
(result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL;
audio_ok = true;
@ -241,7 +241,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
{
LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
if ((result = mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA)) == FMOD_OK &&
(result = mSystem->init(num_channels + EXTRA_SOUND_CHANNELS, fmod_flags, 0)) == FMOD_OK)
(result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0)) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
audio_ok = true;
@ -282,7 +282,9 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
#else // LL_LINUX
// initialize the FMOD engine
result = mSystem->init(num_channels + EXTRA_SOUND_CHANNELS, fmod_flags, 0);
// number of channel in this case looks to be identiacal to number of max simultaneously
// playing objects and we can set practically any number
result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0);
if (Check_FMOD_Error(result, "Error initializing FMOD Studio with default settins, retrying with other format"))
{
result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0/*- ignore*/);
@ -290,7 +292,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
{
return false;
}
result = mSystem->init(num_channels + EXTRA_SOUND_CHANNELS, fmod_flags, 0);
result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0);
}
if (Check_FMOD_Error(result, "Error initializing FMOD Studio"))
{

View File

@ -57,7 +57,7 @@ public:
virtual ~LLAudioEngine_FMODSTUDIO();
// initialization/startup/shutdown
virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title);
virtual bool init(void *user_data, const std::string &app_title);
virtual std::string getDriverName(bool verbose);
virtual void allocateListener();

View File

@ -52,10 +52,10 @@ LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
}
// virtual
bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata, const std::string &app_title)
bool LLAudioEngine_OpenAL::init(void* userdata, const std::string &app_title)
{
mWindGen = NULL;
LLAudioEngine::init(num_channels, userdata, app_title);
LLAudioEngine::init(userdata, app_title);
if(!alutInit(NULL, NULL))
{

View File

@ -212,11 +212,9 @@ U64 LLMemory::getCurrentRSS()
mach_msg_type_number_t basicInfoCount = MACH_TASK_BASIC_INFO_COUNT;
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS)
{
// residentSize = basicInfo.resident_size;
// Although this method is defined to return the "resident set size,"
// in fact what callers want from it is the total virtual memory
// consumed by the application.
residentSize = basicInfo.virtual_size;
residentSize = basicInfo.resident_size;
// 64-bit macos apps allocate 32 GB or more at startup, and this is reflected in virtual_size.
// basicInfo.virtual_size is not what we want.
}
else
{

View File

@ -22,6 +22,7 @@
#include "stringize.h"
LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity):
super(name),
mQueue(name, capacity),
mName("ThreadPool:" + name),
mThreadCount(threads)

View File

@ -22,8 +22,10 @@
namespace LL
{
class ThreadPool
class ThreadPool: public LLInstanceTracker<ThreadPool, std::string>
{
private:
using super = LLInstanceTracker<ThreadPool, std::string>;
public:
/**
* Pass ThreadPool a string name. This can be used to look up the

View File

@ -403,7 +403,7 @@ namespace LL
[result = std::forward<CALLABLE>(callable)(),
callback = std::move(callback)]
()
{ callback(std::move(result)); };
mutable { callback(std::move(result)); };
}
};
@ -449,7 +449,7 @@ namespace LL
callable = std::move(callable),
callback = std::move(callback)]
()
{
mutable {
// Use postMaybe() below in case this originating WorkQueue
// has been closed or destroyed. Remember, the outer lambda is
// now running on a thread servicing the target WorkQueue, and
@ -513,7 +513,7 @@ namespace LL
// We dare to bind a reference to Promise because it's
// specifically designed for cross-thread communication.
[&promise, callable = std::move(callable)]()
{
mutable {
try
{
// call the caller's callable and trigger promise with result
@ -542,7 +542,7 @@ namespace LL
time,
// &promise is designed for cross-thread access
[&promise, callable = std::move(callable)]()
{
mutable {
try
{
callable();

View File

@ -9920,13 +9920,13 @@
<key>NonvisibleObjectsInMemoryTime</key>
<map>
<key>Comment</key>
<string>Number of frames non-visible objects stay in memory before being removed. 0 means never to remove.</string>
<string>Number of frames non-visible objects stay in memory before being removed. 0 means max.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>300</integer>
<integer>64</integer>
</map>
<key>NoPreload</key>
<map>
@ -14205,7 +14205,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>1024</integer>
<integer>2048</integer>
</map>
<key>SceneLoadLowMemoryBound</key>
<map>

View File

@ -202,7 +202,7 @@ VARYING vec2 vary_texcoord2;
uniform float env_intensity;
uniform vec4 specular_color; // specular color RGB and specular exponent (glossiness) in alpha
#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK)
#ifdef HAS_ALPHA_MASK
uniform float minimum_alpha;
#endif
@ -227,11 +227,12 @@ void main()
vec4 diffcol = texture2D(diffuseMap, vary_texcoord0.xy);
diffcol.rgb *= vertex_color.rgb;
#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK)
// Comparing floats cast from 8-bit values, produces acne right at the 8-bit transition points
float bias = 0.001953125; // 1/512, or half an 8-bit quantization
if (diffcol.a < minimum_alpha-bias)
#ifdef HAS_ALPHA_MASK
#if DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND
if (diffcol.a*vertex_color.a < minimum_alpha)
#else
if (diffcol.a < minimum_alpha)
#endif
{
discard;
}

View File

@ -5918,8 +5918,7 @@ void LLAppViewer::idle()
audio_update_wind(false);
// this line actually commits the changes we've made to source positions, etc.
const F32 max_audio_decode_time = 0.002f; // 2 ms decode time
gAudiop->idle(max_audio_decode_time);
gAudiop->idle();
}
}

View File

@ -133,6 +133,7 @@ public:
inline LLFace* getFace(const S32 i) const;
inline S32 getNumFaces() const;
face_list_t& getFaces() { return mFaces; }
const face_list_t& getFaces() const { return mFaces; }
//void removeFace(const S32 i); // SJB: Avoid using this, it's slow
LLFace* addFace(LLFacePool *poolp, LLViewerTexture *texturep);

View File

@ -59,6 +59,7 @@ static BOOL deferred_render = FALSE;
// minimum alpha before discarding a fragment
static const F32 MINIMUM_ALPHA = 0.004f; // ~ 1/255
// minimum alpha before discarding a fragment when rendering impostors
static const F32 MINIMUM_IMPOSTOR_ALPHA = 0.1f;
@ -148,6 +149,10 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass)
(LLPipeline::sUnderWaterRender) ? &gDeferredAlphaWaterProgram : &gDeferredAlphaProgram;
prepare_alpha_shader(simple_shader, false, true); //prime simple shader (loads shadow relevant uniforms)
for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i)
{
prepare_alpha_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], false, false); // note: bindDeferredShader will get called during render loop for materials
}
// first pass, render rigged objects only and render to depth buffer
forwardRender(true);
@ -216,9 +221,15 @@ void LLDrawPoolAlpha::render(S32 pass)
{
minimum_alpha = MINIMUM_IMPOSTOR_ALPHA;
}
prepare_forward_shader(fullbright_shader, minimum_alpha);
prepare_forward_shader(simple_shader, minimum_alpha);
for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i)
{
prepare_forward_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], minimum_alpha);
}
//first pass -- rigged only and drawn to depth buffer
forwardRender(true);

View File

@ -665,6 +665,7 @@ void LLFloaterEditExtDayCycle::onButtonApply(LLUICtrl *ctrl, const LLSD &data)
if (ctrl_action == ACTION_SAVE)
{
doApplyUpdateInventory(dayclone);
clearDirtyFlag();
}
else if (ctrl_action == ACTION_SAVEAS)
{

View File

@ -296,6 +296,7 @@ void LLFloaterFixedEnvironment::onButtonApply(LLUICtrl *ctrl, const LLSD &data)
if (ctrl_action == ACTION_SAVE)
{
doApplyUpdateInventory(setting_clone);
clearDirtyFlag();
}
else if (ctrl_action == ACTION_SAVEAS)
{

View File

@ -1590,6 +1590,62 @@ void pushVertsColorCoded(LLSpatialGroup* group, U32 mask)
}
}
// return false if drawable is rigged and:
// - a linked rigged drawable has a different spatial group
// - a linked rigged drawable face has the wrong draw order index
bool check_rigged_group(LLDrawable* drawable)
{
if (drawable->isState(LLDrawable::RIGGED))
{
LLSpatialGroup* group = drawable->getSpatialGroup();
LLDrawable* root = drawable->getRoot();
if (root->isState(LLDrawable::RIGGED) && root->getSpatialGroup() != group)
{
llassert(false);
return false;
}
S32 last_draw_index = -1;
if (root->isState(LLDrawable::RIGGED))
{
for (auto& face : root->getFaces())
{
if ((S32) face->getDrawOrderIndex() <= last_draw_index)
{
llassert(false);
return false;
}
last_draw_index = face->getDrawOrderIndex();
}
}
for (auto& child : root->getVObj()->getChildren())
{
if (child->mDrawable->isState(LLDrawable::RIGGED))
{
for (auto& face : child->mDrawable->getFaces())
{
if ((S32) face->getDrawOrderIndex() <= last_draw_index)
{
llassert(false);
return false;
}
last_draw_index = face->getDrawOrderIndex();
}
}
if (child->mDrawable->getSpatialGroup() != group)
{
llassert(false);
return false;
}
}
}
return true;
}
void renderOctree(LLSpatialGroup* group)
{
//render solid object bounding box, color
@ -1634,6 +1690,9 @@ void renderOctree(LLSpatialGroup* group)
{
continue;
}
llassert(check_rigged_group(drawable));
if (!group->getSpatialPartition()->isBridge())
{
gGL.pushMatrix();

View File

@ -275,7 +275,7 @@ public:
return lhs->mAvatarp < rhs->mAvatarp;
}
return lhs->mRenderOrder > rhs->mRenderOrder;
return lhs->mRenderOrder < rhs->mRenderOrder;
}
};

View File

@ -1032,7 +1032,7 @@ bool idle_startup()
#else
void* window_handle = NULL;
#endif
bool init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle, LLAppViewer::instance()->getSecondLifeTitle());
bool init = gAudiop->init(window_handle, LLAppViewer::instance()->getSecondLifeTitle());
if(init)
{
// <FS:Ansariel> Output device selection

View File

@ -33,8 +33,6 @@
// comment out to turn off wind
#define kAUDIO_ENABLE_WIND
//#define kAUDIO_ENABLE_WATER 1 // comment out to turn off water
#define kAUDIO_NUM_BUFFERS 30
#define kAUDIO_NUM_SOURCES 30
void init_audio();
void audio_update_volume(bool force_update = true);

View File

@ -1505,6 +1505,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredMaterialProgram[i].addPermutation("DIFFUSE_ALPHA_MODE", llformat("%d", alpha_mode));
if (alpha_mode != 0)
{
gDeferredMaterialProgram[i].mFeatures.hasAlphaMask = true;
gDeferredMaterialProgram[i].addPermutation("HAS_ALPHA_MASK", "1");
}
if (use_sun_shadow)
{
gDeferredMaterialProgram[i].addPermutation("HAS_SUN_SHADOW", "1");
@ -1563,6 +1569,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
}
gDeferredMaterialWaterProgram[i].addPermutation("DIFFUSE_ALPHA_MODE", llformat("%d", alpha_mode));
if (alpha_mode != 0)
{
gDeferredMaterialWaterProgram[i].mFeatures.hasAlphaMask = true;
gDeferredMaterialWaterProgram[i].addPermutation("HAS_ALPHA_MASK", "1");
}
if (use_sun_shadow)
{
gDeferredMaterialWaterProgram[i].addPermutation("HAS_SUN_SHADOW", "1");

View File

@ -366,15 +366,6 @@ void LLVOCacheEntry::updateDebugSettings()
}
timer.reset();
//the number of frames invisible objects stay in memory
static LLCachedControl<U32> inv_obj_time(gSavedSettings,"NonvisibleObjectsInMemoryTime");
sMinFrameRange = inv_obj_time - 1; //make 0 to be the maximum
//min radius: all objects within this radius remain loaded in memory
static LLCachedControl<F32> min_radius(gSavedSettings,"SceneLoadMinRadius");
sNearRadius = llmin((F32)min_radius, gAgentCamera.mDrawDistance); //can not exceed the draw distance
sNearRadius = llmax(sNearRadius, 1.f); //minimum value is 1.0m
//objects within the view frustum whose visible area is greater than this threshold will be loaded
static LLCachedControl<F32> front_pixel_threshold(gSavedSettings,"SceneLoadFrontPixelThreshold");
sFrontPixelThreshold = front_pixel_threshold;
@ -384,29 +375,38 @@ void LLVOCacheEntry::updateDebugSettings()
sRearPixelThreshold = rear_pixel_threshold;
sRearPixelThreshold = llmax(sRearPixelThreshold, sFrontPixelThreshold); //can not be smaller than sFrontPixelThreshold.
// a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold
static LLCachedControl<F32> rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction");
sRearFarRadius = llmax(rear_max_radius_frac * gAgentCamera.mDrawDistance / 100.f, 1.0f); //minimum value is 1.0m
sRearFarRadius = llmax(sRearFarRadius, (F32)min_radius); //can not be less than "SceneLoadMinRadius".
sRearFarRadius = llmin(sRearFarRadius, gAgentCamera.mDrawDistance); //can not be more than the draw distance.
//make the above parameters adaptive to memory usage
//make parameters adaptive to memory usage
//starts to put restrictions from low_mem_bound_MB, apply tightest restrictions when hits high_mem_bound_MB
static LLCachedControl<U32> low_mem_bound_MB(gSavedSettings,"SceneLoadLowMemoryBound");
static LLCachedControl<U32> high_mem_bound_MB(gSavedSettings,"SceneLoadHighMemoryBound");
LLMemory::updateMemoryInfo() ;
U32 allocated_mem = LLMemory::getAllocatedMemKB().value();
allocated_mem /= 1024; //convert to MB.
if(allocated_mem < low_mem_bound_MB)
{
return;
}
F32 adjust_factor = llmax(0.f, (F32)(high_mem_bound_MB - allocated_mem) / (high_mem_bound_MB - low_mem_bound_MB));
static const F32 KB_to_MB = 1.f / 1024.f;
U32 clamped_memory = llclamp(allocated_mem * KB_to_MB, (F32) low_mem_bound_MB, (F32) high_mem_bound_MB);
const F32 adjust_range = high_mem_bound_MB - low_mem_bound_MB;
const F32 adjust_factor = (high_mem_bound_MB - clamped_memory) / adjust_range; // [0, 1]
sRearFarRadius = llmin(adjust_factor * sRearFarRadius, 96.f); //[0.f, 96.f]
sMinFrameRange = (U32)llclamp(adjust_factor * sMinFrameRange, 10.f, 64.f); //[10, 64]
sNearRadius = llmax(adjust_factor * sNearRadius, 1.0f);
//min radius: all objects within this radius remain loaded in memory
static LLCachedControl<F32> min_radius(gSavedSettings,"SceneLoadMinRadius");
static const F32 MIN_RADIUS = 1.0f;
const F32 draw_radius = gAgentCamera.mDrawDistance;
const F32 clamped_min_radius = llclamp((F32) min_radius, MIN_RADIUS, draw_radius); // [1, mDrawDistance]
sNearRadius = MIN_RADIUS + ((clamped_min_radius - MIN_RADIUS) * adjust_factor);
// a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold
static LLCachedControl<F32> rear_max_radius_frac(gSavedSettings,"SceneLoadRearMaxRadiusFraction");
const F32 min_radius_plus_one = sNearRadius + 1.f;
const F32 max_radius = rear_max_radius_frac * gAgentCamera.mDrawDistance;
const F32 clamped_max_radius = llclamp(max_radius, min_radius_plus_one, draw_radius); // [sNearRadius, mDrawDistance]
sRearFarRadius = min_radius_plus_one + ((clamped_max_radius - min_radius_plus_one) * adjust_factor);
//the number of frames invisible objects stay in memory
static LLCachedControl<U32> inv_obj_time(gSavedSettings,"NonvisibleObjectsInMemoryTime");
static const U32 MIN_FRAMES = 10;
static const U32 MAX_FRAMES = 64;
const U32 clamped_frames = inv_obj_time ? llclamp((U32) inv_obj_time, MIN_FRAMES, MAX_FRAMES) : MAX_FRAMES; // [10, 64], with zero => 64
sMinFrameRange = MIN_FRAMES + ((clamped_frames - MIN_FRAMES) * adjust_factor);
}
//static

View File

@ -1722,8 +1722,21 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo()
}
else
{
LL_WARNS("Voice") << "No voice channel credentials" << LL_ENDL;
LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
if (channel != NULL)
{
if (channel->getSessionName().empty() && channel->getSessionID().isNull())
{
if (LLViewerParcelMgr::getInstance()->allowAgentVoice())
{
LL_WARNS("Voice") << "No channel credentials for default channel" << LL_ENDL;
}
}
else
{
LL_WARNS("Voice") << "No voice channel credentials" << LL_ENDL;
}
}
}
}
else

View File

@ -5755,7 +5755,7 @@ static inline void add_face(T*** list, U32* count, T* face)
{
if (count[1] < MAX_FACE_COUNT)
{
face->setDrawOrderIndex(count[1]);
//face->setDrawOrderIndex(count[1]);
list[1][count[1]++] = face;
}
}
@ -5763,15 +5763,40 @@ static inline void add_face(T*** list, U32* count, T* face)
{
if (count[0] < MAX_FACE_COUNT)
{
face->setDrawOrderIndex(count[0]);
//face->setDrawOrderIndex(count[0]);
list[0][count[0]++] = face;
}
}
}
// return index into linkset for given object (0 for root prim)
U32 get_linkset_index(LLVOVolume* vobj)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE;
if (vobj->isRootEdit())
{
return 0;
}
LLViewerObject* root = vobj->getRootEdit();
U32 idx = 1;
for (const auto& child : root->getChildren())
{
if (child == vobj)
{
return idx;
}
++idx;
}
llassert(false);
return idx; //should never get here
}
void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
if (group->changeLOD())
{
group->mLastUpdateDistance = group->mDistance;
@ -5976,6 +6001,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
avatar->addAttachmentOverridesForObject(vobj, NULL, false);
}
U32 linkset_index = get_linkset_index(vobj);
// Standard rigged mesh attachments:
bool rigged = !vobj->isAnimatedObject() && skinInfo && vobj->isAttachment();
// Animated objects. Have to check for isRiggedMesh() to
@ -5995,6 +6022,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
continue;
}
// order by linkset index first and face index second
facep->setDrawOrderIndex(linkset_index * 100 + i);
//ALWAYS null out vertex buffer on rebuild -- if the face lands in a render
// batch, it will recover its vertex buffer reference from the spatial group
facep->setVertexBuffer(NULL);
@ -6019,11 +6049,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
if (facep->isState(LLFace::RIGGED))
{
//face is not rigged but used to be, remove from rigged face pool
LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool();
if (pool)
{
pool->removeFace(facep);
}
facep->clearState(LLFace::RIGGED);
facep->mAvatar = NULL;
facep->mSkinInfo = NULL;
@ -6500,6 +6525,13 @@ struct CompareBatchBreakerRigged
}
};
struct CompareDrawOrder
{
bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
{
return lhs->getDrawOrderIndex() < rhs->getDrawOrderIndex();
}
};
U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort, BOOL batch_textures, BOOL rigged)
{
@ -6544,6 +6576,11 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
//sort faces by things that break batches, including avatar and mesh id
std::sort(faces, faces + face_count, CompareBatchBreakerRigged());
}
else
{
// preserve legacy draw order for rigged faces
std::sort(faces, faces + face_count, CompareDrawOrder());
}
}
else if (!distance_sort)
{

View File

@ -7488,13 +7488,13 @@ void LLPipeline::doResetVertexBuffers(bool forced)
gSky.resetVertexBuffers();
LLVOPartGroup::destroyGL();
gGL.resetVertexBuffer();
if ( LLPathingLib::getInstance() )
{
LLPathingLib::getInstance()->cleanupVBOManager();
}
LLVOPartGroup::destroyGL();
gGL.resetVertexBuffer();
SUBSYSTEM_CLEANUP(LLVertexBuffer);