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.cppmaster
commit
7185e97905
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ public:
|
|||
return lhs->mAvatarp < rhs->mAvatarp;
|
||||
}
|
||||
|
||||
return lhs->mRenderOrder > rhs->mRenderOrder;
|
||||
return lhs->mRenderOrder < rhs->mRenderOrder;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue