Merge remote-tracking branch 'origin/main' into DRTVWR-528

master
Brad Kittenbrink 2022-11-28 14:33:27 -08:00
commit 0b188ac04e
660 changed files with 23663 additions and 10608 deletions

View File

@ -482,9 +482,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>2653c3627fd8687ff9e003425fd14834</string>
<string>439d92ec73f0500ba1671faad2bd8090</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90199/821852/dullahan-1.12.3.202111032211_91.1.21_g9dd45fe_chromium-91.0.4472.114-darwin64-565428.tar.bz2</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104637/916643/dullahan-1.12.4.202209142017_91.1.21_g9dd45fe_chromium-91.0.4472.114-darwin64-575005.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
@ -494,9 +494,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>b4003772562a5dd40bc112eec7cba5f5</string>
<string>2a7c01da15de77bc1fd1863327174d5e</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90201/821871/dullahan-1.12.3.202111032221_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows-565428.tar.bz2</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104638/916654/dullahan-1.12.4.202209142021_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows-575005.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
@ -506,16 +506,16 @@
<key>archive</key>
<map>
<key>hash</key>
<string>d9030d7a7390b3bda7de2adcc27e535a</string>
<string>d06bee9b2517fbb09ba1a65e6d675361</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90200/821876/dullahan-1.12.3.202111032221_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows64-565428.tar.bz2</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104639/916659/dullahan-1.12.4.202209142021_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows64-575005.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>version</key>
<string>1.12.3.202111032221_91.1.21_g9dd45fe_chromium-91.0.4472.114</string>
<string>1.12.4.202209142021_91.1.21_g9dd45fe_chromium-91.0.4472.114</string>
</map>
<key>expat</key>
<map>
@ -2393,9 +2393,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>6ce3cbaed968a69fb7a2cca80220874d</string>
<string>b583668b28fde0490e6953f10e93e4ab</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80380/758537/slvoice-4.10.0000.32327.5fc3fe7c.558436-darwin64-558436.tar.bz2</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/98681/871545/slvoice-4.10.0000.32327.5fc3fe7c.571099-darwin64-571099.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
@ -2417,9 +2417,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>2eb38c5eff4d0f18fbb89d0c30c4f0a4</string>
<string>6e0ed41653955afe8eeb8945776cf07b</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80382/758550/slvoice-4.10.0000.32327.5fc3fe7c.558436-windows-558436.tar.bz2</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/98683/871560/slvoice-4.10.0000.32327.5fc3fe7c.571099-windows-571099.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
@ -2429,16 +2429,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>9ee8f3cbc5369c598a998c61961ed16d</string>
<string>c39735851fd05c194d0be09b8f9e8cb7</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80381/758551/slvoice-4.10.0000.32327.5fc3fe7c.558436-windows64-558436.tar.bz2</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/98682/871552/slvoice-4.10.0000.32327.5fc3fe7c.571099-windows64-571099.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>version</key>
<string>4.10.0000.32327.5fc3fe7c.558436</string>
<string>4.10.0000.32327.5fc3fe7c.571099</string>
</map>
<key>threejs</key>
<map>
@ -2888,7 +2888,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>package_description</key>
<map>
<key>canonical_repo</key>
<string>https://bitbucket.org/lindenlab/viewer</string>
<string>https://github.com/secondlife/viewer</string>
<key>copyright</key>
<string>Copyright (c) 2020, Linden Research, Inc.</string>
<key>description</key>

View File

@ -223,6 +223,7 @@ Ansariel Hiller
MAINT-8723
SL-10385
SL-10891
SL-10675
SL-13364
SL-13858
SL-13697
@ -235,6 +236,7 @@ Ansariel Hiller
SL-15226
SL-15227
SL-15398
SL-18432
Aralara Rajal
Arare Chantilly
CHUIBUG-191
@ -400,6 +402,7 @@ Cinder Roxley
STORM-2127
STORM-2144
SL-3404
SL-17634
Clara Young
Coaldust Numbers
VWR-1095
@ -820,6 +823,7 @@ Jonathan Yap
Kadah Coba
STORM-1060
STORM-1843
SL-10675
Jondan Lundquist
Joosten Briebers
MAINT-7074
@ -1120,6 +1124,7 @@ Nicky Dasmijn
SL-14541
SL-16438
SL-17218
SL-17585
Nicky Perian
OPEN-1
STORM-1087

View File

@ -927,6 +927,9 @@ BOOL LLAvatarAppearance::loadAvatar()
return FALSE;
}
// initialize mJointAliasMap
getJointAliases();
// avatar_lad.xml : <skeleton>
if( !loadSkeletonNode() )
{
@ -1047,7 +1050,6 @@ BOOL LLAvatarAppearance::loadAvatar()
return FALSE;
}
}
return TRUE;
}

View File

@ -612,14 +612,16 @@ BOOL LLPolyMeshSharedData::loadMesh( const std::string& fileName )
// we reached the end of the morphs
break;
}
LLPolyMorphData* morph_data = new LLPolyMorphData(std::string(morphName));
std::string morph_name(morphName);
LLPolyMorphData* morph_data = new LLPolyMorphData(morph_name);
BOOL result = morph_data->loadBinary(fp, this);
if (!result)
{
delete morph_data;
continue;
LL_WARNS() << "Failure loading " << morph_name << " from " << fileName << LL_ENDL;
delete morph_data;
continue;
}
mMorphData.insert(morph_data);

View File

@ -156,7 +156,9 @@ BOOL LLPolyMorphData::loadBinary(LLFILE *fp, LLPolyMeshSharedData *mesh)
if (mVertexIndices[v] > 10000)
{
LL_ERRS() << "Bad morph index: " << mVertexIndices[v] << LL_ENDL;
// Bad install? These are usually .llm files from 'character' fodler
LL_WARNS() << "Bad morph index " << v << ": " << mVertexIndices[v] << LL_ENDL;
return FALSE;
}

332
indra/llaudio/llaudiodecodemgr.cpp Normal file → Executable file
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
@ -532,146 +532,254 @@ 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);
try
{
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)
{
// Audio decode has errored
return decode_state;
}
std::string uuid_str;
std::string d_path;
// 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;
}
LLTimer timer;
timer.reset();
// 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.
uuid.toString(uuid_str);
d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf";
enqueueFinishAudio(decode_id, decode_state);
});
}
catch (const LLThreadSafeQueueInterrupt&)
{
// Shutdown
// Consider making processQueue() do a cleanup instead
// of starting more decodes
LL_WARNS() << "Tried to start decoding on shutdown" << LL_ENDL;
}
}
}
mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
if (!mCurrentDecodep->initDecode())
{
mCurrentDecodep = NULL;
}
}
}
}
LLPointer<LLVorbisDecodeState> beginDecodingAndWritingAudio(const LLUUID &decode_id)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
LL_DEBUGS() << "Decoding " << decode_id << " from audio queue!" << LL_ENDL;
std::string d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, decode_id.asString()) + ".dsf";
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)
@ -687,7 +795,7 @@ BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
{
// Just put it on the decode queue.
LL_DEBUGS("AudioEngine") << "addDecodeRequest for " << uuid << " has local asset file already" << LL_ENDL;
mImpl->mDecodeQueue.push_back(uuid);
mImpl->mDecodeQueue.push_back(uuid);
return TRUE;
}

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

@ -83,18 +83,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.
@ -111,18 +103,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;
@ -131,10 +119,6 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::stri
void LLAudioEngine::shutdown()
{
// Clean up decode manager
delete gAudioDecodeMgrp;
gAudioDecodeMgrp = NULL;
// Clean up wind source
cleanupWind();
@ -156,14 +140,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;
@ -229,7 +213,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])
{
@ -240,20 +224,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])
{
@ -276,7 +254,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
{
// The source is done playing, clean it up.
delete sourcep;
mAllSources.erase(iter++);
iter = mAllSources.erase(iter);
continue;
}
@ -473,7 +451,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])
{
@ -489,7 +467,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])
{
@ -498,7 +476,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
@ -532,7 +510,7 @@ bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uu
{
if (audio_uuid.notNull())
{
gAudioDecodeMgrp->addDecodeRequest(audio_uuid);
LLAudioDecodeMgr::getInstance()->addDecodeRequest(audio_uuid);
}
}
else
@ -561,7 +539,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])
{
@ -574,7 +552,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])
{
@ -605,7 +583,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])
{
@ -633,7 +611,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();
@ -660,7 +638,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)
{
@ -678,7 +656,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;
@ -827,7 +805,8 @@ void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_i
addAudioSource(asp);
if (pos_global.isExactlyZero())
{
asp->setAmbient(true);
// For sound preview and UI
asp->setForcedPriority(true);
}
else
{
@ -953,6 +932,7 @@ LLAudioSource * LLAudioEngine::findAudioSource(const LLUUID &source_id)
LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
data_map::iterator iter;
iter = mAllData.find(audio_uuid);
if (iter == mAllData.end())
@ -1039,7 +1019,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])
{
@ -1067,7 +1047,7 @@ void LLAudioEngine::startNextTransfer()
continue;
}
if (!adp->hasLocalData() && adp->hasValidData())
if (!adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1078,7 +1058,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])
{
@ -1103,7 +1083,7 @@ void LLAudioEngine::startNextTransfer()
continue;
}
if (!adp->hasLocalData() && adp->hasValidData())
if (!adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1115,7 +1095,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])
{
@ -1143,7 +1123,7 @@ void LLAudioEngine::startNextTransfer()
continue;
}
if (!adp->hasLocalData() && adp->hasValidData())
if (!adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1171,7 +1151,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();
@ -1179,7 +1159,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();
@ -1194,7 +1174,7 @@ void LLAudioEngine::startNextTransfer()
continue;
}
if (!adp->hasLocalData() && adp->hasValidData())
if (!adp->hasLocalData() && !adp->hasDecodeFailed())
{
asset_id = adp->getID();
max_pri = asp->getPriority();
@ -1235,7 +1215,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);
@ -1252,9 +1232,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;
@ -1273,7 +1253,7 @@ LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32
mPriority(0.f),
mGain(gain),
mSourceMuted(false),
mAmbient(false),
mForcedPriority(false),
mLoop(false),
mSyncMaster(false),
mSyncSlave(false),
@ -1324,11 +1304,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 ;
@ -1339,7 +1323,7 @@ void LLAudioSource::update()
void LLAudioSource::updatePriority()
{
if (isAmbient())
if (isForcedPriority())
{
mPriority = 1.f;
}
@ -1624,12 +1608,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;
@ -1786,7 +1770,8 @@ LLAudioData::LLAudioData(const LLUUID &uuid) :
mHasLocalData(false),
mHasDecodedData(false),
mHasCompletedDecode(false),
mHasValidData(true)
mHasDecodeFailed(false),
mHasWAVLoadFailed(false)
{
if (uuid.isNull())
{
@ -1821,12 +1806,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;
}
@ -1835,6 +1822,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;
}
@ -1843,7 +1832,8 @@ bool LLAudioData::load()
mID.toString(uuid_str);
wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf";
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);

65
indra/llaudio/llaudioengine.h Normal file → Executable file
View File

@ -47,8 +47,8 @@ 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 MAX_BUFFERS 40 // Some extra for preloading, maybe?
#define LL_MAX_AUDIO_CHANNELS 30
#define LL_MAX_AUDIO_BUFFERS 40 // Some extra for preloading, maybe?
class LLAudioSource;
class LLAudioData;
@ -88,7 +88,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();
@ -96,7 +96,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();
//
@ -209,7 +209,6 @@ protected:
S32 mLastStatus;
S32 mNumChannels;
bool mEnableWind;
LLUUID mCurrentTransfer; // Audio file currently being transferred by the system
@ -224,11 +223,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.
@ -266,8 +265,8 @@ public:
void addAudioData(LLAudioData *adp, bool set_current = TRUE);
void setAmbient(const bool ambient) { mAmbient = ambient; }
bool isAmbient() const { return mAmbient; }
void setForcedPriority(const bool ambient) { mForcedPriority = ambient; }
bool isForcedPriority() const { return mForcedPriority; }
void setLoop(const bool loop) { mLoop = loop; }
bool isLoop() const { return mLoop; }
@ -326,7 +325,7 @@ protected:
F32 mPriority;
F32 mGain;
bool mSourceMuted;
bool mAmbient;
bool mForcedPriority; // ignore mute, set high priority, researved for sound preview and UI
bool mLoop;
bool mSyncMaster;
bool mSyncSlave;
@ -360,32 +359,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

@ -74,7 +74,7 @@ static inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
return true;
}
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;
@ -86,7 +86,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");
@ -98,7 +98,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 + 2);
result = mSystem->setSoftwareChannels(LL_MAX_AUDIO_CHANNELS + 2);
Check_FMOD_Error(result, "FMOD::System::setSoftwareChannels");
FMOD_ADVANCEDSETTINGS settings;
@ -127,7 +127,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
{
LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL;
if (mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK &&
(result = mSystem->init(num_channels + 2, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK)
(result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL;
audio_ok = true;
@ -149,7 +149,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
{
LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
if (mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK &&
(result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK)
(result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0)) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
audio_ok = true;
@ -190,7 +190,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
// initialize the FMOD engine
// 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(num_channels + 2, fmod_flags, 0);
result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, 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*/);
@ -198,7 +198,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons
{
return false;
}
result = mSystem->init(num_channels + 2, fmod_flags, 0);
result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0);
}
if (Check_FMOD_Error(result, "Error initializing FMOD Studio"))
{
@ -519,9 +519,9 @@ void LLAudioChannelFMODSTUDIO::update3DPosition()
return;
}
if (mCurrentSourcep->isAmbient())
if (mCurrentSourcep->isForcedPriority())
{
// Ambient sound, don't need to do any positional updates.
// Prioritized UI and preview sounds don't need to do any positional updates.
set3DMode(false);
}
else

View File

@ -51,7 +51,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))
{
@ -297,7 +297,7 @@ void LLAudioChannelOpenAL::update3DPosition()
{
return;
}
if (mCurrentSourcep->isAmbient())
if (mCurrentSourcep->isForcedPriority())
{
alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0);
alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0);

View File

@ -40,7 +40,7 @@ class LLAudioEngine_OpenAL : public LLAudioEngine
LLAudioEngine_OpenAL();
virtual ~LLAudioEngine_OpenAL();
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

@ -70,7 +70,11 @@ mRetryCount(0)
// Must be larger than the usual Second Life frame stutter time.
const U32 buffer_seconds = 10; //sec
const U32 estimated_bitrate = 128; //kbit/sec
mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
FMOD_RESULT result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << "setStreamBufferSize error: " << FMOD_ErrorString(result) << LL_ENDL;
}
// Here's where we set the size of the network buffer and some buffering
// parameters. In this case we want a network buffer of 16k, we want it
@ -134,7 +138,7 @@ void LLStreamingAudio_FMODSTUDIO::killDeadStreams()
{
LL_INFOS("FMOD") << "Closed dead stream" << LL_ENDL;
delete streamp;
mDeadStreams.erase(iter++);
iter = mDeadStreams.erase(iter);
}
else
{
@ -404,7 +408,11 @@ FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream()
if (mStreamChannel)
return mStreamChannel; //Already have a channel for this stream.
mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel);
FMOD_RESULT result = mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << FMOD_ErrorString(result) << LL_ENDL;
}
return mStreamChannel;
}
@ -445,16 +453,29 @@ bool LLAudioStreamManagerFMODSTUDIO::stopStream()
FMOD_OPENSTATE LLAudioStreamManagerFMODSTUDIO::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy)
{
FMOD_OPENSTATE state;
mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy);
FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << FMOD_ErrorString(result) << LL_ENDL;
}
return state;
}
void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime)
{
mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES);
FMOD_RESULT result = mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << "setStreamBufferSize error: " << FMOD_ErrorString(result) << LL_ENDL;
return;
}
FMOD_ADVANCEDSETTINGS settings;
memset(&settings, 0, sizeof(settings));
settings.cbSize = sizeof(settings);
settings.defaultDecodeBufferSize = decodebuffertime;//ms
mSystem->setAdvancedSettings(&settings);
result = mSystem->setAdvancedSettings(&settings);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << "setAdvancedSettings error: " << FMOD_ErrorString(result) << LL_ENDL;
}
}

View File

@ -44,6 +44,14 @@ using namespace std;
#define INCHES_TO_METERS 0.02540005f
/// The .bvh does not have a formal spec, and different readers interpret things in their own way.
/// In OUR usage, frame 0 is used in optimization and is not considered to be part of the animation.
const S32 NUMBER_OF_IGNORED_FRAMES_AT_START = 1;
/// In our usage, the last frame is used only to indicate what the penultimate frame should be interpolated towards.
/// I.e., the animation only plays up to the start of the last frame. There is no hold or exptrapolation past that point..
/// Thus there are two frame of the total that do not contribute to the total running time of the animation.
const S32 NUMBER_OF_UNPLAYED_FRAMES = NUMBER_OF_IGNORED_FRAMES_AT_START + 1;
const F32 POSITION_KEYFRAME_THRESHOLD_SQUARED = 0.03f * 0.03f;
const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f;
@ -865,7 +873,10 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
return E_ST_NO_FRAME_TIME;
}
mDuration = (F32)mNumFrames * mFrameTime;
// If the user only supplies one animation frame (after the ignored reference frame 0), hold for mFrameTime.
// If the user supples exactly one total frame, it isn't clear if that is a pose or reference frame, and the
// behavior is not defined. In this case, retain historical undefined behavior.
mDuration = llmax((F32)(mNumFrames - NUMBER_OF_UNPLAYED_FRAMES), 1.0f) * mFrameTime;
if (!mLoop)
{
mLoopOutPoint = mDuration;
@ -1355,12 +1366,13 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp)
LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
S32 outcount = 0;
S32 frame = 1;
S32 frame = 0;
for ( ki = joint->mKeys.begin();
ki != joint->mKeys.end();
++ki )
{
if ((frame == 1) && joint->mRelativeRotationKey)
if ((frame == 0) && joint->mRelativeRotationKey)
{
first_frame_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
@ -1373,7 +1385,7 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp)
continue;
}
time = (F32)frame * mFrameTime;
time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts.
if (mergeParent)
{
@ -1433,12 +1445,12 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp)
LLVector3 relPos = joint->mRelativePosition;
LLVector3 relKey;
frame = 1;
frame = 0;
for ( ki = joint->mKeys.begin();
ki != joint->mKeys.end();
++ki )
{
if ((frame == 1) && joint->mRelativePositionKey)
if ((frame == 0) && joint->mRelativePositionKey)
{
relKey.setVec(ki->mPos);
}
@ -1449,7 +1461,7 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp)
continue;
}
time = (F32)frame * mFrameTime;
time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts.
LLVector3 inPos = (LLVector3(ki->mPos) - relKey) * ~first_frame_rot;// * fixup_rot;
LLVector3 outPos = inPos * frameRot * offsetRot;

View File

@ -1222,8 +1222,11 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8
//-----------------------------------------------------------------------------
// deserialize()
//
// allow_invalid_joints should be true when handling existing content, to avoid breakage.
// During upload, we should be more restrictive and reject such animations.
//-----------------------------------------------------------------------------
BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id)
BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, bool allow_invalid_joints)
{
BOOL old_version = FALSE;
mJointMotionList = new LLKeyframeMotion::JointMotionList;
@ -1344,6 +1347,15 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id)
return FALSE;
}
//SL-17206 hack to alter Female_land loop setting, while current behavior won't be changed serverside
LLUUID const female_land_anim("ca1baf4d-0a18-5a1f-0330-e4bd1e71f09e");
LLUUID const formal_female_land_anim("6a9a173b-61fa-3ad5-01fa-a851cfc5f66a");
if (female_land_anim == asset_id || formal_female_land_anim == asset_id)
{
LL_WARNS() << "Animation(" << asset_id << ") won't be looped." << LL_ENDL;
mJointMotionList->mLoop = FALSE;
}
//-------------------------------------------------------------------------
// get easeIn and easeOut
//-------------------------------------------------------------------------
@ -1443,6 +1455,7 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id)
if (joint)
{
S32 joint_num = joint->getJointNum();
joint_name = joint->getName(); // canonical name in case this is an alias.
// LL_INFOS() << " joint: " << joint_name << LL_ENDL;
if ((joint_num >= (S32)LL_CHARACTER_MAX_ANIMATED_JOINTS) || (joint_num < 0))
{
@ -1457,7 +1470,10 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id)
{
LL_WARNS() << "invalid joint name: " << joint_name
<< " for animation " << asset_id << LL_ENDL;
//return FALSE;
if (!allow_invalid_joints)
{
return FALSE;
}
}
joint_motion->mJointName = joint_name;
@ -2096,8 +2112,9 @@ U32 LLKeyframeMotion::getFileSize()
//-----------------------------------------------------------------------------
// dumpToFile()
//-----------------------------------------------------------------------------
void LLKeyframeMotion::dumpToFile(const std::string& name)
bool LLKeyframeMotion::dumpToFile(const std::string& name)
{
bool succ = false;
if (isLoaded())
{
std::string outfile_base;
@ -2114,10 +2131,24 @@ void LLKeyframeMotion::dumpToFile(const std::string& name)
const LLUUID& id = getID();
outfile_base = id.asString();
}
std::string outfilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfile_base + ".anim");
if (gDirUtilp->getExtension(outfile_base).empty())
{
outfile_base += ".anim";
}
std::string outfilename;
if (gDirUtilp->getDirName(outfile_base).empty())
{
outfilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfile_base);
}
else
{
outfilename = outfile_base;
}
if (LLFile::isfile(outfilename))
{
return;
LL_WARNS() << outfilename << " already exists, write failed" << LL_ENDL;
return false;
}
S32 file_size = getFileSize();
@ -2131,11 +2162,13 @@ void LLKeyframeMotion::dumpToFile(const std::string& name)
outfile.open(outfilename, LL_APR_WPB);
if (outfile.getFileHandle())
{
outfile.write(buffer, file_size);
S32 wrote_bytes = outfile.write(buffer, file_size);
succ = (wrote_bytes == file_size);
}
}
delete [] buffer;
}
return succ;
}
//-----------------------------------------------------------------------------

View File

@ -156,9 +156,9 @@ public:
public:
U32 getFileSize();
BOOL serialize(LLDataPacker& dp) const;
BOOL deserialize(LLDataPacker& dp, const LLUUID& asset_id);
BOOL deserialize(LLDataPacker& dp, const LLUUID& asset_id, bool allow_invalid_joints = true);
BOOL isLoaded() { return mJointMotionList != NULL; }
void dumpToFile(const std::string& name);
bool dumpToFile(const std::string& name);
// setters for modifying a keyframe animation
@ -432,6 +432,9 @@ protected:
F32 mLastUpdateTime;
F32 mLastLoopedTime;
AssetStatus mAssetStatus;
public:
void setCharacter(LLCharacter* character) { mCharacter = character; }
};
class LLKeyframeDataCache

View File

@ -266,6 +266,11 @@ set(llcommon_HEADER_FILES
workqueue.h
StackWalker.h
)
if (DARWIN)
list(APPEND llcommon_HEADER_FILES llsys_objc.h)
list(APPEND llcommon_SOURCE_FILES llsys_objc.mm)
endif (DARWIN)
set_source_files_properties(${llcommon_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
@ -313,12 +318,6 @@ target_link_libraries(
${TRACY_LIBRARY}
)
if (DARWIN)
include(CMakeFindFrameworks)
find_library(CARBON_LIBRARY Carbon)
target_link_libraries(llcommon ${CARBON_LIBRARY})
endif (DARWIN)
add_dependencies(llcommon stage_third_party_libs)
if (LL_TESTS)

View File

@ -116,14 +116,20 @@ void LLAlignedArray<T, alignment>::resize(U32 size)
template <class T, U32 alignment>
T& LLAlignedArray<T, alignment>::operator[](int idx)
{
llassert(idx < mElementCount);
if(idx >= mElementCount || idx < 0)
{
LL_ERRS() << "Out of bounds LLAlignedArray, requested: " << (S32)idx << " size: " << mElementCount << LL_ENDL;
}
return mArray[idx];
}
template <class T, U32 alignment>
const T& LLAlignedArray<T, alignment>::operator[](int idx) const
{
llassert(idx < mElementCount);
if (idx >= mElementCount || idx < 0)
{
LL_ERRS() << "Out of bounds LLAlignedArray, requested: " << (S32)idx << " size: " << mElementCount << LL_ENDL;
}
return mArray[idx];
}

View File

@ -35,6 +35,7 @@
// STL headers
// std headers
#include <atomic>
#include <stdexcept>
// external library headers
#include <boost/bind.hpp>
#include <boost/fiber/fiber.hpp>
@ -214,6 +215,22 @@ std::string LLCoros::logname()
return data.mName.empty()? data.getKey() : data.mName;
}
void LLCoros::saveException(const std::string& name, std::exception_ptr exc)
{
mExceptionQueue.emplace(name, exc);
}
void LLCoros::rethrow()
{
if (! mExceptionQueue.empty())
{
ExceptionData front = mExceptionQueue.front();
mExceptionQueue.pop();
LL_WARNS("LLCoros") << "Rethrowing exception from coroutine " << front.name << LL_ENDL;
std::rethrow_exception(front.exception);
}
}
void LLCoros::setStackSize(S32 stacksize)
{
LL_DEBUGS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL;
@ -302,11 +319,11 @@ U32 cpp_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop,
}
}
void LLCoros::winlevel(const std::string& name, const callable_t& callable)
void LLCoros::sehHandle(const std::string& name, const LLCoros::callable_t& callable)
{
__try
{
toplevelTryWrapper(name, callable);
LLCoros::toplevelTryWrapper(name, callable);
}
__except (cpp_exception_filter(GetExceptionCode(), GetExceptionInformation(), name))
{
@ -321,7 +338,6 @@ void LLCoros::winlevel(const std::string& name, const callable_t& callable)
throw std::exception(integer_string);
}
}
#endif
void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& callable)
@ -350,11 +366,19 @@ void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& call
}
catch (...)
{
#if LL_WINDOWS
// Any OTHER kind of uncaught exception will cause the viewer to
// crash, hopefully informatively.
// crash, SEH handling should catch it and report to bugsplat.
LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name));
// to not modify callstack
throw;
#else
// Stash any OTHER kind of uncaught exception in the rethrow() queue
// to be rethrown by the main fiber.
LL_WARNS("LLCoros") << "Capturing uncaught exception in coroutine "
<< name << LL_ENDL;
LLCoros::instance().saveException(name, std::current_exception());
#endif
}
}
@ -364,8 +388,9 @@ void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& call
void LLCoros::toplevel(std::string name, callable_t callable)
{
#if LL_WINDOWS
// Can not use __try in functions that require unwinding, so use one more wrapper
winlevel(name, callable);
// Because SEH can's have unwinding, need to call a wrapper
// 'try' is inside SEH handling to not catch LLContinue
sehHandle(name, callable);
#else
toplevelTryWrapper(name, callable);
#endif

View File

@ -38,6 +38,8 @@
#include "llinstancetracker.h"
#include <boost/function.hpp>
#include <string>
#include <exception>
#include <queue>
// e.g. #include LLCOROS_MUTEX_HEADER
#define LLCOROS_MUTEX_HEADER <boost/fiber/mutex.hpp>
@ -156,6 +158,19 @@ public:
* LLCoros::launch()).
*/
static std::string getName();
/**
* rethrow() is called by the thread's main fiber to propagate an
* exception from any coroutine into the main fiber, where it can engage
* the normal unhandled-exception machinery, up to and including crash
* reporting.
*
* LLCoros maintains a queue of otherwise-uncaught exceptions from
* terminated coroutines. Each call to rethrow() pops the first of those
* and rethrows it. When the queue is empty (normal case), rethrow() is a
* no-op.
*/
void rethrow();
/**
* This variation returns a name suitable for log messages: the explicit
@ -292,13 +307,27 @@ public:
private:
std::string generateDistinctName(const std::string& prefix) const;
#if LL_WINDOWS
void winlevel(const std::string& name, const callable_t& callable);
#endif
void toplevelTryWrapper(const std::string& name, const callable_t& callable);
void toplevel(std::string name, callable_t callable);
#if LL_WINDOWS
void sehHandle(const std::string& name, const callable_t& callable); // calls toplevelTryWrapper
#endif
void toplevel(std::string name, callable_t callable); // calls sehHandle or toplevelTryWrapper
struct CoroData;
static CoroData& get_CoroData(const std::string& caller);
void saveException(const std::string& name, std::exception_ptr exc);
struct ExceptionData
{
ExceptionData(const std::string& nm, std::exception_ptr exc):
name(nm),
exception(exc)
{}
// name of coroutine that originally threw this exception
std::string name;
// the thrown exception
std::exception_ptr exception;
};
std::queue<ExceptionData> mExceptionQueue;
S32 mStackSize;

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

@ -118,7 +118,11 @@ namespace
eMONTIOR_MWAIT=33,
eCPLDebugStore=34,
eThermalMonitor2=35,
eAltivec=36
eAltivec=36,
eSSE3S_Features = 37,
eSSE4_1_Features = 38,
eSSE4_2_Features = 39,
eSSE4a_Features = 40,
};
const char* cpu_feature_names[] =
@ -161,7 +165,11 @@ namespace
"CPL Qualified Debug Store",
"Thermal Monitor 2",
"Altivec"
"Altivec",
"SSE3S Instructions",
"SSE4.1 Instructions",
"SSE4.2 Instructions",
"SSE4a Instructions",
};
std::string intel_CPUFamilyName(int composed_family)
@ -250,6 +258,31 @@ public:
return hasExtension(cpu_feature_names[eSSE2_Ext]);
}
bool hasSSE3() const
{
return hasExtension(cpu_feature_names[eSSE3_Features]);
}
bool hasSSE3S() const
{
return hasExtension(cpu_feature_names[eSSE3S_Features]);
}
bool hasSSE41() const
{
return hasExtension(cpu_feature_names[eSSE4_1_Features]);
}
bool hasSSE42() const
{
return hasExtension(cpu_feature_names[eSSE4_2_Features]);
}
bool hasSSE4a() const
{
return hasExtension(cpu_feature_names[eSSE4a_Features]);
}
bool hasAltivec() const
{
return hasExtension("Altivec");
@ -473,6 +506,12 @@ private:
*((int*)(cpu_vendor+4)) = cpu_info[3];
*((int*)(cpu_vendor+8)) = cpu_info[2];
setInfo(eVendor, cpu_vendor);
std::string cmp_vendor(cpu_vendor);
bool is_amd = false;
if (cmp_vendor == "AuthenticAMD")
{
is_amd = true;
}
// Get the information associated with each valid Id
for(unsigned int i=0; i<=ids; ++i)
@ -504,6 +543,7 @@ private:
if(cpu_info[2] & 0x8)
{
// intel specific SSE3 suplements
setExtension(cpu_feature_names[eMONTIOR_MWAIT]);
}
@ -516,7 +556,22 @@ private:
{
setExtension(cpu_feature_names[eThermalMonitor2]);
}
if (cpu_info[2] & 0x200)
{
setExtension(cpu_feature_names[eSSE3S_Features]);
}
if (cpu_info[2] & 0x80000)
{
setExtension(cpu_feature_names[eSSE4_1_Features]);
}
if (cpu_info[2] & 0x100000)
{
setExtension(cpu_feature_names[eSSE4_2_Features]);
}
unsigned int feature_info = (unsigned int) cpu_info[3];
for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1)
{
@ -543,8 +598,17 @@ private:
__cpuid(cpu_info, i);
// Interpret CPU brand string and cache information.
if (i == 0x80000002)
memcpy(cpu_brand_string, cpu_info, sizeof(cpu_info));
if (i == 0x80000001)
{
if (is_amd)
{
setExtension(cpu_feature_names[eSSE4a_Features]);
}
}
else if (i == 0x80000002)
{
memcpy(cpu_brand_string, cpu_info, sizeof(cpu_info));
}
else if (i == 0x80000003)
memcpy(cpu_brand_string + 16, cpu_info, sizeof(cpu_info));
else if (i == 0x80000004)
@ -690,6 +754,41 @@ private:
uint64_t ext_feature_info = getSysctlInt64("machdep.cpu.extfeature_bits");
S32 *ext_feature_infos = (S32*)(&ext_feature_info);
setConfig(eExtFeatureBits, ext_feature_infos[0]);
char cpu_features[1024];
len = sizeof(cpu_features);
memset(cpu_features, 0, len);
sysctlbyname("machdep.cpu.features", (void*)cpu_features, &len, NULL, 0);
std::string cpu_features_str(cpu_features);
cpu_features_str = " " + cpu_features_str + " ";
if (cpu_features_str.find(" SSE3 ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE3_Features]);
}
if (cpu_features_str.find(" SSSE3 ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE3S_Features]);
}
if (cpu_features_str.find(" SSE4.1 ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE4_1_Features]);
}
if (cpu_features_str.find(" SSE4.2 ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE4_2_Features]);
}
if (cpu_features_str.find(" SSE4A ") != std::string::npos)
{
// Not supposed to happen?
setExtension(cpu_feature_names[eSSE4a_Features]);
}
}
};
@ -800,6 +899,31 @@ private:
{
setExtension(cpu_feature_names[eSSE2_Ext]);
}
if (flags.find(" pni ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE3_Features]);
}
if (flags.find(" ssse3 ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE3S_Features]);
}
if (flags.find(" sse4_1 ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE4_1_Features]);
}
if (flags.find(" sse4_2 ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE4_2_Features]);
}
if (flags.find(" sse4a ") != std::string::npos)
{
setExtension(cpu_feature_names[eSSE4a_Features]);
}
# endif // LL_X86
}
@ -860,6 +984,11 @@ LLProcessorInfo::~LLProcessorInfo() {}
F64MegahertzImplicit LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); }
bool LLProcessorInfo::hasSSE() const { return mImpl->hasSSE(); }
bool LLProcessorInfo::hasSSE2() const { return mImpl->hasSSE2(); }
bool LLProcessorInfo::hasSSE3() const { return mImpl->hasSSE3(); }
bool LLProcessorInfo::hasSSE3S() const { return mImpl->hasSSE3S(); }
bool LLProcessorInfo::hasSSE41() const { return mImpl->hasSSE41(); }
bool LLProcessorInfo::hasSSE42() const { return mImpl->hasSSE42(); }
bool LLProcessorInfo::hasSSE4a() const { return mImpl->hasSSE4a(); }
bool LLProcessorInfo::hasAltivec() const { return mImpl->hasAltivec(); }
std::string LLProcessorInfo::getCPUFamilyName() const { return mImpl->getCPUFamilyName(); }
std::string LLProcessorInfo::getCPUBrandName() const { return mImpl->getCPUBrandName(); }

View File

@ -54,6 +54,11 @@ public:
F64MegahertzImplicit getCPUFrequency() const;
bool hasSSE() const;
bool hasSSE2() const;
bool hasSSE3() const;
bool hasSSE3S() const;
bool hasSSE41() const;
bool hasSSE42() const;
bool hasSSE4a() const;
bool hasAltivec() const;
std::string getCPUFamilyName() const;
std::string getCPUBrandName() const;

View File

@ -30,7 +30,7 @@
#include "llerror.h"
// maximum reference count before sounding memory leak alarm
const S32 gMaxRefCount = 65536;
const S32 gMaxRefCount = S32_MAX;
LLRefCount::LLRefCount(const LLRefCount& other)
: mRef(0)

View File

@ -64,6 +64,7 @@ using namespace llsd;
# include <psapi.h> // GetPerformanceInfo() et al.
# include <VersionHelpers.h>
#elif LL_DARWIN
# include "llsys_objc.h"
# include <errno.h>
# include <sys/sysctl.h>
# include <sys/utsname.h>
@ -74,12 +75,6 @@ using namespace llsd;
# include <mach/mach_host.h>
# include <mach/task.h>
# include <mach/task_info.h>
// disable warnings about Gestalt calls being deprecated
// until Apple get's on the ball and provides an alternative
//
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif LL_LINUX
# include <errno.h>
# include <sys/utsname.h>
@ -278,12 +273,9 @@ LLOSInfo::LLOSInfo() :
{
const char * DARWIN_PRODUCT_NAME = "Mac OS X";
SInt32 major_version, minor_version, bugfix_version;
OSErr r1 = Gestalt(gestaltSystemVersionMajor, &major_version);
OSErr r2 = Gestalt(gestaltSystemVersionMinor, &minor_version);
OSErr r3 = Gestalt(gestaltSystemVersionBugFix, &bugfix_version);
S32 major_version, minor_version, bugfix_version = 0;
if((r1 == noErr) && (r2 == noErr) && (r3 == noErr))
if (LLGetDarwinOSInfo(major_version, minor_version, bugfix_version))
{
mMajorVer = major_version;
mMinorVer = minor_version;
@ -597,6 +589,11 @@ LLCPUInfo::LLCPUInfo()
// proc.WriteInfoTextFile("procInfo.txt");
mHasSSE = proc.hasSSE();
mHasSSE2 = proc.hasSSE2();
mHasSSE3 = proc.hasSSE3();
mHasSSE3S = proc.hasSSE3S();
mHasSSE41 = proc.hasSSE41();
mHasSSE42 = proc.hasSSE42();
mHasSSE4a = proc.hasSSE4a();
mHasAltivec = proc.hasAltivec();
mCPUMHz = (F64)proc.getCPUFrequency();
mFamily = proc.getCPUFamilyName();
@ -609,6 +606,35 @@ LLCPUInfo::LLCPUInfo()
}
mCPUString = out.str();
LLStringUtil::trim(mCPUString);
if (mHasSSE)
{
mSSEVersions.append("1");
}
if (mHasSSE2)
{
mSSEVersions.append("2");
}
if (mHasSSE3)
{
mSSEVersions.append("3");
}
if (mHasSSE3S)
{
mSSEVersions.append("3S");
}
if (mHasSSE41)
{
mSSEVersions.append("4.1");
}
if (mHasSSE42)
{
mSSEVersions.append("4.2");
}
if (mHasSSE4a)
{
mSSEVersions.append("4a");
}
}
bool LLCPUInfo::hasAltivec() const
@ -626,6 +652,31 @@ bool LLCPUInfo::hasSSE2() const
return mHasSSE2;
}
bool LLCPUInfo::hasSSE3() const
{
return mHasSSE3;
}
bool LLCPUInfo::hasSSE3S() const
{
return mHasSSE3S;
}
bool LLCPUInfo::hasSSE41() const
{
return mHasSSE41;
}
bool LLCPUInfo::hasSSE42() const
{
return mHasSSE42;
}
bool LLCPUInfo::hasSSE4a() const
{
return mHasSSE4a;
}
F64 LLCPUInfo::getMHz() const
{
return mCPUMHz;
@ -636,6 +687,11 @@ std::string LLCPUInfo::getCPUString() const
return mCPUString;
}
const LLSD& LLCPUInfo::getSSEVersions() const
{
return mSSEVersions;
}
void LLCPUInfo::stream(std::ostream& s) const
{
// gather machine information.
@ -645,6 +701,11 @@ void LLCPUInfo::stream(std::ostream& s) const
// CPU's attributes regardless of platform
s << "->mHasSSE: " << (U32)mHasSSE << std::endl;
s << "->mHasSSE2: " << (U32)mHasSSE2 << std::endl;
s << "->mHasSSE3: " << (U32)mHasSSE3 << std::endl;
s << "->mHasSSE3S: " << (U32)mHasSSE3S << std::endl;
s << "->mHasSSE41: " << (U32)mHasSSE41 << std::endl;
s << "->mHasSSE42: " << (U32)mHasSSE42 << std::endl;
s << "->mHasSSE4a: " << (U32)mHasSSE4a << std::endl;
s << "->mHasAltivec: " << (U32)mHasAltivec << std::endl;
s << "->mCPUMHz: " << mCPUMHz << std::endl;
s << "->mCPUString: " << mCPUString << std::endl;
@ -1341,10 +1402,3 @@ BOOL gzip_file(const std::string& srcfile, const std::string& dstfile)
if (dst != NULL) gzclose(dst);
return retval;
}
#if LL_DARWIN
// disable warnings about Gestalt calls being deprecated
// until Apple get's on the ball and provides an alternative
//
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

View File

@ -80,10 +80,16 @@ public:
void stream(std::ostream& s) const;
std::string getCPUString() const;
const LLSD& getSSEVersions() const;
bool hasAltivec() const;
bool hasSSE() const;
bool hasSSE2() const;
bool hasSSE3() const;
bool hasSSE3S() const;
bool hasSSE41() const;
bool hasSSE42() const;
bool hasSSE4a() const;
F64 getMHz() const;
// Family is "AMD Duron" or "Intel Pentium Pro"
@ -92,10 +98,16 @@ public:
private:
bool mHasSSE;
bool mHasSSE2;
bool mHasSSE3;
bool mHasSSE3S;
bool mHasSSE41;
bool mHasSSE42;
bool mHasSSE4a;
bool mHasAltivec;
F64 mCPUMHz;
std::string mFamily;
std::string mCPUString;
LLSD mSSEVersions;
};
//=============================================================================

View File

@ -0,0 +1,33 @@
/**
* @file llsys_objc.h
* @brief Header file for llsys_objc.mm
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLSYS_OBJC_H
#define LL_LLSYS_OBJC_H
bool LLGetDarwinOSInfo(int &major, int &minor, int &patch);
#endif // LL_LLSYS_OBJC_H

View File

@ -0,0 +1,64 @@
/**
* @file llsys_objc.mm
* @brief obj-c implementation of the system information functions
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#import "llsys_objc.h"
#import <AppKit/AppKit.h>
static int intAtStringIndex(NSArray *array, int index)
{
return [(NSString *)[array objectAtIndex:index] integerValue];
}
bool LLGetDarwinOSInfo(int &major, int &minor, int &patch)
{
if (NSAppKitVersionNumber > NSAppKitVersionNumber10_8)
{
NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
major = osVersion.majorVersion;
minor = osVersion.minorVersion;
patch = osVersion.patchVersion;
}
else
{
NSString* versionString = [[NSDictionary dictionaryWithContentsOfFile:
@"/System/Library/CoreServices/SystemVersion.plist"] objectForKey:@"ProductVersion"];
NSArray* versions = [versionString componentsSeparatedByString:@"."];
NSUInteger count = [versions count];
if (count > 0)
{
major = intAtStringIndex(versions, 0);
if (count > 1)
{
minor = intAtStringIndex(versions, 1);
if (count > 2)
{
patch = intAtStringIndex(versions, 2);
}
}
}
}
return true;
}

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

@ -131,28 +131,44 @@ void LLDiskCache::purge()
LL_INFOS() << "Purging cache to a maximum of " << mMaxSizeBytes << " bytes" << LL_ENDL;
std::vector<bool> file_removed;
if (mEnableCacheDebugInfo)
{
file_removed.reserve(file_info.size());
}
uintmax_t file_size_total = 0;
for (file_info_t& entry : file_info)
{
file_size_total += entry.second.first;
std::string action = "";
if (file_size_total > mMaxSizeBytes)
bool should_remove = file_size_total > mMaxSizeBytes;
if (mEnableCacheDebugInfo)
{
file_removed.push_back(should_remove);
}
if (should_remove)
{
action = "DELETE:";
boost::filesystem::remove(entry.second.second, ec);
if (ec.failed())
{
LL_WARNS() << "Failed to delete cache file " << entry.second.second << ": " << ec.message() << LL_ENDL;
}
}
else
{
action = " KEEP:";
}
}
if (mEnableCacheDebugInfo)
if (mEnableCacheDebugInfo)
{
auto end_time = std::chrono::high_resolution_clock::now();
auto execute_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
// Log afterward so it doesn't affect the time measurement
// Logging thousands of file results can take hundreds of milliseconds
for (size_t i = 0; i < file_info.size(); ++i)
{
const file_info_t& entry = file_info[i];
const bool removed = file_removed[i];
const std::string action = removed ? "DELETE:" : "KEEP:";
// have to do this because of LL_INFO/LL_END weirdness
std::ostringstream line;
@ -163,12 +179,7 @@ void LLDiskCache::purge()
line << " (" << file_size_total << "/" << mMaxSizeBytes << ")";
LL_INFOS() << line.str() << LL_ENDL;
}
}
if (mEnableCacheDebugInfo)
{
auto end_time = std::chrono::high_resolution_clock::now();
auto execute_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
LL_INFOS() << "Total dir size after purge is " << dirFileSize(mCacheDir) << LL_ENDL;
LL_INFOS() << "Cache purge took " << execute_time << " ms to execute for " << file_info.size() << " files" << LL_ENDL;
}
@ -354,6 +365,38 @@ void LLDiskCache::clearCache()
}
}
void LLDiskCache::removeOldVFSFiles()
{
//VFS files won't be created, so consider removing this code later
static const char CACHE_FORMAT[] = "inv.llsd";
static const char DB_FORMAT[] = "db2.x";
boost::system::error_code ec;
#if LL_WINDOWS
std::wstring cache_path(utf8str_to_utf16str(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "")));
#else
std::string cache_path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""));
#endif
if (boost::filesystem::is_directory(cache_path, ec) && !ec.failed())
{
for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_path, ec), {}))
{
if (boost::filesystem::is_regular_file(entry, ec) && !ec.failed())
{
if ((entry.path().string().find(CACHE_FORMAT) != std::string::npos) ||
(entry.path().string().find(DB_FORMAT) != std::string::npos))
{
boost::filesystem::remove(entry, ec);
if (ec.failed())
{
LL_WARNS() << "Failed to delete cache file " << entry << ": " << ec.message() << LL_ENDL;
}
}
}
}
}
}
uintmax_t LLDiskCache::dirFileSize(const std::string dir)
{
uintmax_t total_file_size = 0;

View File

@ -140,6 +140,8 @@ class LLDiskCache :
*/
const std::string getCacheInfo();
void removeOldVFSFiles();
private:
/**
* Utility function to gather the total size the files in a given

View File

@ -234,6 +234,8 @@ void LLParcel::init(const LLUUID &owner_id,
setRegionAllowEnvironmentOverride(FALSE);
setParcelEnvironmentVersion(INVALID_PARCEL_ENVIRONMENT_VERSION);
setObscureMOAP(false);
}
void LLParcel::overrideOwner(const LLUUID& owner_id, BOOL is_group_owned)
@ -540,6 +542,7 @@ void LLParcel::packMessage(LLSD& msg)
msg["see_avs"] = (LLSD::Boolean) getSeeAVs();
msg["group_av_sounds"] = (LLSD::Boolean) getAllowGroupAVSounds();
msg["any_av_sounds"] = (LLSD::Boolean) getAllowAnyAVSounds();
msg["obscure_moap"] = (LLSD::Boolean) getObscureMOAP();
}

View File

@ -306,6 +306,7 @@ public:
void setRestrictPushObject(BOOL b) { setParcelFlag(PF_RESTRICT_PUSHOBJECT, b); }
void setAllowGroupAVSounds(BOOL b) { mAllowGroupAVSounds = b; }
void setAllowAnyAVSounds(BOOL b) { mAllowAnyAVSounds = b; }
void setObscureMOAP(bool b) { mObscureMOAP = b; }
void setDrawDistance(F32 dist) { mDrawDistance = dist; }
void setSalePrice(S32 price) { mSalePrice = price; }
@ -517,6 +518,8 @@ public:
BOOL getAllowGroupAVSounds() const { return mAllowGroupAVSounds; }
BOOL getAllowAnyAVSounds() const { return mAllowAnyAVSounds; }
bool getObscureMOAP() const { return mObscureMOAP; }
F32 getDrawDistance() const { return mDrawDistance; }
S32 getSalePrice() const { return mSalePrice; }
@ -670,6 +673,7 @@ protected:
BOOL mRegionAllowEnvironmentOverride;
BOOL mAllowGroupAVSounds;
BOOL mAllowAnyAVSounds;
bool mObscureMOAP;
S32 mCurrentEnvironmentVersion;
bool mIsDefaultDayCycle;

View File

@ -4,12 +4,14 @@ project(llmath)
include(00-Common)
include(LLCommon)
include(LLMeshOptimizer)
include(bugsplat)
include(Boost)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
${LLMESHOPTIMIZER_INCLUDE_DIRS}
)
set(llmath_SOURCE_FILES
@ -109,6 +111,7 @@ add_library (llmath ${llmath_SOURCE_FILES})
target_link_libraries(llmath
${LLCOMMON_LIBRARIES}
${LLMESHOPTIMIZER_LIBRARIES}
)
# Add tests

View File

@ -48,52 +48,59 @@ extern float gOctreeMinSize;
#define LL_OCTREE_MAX_CAPACITY 128
#endif*/
template <class T> class LLOctreeNode;
// T is the type of the element referenced by the octree node.
// T_PTR determines how pointers to elements are stored internally.
// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and
// deletes elements removed from the tree.
// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API
// user is responsible for managing the storage lifecycle of elements added to
// the tree.
template <class T, typename T_PTR> class LLOctreeNode;
template <class T>
template <class T, typename T_PTR>
class LLOctreeListener: public LLTreeListener<T>
{
public:
typedef LLTreeListener<T> BaseType;
typedef LLOctreeNode<T> oct_node;
typedef LLOctreeNode<T, T_PTR> oct_node;
virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0;
virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0;
};
template <class T>
template <class T, typename T_PTR>
class LLOctreeTraveler
{
public:
virtual void traverse(const LLOctreeNode<T>* node);
virtual void visit(const LLOctreeNode<T>* branch) = 0;
virtual void traverse(const LLOctreeNode<T, T_PTR>* node);
virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0;
};
template <class T>
class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T>
template <class T, typename T_PTR>
class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR>
{
public:
virtual void traverse(const LLOctreeNode<T>* node) override;
virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override;
};
template <class T>
template <class T, typename T_PTR>
class alignas(16) LLOctreeNode : public LLTreeNode<T>
{
LL_ALIGN_NEW
public:
typedef LLOctreeTraveler<T> oct_traveler;
typedef LLTreeTraveler<T> tree_traveler;
typedef std::vector< LLPointer<T> > element_list; // note: don't remove the whitespace between "> >"
typedef LLPointer<T>* element_iter;
typedef const LLPointer<T>* const_element_iter;
typedef LLOctreeTraveler<T, T_PTR> oct_traveler;
typedef LLTreeTraveler<T> tree_traveler;
typedef std::vector<T_PTR> element_list;
typedef typename element_list::iterator element_iter;
typedef typename element_list::const_iterator const_element_iter;
typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
typedef LLOctreeNode<T>** child_list;
typedef LLOctreeNode<T>** child_iter;
typedef LLOctreeNode<T, T_PTR>** child_list;
typedef LLOctreeNode<T, T_PTR>** child_iter;
typedef LLTreeNode<T> BaseType;
typedef LLOctreeNode<T> oct_node;
typedef LLOctreeListener<T> oct_listener;
typedef LLTreeNode<T> BaseType;
typedef LLOctreeNode<T, T_PTR> oct_node;
typedef LLOctreeListener<T, T_PTR> oct_listener;
enum
{
@ -108,9 +115,6 @@ public:
mOctant(octant)
{
llassert(size[0] >= gOctreeMinSize*0.5f);
//always keep a NULL terminated list to avoid out of bounds exceptions in debug builds
mData.push_back(NULL);
mDataEnd = &mData[0];
mCenter = center;
mSize = size;
@ -121,8 +125,6 @@ public:
mOctant = ((oct_node*) mParent)->getOctant(mCenter);
}
mElementCount = 0;
clearChildren();
}
@ -130,15 +132,14 @@ public:
{
BaseType::destroyListeners();
for (U32 i = 0; i < mElementCount; ++i)
const U32 element_count = getElementCount();
for (U32 i = 0; i < element_count; ++i)
{
mData[i]->setBinIndex(-1);
mData[i] = NULL;
}
mData.clear();
mData.push_back(NULL);
mDataEnd = &mData[0];
for (U32 i = 0; i < getChildCount(); i++)
{
@ -238,14 +239,12 @@ public:
void accept(oct_traveler* visitor) { visitor->visit(this); }
virtual bool isLeaf() const { return mChildCount == 0; }
U32 getElementCount() const { return mElementCount; }
bool isEmpty() const { return mElementCount == 0; }
element_list& getData() { return mData; }
const element_list& getData() const { return mData; }
element_iter getDataBegin() { return &mData[0]; }
element_iter getDataEnd() { return mDataEnd; }
const_element_iter getDataBegin() const { return &mData[0]; }
const_element_iter getDataEnd() const { return mDataEnd; }
U32 getElementCount() const { return (U32)mData.size(); }
bool isEmpty() const { return mData.empty(); }
element_iter getDataBegin() { return mData.begin(); }
element_iter getDataEnd() { return mData.end(); }
const_element_iter getDataBegin() const { return mData.cbegin(); }
const_element_iter getDataEnd() const { return mData.cend(); }
U32 getChildCount() const { return mChildCount; }
oct_node* getChild(U32 index) { return mChild[index]; }
@ -263,7 +262,7 @@ public:
U8 idx = mChildMap[i];
if (idx != NO_CHILD_NODES)
{
LLOctreeNode<T>* child = mChild[idx];
oct_node* child = mChild[idx];
if (child->getOctant() != i)
{
@ -281,7 +280,7 @@ public:
oct_node* getNodeAt(const LLVector4a& pos, const F32& rad)
{
LLOctreeNode<T>* node = this;
oct_node* node = this;
if (node->isInside(pos, rad))
{
@ -303,7 +302,7 @@ public:
}
else if (!node->contains(rad) && node->getParent())
{ //if we got here, data does not exist in this node
return ((LLOctreeNode<T>*) node->getParent())->getNodeAt(pos, rad);
return ((oct_node*) node->getParent())->getNodeAt(pos, rad);
}
return node;
@ -318,7 +317,7 @@ public:
OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL;
return false;
}
LLOctreeNode<T>* parent = getOctParent();
oct_node* parent = getOctParent();
//is it here?
if (isInside(data->getPositionGroup()))
@ -326,11 +325,8 @@ public:
if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) ||
(data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity)))
{ //it belongs here
mData.push_back(NULL);
mData[mElementCount] = data;
mElementCount++;
mDataEnd = &mData[mElementCount];
data->setBinIndex(mElementCount-1);
mData.push_back(data);
data->setBinIndex(getElementCount() - 1);
BaseType::insert(data);
return true;
}
@ -354,7 +350,7 @@ public:
size.mul(0.5f);
//push center in direction of data
LLOctreeNode<T>::pushCenter(center, size, data);
oct_node::pushCenter(center, size, data);
// handle case where floating point number gets too small
LLVector4a val;
@ -366,11 +362,8 @@ public:
if( lt == 0x7 )
{
mData.push_back(NULL);
mData[mElementCount] = data;
mElementCount++;
mDataEnd = &mData[mElementCount];
data->setBinIndex(mElementCount-1);
mData.push_back(data);
data->setBinIndex(getElementCount() - 1);
BaseType::insert(data);
return true;
}
@ -396,7 +389,7 @@ public:
llassert(size[0] >= gOctreeMinSize*0.5f);
//make the new kid
child = new LLOctreeNode<T>(center, size, this);
child = new oct_node(center, size, this);
addChild(child);
child->insert(data);
@ -429,28 +422,25 @@ public:
}
void _remove(T* data, S32 i)
{ //precondition -- mElementCount > 0, idx is in range [0, mElementCount)
{ //precondition -- getElementCount() > 0, idx is in range [0, getElementCount())
mElementCount--;
data->setBinIndex(-1);
if (mElementCount > 0)
const U32 new_element_count = getElementCount() - 1;
if (new_element_count > 0)
{
if (mElementCount != i)
if (new_element_count != i)
{
mData[i] = mData[mElementCount]; //might unref data, do not access data after this point
mData[i] = mData[new_element_count]; //might unref data, do not access data after this point
mData[i]->setBinIndex(i);
}
mData[mElementCount] = NULL;
mData[new_element_count] = NULL;
mData.pop_back();
mDataEnd = &mData[mElementCount];
}
else
{
mData.clear();
mData.push_back(NULL);
mDataEnd = &mData[0];
}
this->notifyRemoval(data);
@ -463,7 +453,7 @@ public:
S32 i = data->getBinIndex();
if (i >= 0 && i < mElementCount)
if (i >= 0 && i < getElementCount())
{
if (mData[i] == data)
{ //found it
@ -506,7 +496,8 @@ public:
void removeByAddress(T* data)
{
for (U32 i = 0; i < mElementCount; ++i)
const U32 element_count = getElementCount();
for (U32 i = 0; i < element_count; ++i)
{
if (mData[i] == data)
{ //we have data
@ -518,7 +509,7 @@ public:
for (U32 i = 0; i < getChildCount(); i++)
{ //we don't contain data, so pass this guy down
LLOctreeNode<T>* child = (LLOctreeNode<T>*) getChild(i);
oct_node* child = (oct_node*) getChild(i);
child->removeByAddress(data);
}
}
@ -672,22 +663,20 @@ protected:
oct_node* mParent;
U8 mOctant;
LLOctreeNode<T>* mChild[8];
oct_node* mChild[8];
U8 mChildMap[8];
U32 mChildCount;
element_list mData;
element_iter mDataEnd;
U32 mElementCount;
};
//just like a regular node, except it might expand on insert and compress on balance
template <class T>
class LLOctreeRoot : public LLOctreeNode<T>
template <class T, typename T_PTR>
class LLOctreeRoot : public LLOctreeNode<T, T_PTR>
{
public:
typedef LLOctreeNode<T> BaseType;
typedef LLOctreeNode<T> oct_node;
typedef LLOctreeNode<T, T_PTR> BaseType;
typedef LLOctreeNode<T, T_PTR> oct_node;
LLOctreeRoot(const LLVector4a& center,
const LLVector4a& size,
@ -768,7 +757,7 @@ public:
oct_node* node = this->getNodeAt(data);
if (node == this)
{
LLOctreeNode<T>::insert(data);
oct_node::insert(data);
}
else if (node->isInside(data->getPositionGroup()))
{
@ -788,13 +777,13 @@ public:
LLVector4a center, size;
center = this->getCenter();
size = this->getSize();
LLOctreeNode<T>::pushCenter(center, size, data);
oct_node::pushCenter(center, size, data);
this->setCenter(center);
size.mul(2.f);
this->setSize(size);
this->updateMinMax();
}
LLOctreeNode<T>::insert(data);
oct_node::insert(data);
}
else
{
@ -806,7 +795,7 @@ public:
//expand this node
LLVector4a newcenter(center);
LLOctreeNode<T>::pushCenter(newcenter, size, data);
oct_node::pushCenter(newcenter, size, data);
this->setCenter(newcenter);
LLVector4a size2 = size;
size2.mul(2.f);
@ -816,11 +805,11 @@ public:
llassert(size[0] >= gOctreeMinSize);
//copy our children to a new branch
LLOctreeNode<T>* newnode = new LLOctreeNode<T>(center, size, this);
oct_node* newnode = new oct_node(center, size, this);
for (U32 i = 0; i < this->getChildCount(); i++)
{
LLOctreeNode<T>* child = this->getChild(i);
oct_node* child = this->getChild(i);
newnode->addChild(child);
}
@ -846,8 +835,8 @@ public:
//========================
// LLOctreeTraveler
//========================
template <class T>
void LLOctreeTraveler<T>::traverse(const LLOctreeNode<T>* node)
template <class T, typename T_PTR>
void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
{
node->accept(this);
for (U32 i = 0; i < node->getChildCount(); i++)
@ -856,8 +845,8 @@ void LLOctreeTraveler<T>::traverse(const LLOctreeNode<T>* node)
}
}
template <class T>
void LLOctreeTravelerDepthFirst<T>::traverse(const LLOctreeNode<T>* node)
template <class T, typename T_PTR>
void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
{
for (U32 i = 0; i < node->getChildCount(); i++)
{

View File

@ -49,6 +49,7 @@
#include "llsdserialize.h"
#include "llvector4a.h"
#include "llmatrix4a.h"
#include "llmeshoptimizer.h"
#include "lltimer.h"
#define DEBUG_SILHOUETTE_BINORMALS 0
@ -370,7 +371,7 @@ BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, cons
}
}
class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle>
class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*>
{
public:
const LLVolumeFace* mFace;
@ -380,7 +381,7 @@ public:
mFace = face;
}
virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
{ //this is a depth first traversal, so it's safe to assum all children have complete
//bounding data
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
@ -398,8 +399,7 @@ public:
min = *(tri->mV[0]);
max = *(tri->mV[0]);
for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter =
branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
{ //for each triangle in node
//stretch by triangles in node
@ -683,7 +683,7 @@ LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, BOOL flat, F3
Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
static LLAlignedArray<LLVector4a,64> pt;
static thread_local LLAlignedArray<LLVector4a,64> pt;
pt.resize(mTotal) ;
for (S32 i=mTotalOut;i<mTotal;i++)
@ -1624,9 +1624,6 @@ BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
//genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
genNGon(params, llfloor(MIN_DETAIL_FACES * detail));
F32 t = 0.f;
F32 tStep = 1.0f / mPath.size();
F32 toggle = 0.5f;
for (S32 i=0;i<(S32)mPath.size();i++)
{
@ -1635,7 +1632,6 @@ BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
toggle = -0.5f;
else
toggle = 0.5f;
t += tStep;
}
}
@ -2426,6 +2422,13 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
//copy out indices
S32 num_indices = idx.size() / 2;
const S32 indices_to_discard = num_indices % 3;
if (indices_to_discard > 0)
{
// Invalid number of triangle indices
LL_WARNS() << "Incomplete triangle discarded from face! Indices count " << num_indices << " was not divisible by 3. face index: " << i << " Total: " << face_count << LL_ENDL;
num_indices -= indices_to_discard;
}
face.resizeIndices(num_indices);
if (num_indices > 2 && !face.mIndices)
@ -2441,8 +2444,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
}
U16* indices = (U16*) &(idx[0]);
U32 count = idx.size()/2;
for (U32 j = 0; j < count; ++j)
for (U32 j = 0; j < num_indices; ++j)
{
face.mIndices[j] = indices[j];
}
@ -3834,8 +3836,8 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
#if DEBUG_SILHOUETTE_EDGE_MAP
//for each triangle
U32 count = face.mNumIndices;
for (U32 j = 0; j < count/3; j++) {
U32 tri_count = face.mNumIndices / 3;
for (U32 j = 0; j < tri_count; j++) {
//get vertices
S32 v1 = face.mIndices[j*3+0];
S32 v2 = face.mIndices[j*3+1];
@ -3853,7 +3855,7 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
continue;
}
if (nIndex >= (S32) count/3) {
if (nIndex >= (S32)tri_count) {
continue;
}
//get neighbor vertices
@ -4145,13 +4147,13 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
}
else
{
if (!face.mOctree)
if (!face.getOctree())
{
face.createOctree();
}
LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
intersect.traverse(face.mOctree);
intersect.traverse(face.getOctree());
if (intersect.mHitFace)
{
hit_face = i;
@ -4706,6 +4708,7 @@ LLVolumeFace::LLVolumeFace() :
#endif
mWeightsScrubbed(FALSE),
mOctree(NULL),
mOctreeTriangles(NULL),
mOptimized(FALSE)
{
mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
@ -4735,8 +4738,9 @@ LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
mJointIndices(NULL),
#endif
mWeightsScrubbed(FALSE),
mOctree(NULL)
{
mOctree(NULL),
mOctreeTriangles(NULL)
{
mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
mCenter = mExtents+2;
*this = src;
@ -4876,8 +4880,7 @@ void LLVolumeFace::freeData()
mJustWeights = NULL;
#endif
delete mOctree;
mOctree = NULL;
destroyOctree();
}
BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
@ -4885,8 +4888,7 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
//tree for this face is no longer valid
delete mOctree;
mOctree = NULL;
destroyOctree();
LL_CHECK_MEMORY
BOOL ret = FALSE ;
@ -4952,6 +4954,50 @@ bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a
return a.mV[2] < b.mV[2];
}
void LLVolumeFace::remap()
{
// Generate a remap buffer
std::vector<unsigned int> remap(mNumVertices);
S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0],
mIndices,
mNumIndices,
mPositions,
mNormals,
mTexCoords,
mNumVertices);
// Allocate new buffers
S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF;
U16* remap_indices = (U16*)ll_aligned_malloc_16(size);
S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF;
LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size);
LLVector4a* remap_normals = remap_positions + remap_vertices_count;
LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count);
// Fill the buffers
LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]);
LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]);
LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]);
LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]);
// Free unused buffers
ll_aligned_free_16(mIndices);
ll_aligned_free<64>(mPositions);
// Tangets are now invalid
ll_aligned_free_16(mTangents);
mTangents = NULL;
// Assign new values
mIndices = remap_indices;
mPositions = remap_positions;
mNormals = remap_normals;
mTexCoords = remap_tex_coords;
mNumVertices = remap_vertices_count;
mNumAllocatedVertices = remap_vertices_count;
}
void LLVolumeFace::optimize(F32 angle_cutoff)
{
LLVolumeFace new_face;
@ -5555,21 +5601,27 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
if (mOctree)
if (getOctree())
{
return;
}
mOctree = new LLOctreeRoot<LLVolumeTriangle>(center, size, NULL);
new LLVolumeOctreeListener(mOctree);
llassert(mNumIndices % 3 == 0);
for (U32 i = 0; i < mNumIndices; i+= 3)
mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL);
new LLVolumeOctreeListener(mOctree);
const U32 num_triangles = mNumIndices / 3;
// Initialize all the triangles we need
mOctreeTriangles = new LLVolumeTriangle[num_triangles];
for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
{ //for each triangle
LLPointer<LLVolumeTriangle> tri = new LLVolumeTriangle();
const U32 index = triangle_index * 3;
LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
const LLVector4a& v0 = mPositions[mIndices[i]];
const LLVector4a& v1 = mPositions[mIndices[i+1]];
const LLVector4a& v2 = mPositions[mIndices[i+2]];
const LLVector4a& v0 = mPositions[mIndices[index]];
const LLVector4a& v1 = mPositions[mIndices[index + 1]];
const LLVector4a& v2 = mPositions[mIndices[index + 2]];
//store pointers to vertex data
tri->mV[0] = &v0;
@ -5577,9 +5629,9 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
tri->mV[2] = &v2;
//store indices
tri->mIndex[0] = mIndices[i];
tri->mIndex[1] = mIndices[i+1];
tri->mIndex[2] = mIndices[i+2];
tri->mIndex[0] = mIndices[index];
tri->mIndex[1] = mIndices[index + 1];
tri->mIndex[2] = mIndices[index + 2];
//get minimum point
LLVector4a min = v0;
@ -5622,6 +5674,19 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
}
}
void LLVolumeFace::destroyOctree()
{
delete mOctree;
mOctree = NULL;
delete[] mOctreeTriangles;
mOctreeTriangles = NULL;
}
const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const
{
return mOctree;
}
void LLVolumeFace::swapData(LLVolumeFace& rhs)
{
@ -6493,6 +6558,7 @@ void LLVolumeFace::allocateJointIndices(S32 num_verts)
void LLVolumeFace::resizeIndices(S32 num_indices)
{
ll_aligned_free_16(mIndices);
llassert(num_indices % 3 == 0);
if (num_indices)
{
@ -6629,13 +6695,19 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
else
{
// Get s value for tex-coord.
if (!flat)
S32 index = mBeginS + s;
if (index >= profile.size())
{
// edge?
ss = flat ? 1.f - begin_stex : 1.f;
}
else if (!flat)
{
ss = profile[mBeginS + s][2];
ss = profile[index][2];
}
else
{
ss = profile[mBeginS + s][2] - begin_stex;
ss = profile[index][2] - begin_stex;
}
}
@ -6821,7 +6893,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
LLVector4a* norm = mNormals;
static LLAlignedArray<LLVector4a, 64> triangle_normals;
static thread_local LLAlignedArray<LLVector4a, 64> triangle_normals;
try
{
triangle_normals.resize(count);

View File

@ -35,7 +35,8 @@ class LLVolumeParams;
class LLProfile;
class LLPath;
template <class T> class LLOctreeNode;
template<class T> class LLPointer;
template <class T, typename T_PTR> class LLOctreeNode;
class LLVolumeFace;
class LLVolume;
@ -902,10 +903,17 @@ public:
typedef std::map<LLVector3, std::vector<VertexMapData>, VertexMapData::ComparePosition > PointMap;
};
// Eliminates non unique triangles, takes positions,
// normals and texture coordinates into account.
void remap();
void optimize(F32 angle_cutoff = 2.f);
bool cacheOptimize();
void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f));
void destroyOctree();
// Get a reference to the octree, which may be null
const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const;
enum
{
@ -973,13 +981,14 @@ public:
// Which joints are rigged to, and the bounding box of any rigged
// vertices per joint.
LLJointRiggingInfoTab mJointRiggingInfoTab;
LLOctreeNode<LLVolumeTriangle>* mOctree;
//whether or not face has been cache optimized
BOOL mOptimized;
private:
LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree;
LLVolumeTriangle* mOctreeTriangles;
BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE);
BOOL createCap(LLVolume* volume, BOOL partial_build = FALSE);
BOOL createSide(LLVolume* volume, BOOL partial_build = FALSE);

View File

@ -75,7 +75,7 @@ BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, c
}
LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node)
LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
{
node->addListener(this);
}
@ -85,13 +85,12 @@ LLVolumeOctreeListener::~LLVolumeOctreeListener()
}
void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent,
LLOctreeNode<LLVolumeTriangle>* child)
void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent,
LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child)
{
new LLVolumeOctreeListener(child);
}
LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir,
const LLVolumeFace* face, F32* closest_t,
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
@ -108,7 +107,7 @@ LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& sta
mEnd.setAdd(mStart, mDir);
}
void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>* node)
void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
{
LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0);
@ -122,9 +121,9 @@ void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>
}
}
void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle>* node)
void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
{
for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter =
for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter =
node->getDataBegin(); iter != node->getDataEnd(); ++iter)
{
const LLVolumeTriangle* tri = *iter;
@ -219,7 +218,7 @@ const F32& LLVolumeTriangle::getBinRadius() const
//TEST CODE
void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
{
LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
@ -256,7 +255,7 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
}
//children fit, check data
for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getDataBegin();
for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin();
iter != branch->getDataEnd(); ++iter)
{
const LLVolumeTriangle* tri = *iter;
@ -273,4 +272,3 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
}
}

View File

@ -77,11 +77,11 @@ public:
};
class alignas(16) LLVolumeOctreeListener : public LLOctreeListener<LLVolumeTriangle>
class alignas(16) LLVolumeOctreeListener : public LLOctreeListener<LLVolumeTriangle, LLVolumeTriangle*>
{
LL_ALIGN_NEW
public:
LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node);
LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
~LLVolumeOctreeListener();
LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs)
@ -96,11 +96,9 @@ public:
}
//LISTENER FUNCTIONS
virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent,
LLOctreeNode<LLVolumeTriangle>* child);
virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child);
virtual void handleStateChange(const LLTreeNode<LLVolumeTriangle>* node) { }
virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle>* parent,
const LLOctreeNode<LLVolumeTriangle>* child) { }
virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child) { }
virtual void handleInsertion(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
virtual void handleRemoval(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
virtual void handleDestruction(const LLTreeNode<LLVolumeTriangle>* node) { }
@ -111,7 +109,7 @@ public:
LL_ALIGN_16(LLVector4a mExtents[2]); // extents (min, max) of this node and all its children
};
class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle>
class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*>
{
public:
const LLVolumeFace* mFace;
@ -129,14 +127,14 @@ public:
const LLVolumeFace* face, F32* closest_t,
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
void traverse(const LLOctreeNode<LLVolumeTriangle>* node);
void traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
virtual void visit(const LLOctreeNode<LLVolumeTriangle>* node);
virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
};
class LLVolumeOctreeValidate : public LLOctreeTraveler<LLVolumeTriangle>
class LLVolumeOctreeValidate : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*>
{
virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch);
virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch);
};
#endif

View File

@ -28,6 +28,9 @@
#include "meshoptimizer.h"
#include "llmath.h"
#include "v2math.h"
LLMeshOptimizer::LLMeshOptimizer()
{
// Todo: Looks like for memory management, we can add allocator and deallocator callbacks
@ -40,24 +43,218 @@ LLMeshOptimizer::~LLMeshOptimizer()
}
//static
void LLMeshOptimizer::generateShadowIndexBuffer(U16 *destination,
const U16 *indices,
void LLMeshOptimizer::generateShadowIndexBufferU32(U32 *destination,
const U32 *indices,
U64 index_count,
const LLVector4a *vertex_positions,
U64 vertex_count,
U64 vertex_positions_stride
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count
)
{
meshopt_generateShadowIndexBuffer<unsigned short>(destination,
meshopt_Stream streams[3];
S32 index = 0;
if (vertex_positions)
{
streams[index].data = (const float*)vertex_positions;
// Despite being LLVector4a, only x, y and z are in use
streams[index].size = sizeof(F32) * 3;
streams[index].stride = sizeof(F32) * 4;
index++;
}
if (normals)
{
streams[index].data = (const float*)normals;
streams[index].size = sizeof(F32) * 3;
streams[index].stride = sizeof(F32) * 4;
index++;
}
if (text_coords)
{
streams[index].data = (const float*)text_coords;
streams[index].size = sizeof(F32) * 2;
streams[index].stride = sizeof(F32) * 2;
index++;
}
if (index == 0)
{
// invalid
return;
}
meshopt_generateShadowIndexBufferMulti<unsigned int>(destination,
indices,
index_count,
(const float*)vertex_positions, // verify that it is correct to convert to float
vertex_count,
sizeof(LLVector4a),
vertex_positions_stride
streams,
index
);
}
//static
void LLMeshOptimizer::generateShadowIndexBufferU16(U16 *destination,
const U16 *indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count
)
{
meshopt_Stream streams[3];
S32 index = 0;
if (vertex_positions)
{
streams[index].data = (const float*)vertex_positions;
streams[index].size = sizeof(F32) * 3;
streams[index].stride = sizeof(F32) * 4;
index++;
}
if (normals)
{
streams[index].data = (const float*)normals;
streams[index].size = sizeof(F32) * 3;
streams[index].stride = sizeof(F32) * 4;
index++;
}
if (text_coords)
{
streams[index].data = (const float*)text_coords;
streams[index].size = sizeof(F32) * 2;
streams[index].stride = sizeof(F32) * 2;
index++;
}
if (index == 0)
{
// invalid
return;
}
meshopt_generateShadowIndexBufferMulti<unsigned short>(destination,
indices,
index_count,
vertex_count,
streams,
index);
}
void LLMeshOptimizer::optimizeVertexCacheU32(U32 * destination, const U32 * indices, U64 index_count, U64 vertex_count)
{
meshopt_optimizeVertexCache<unsigned int>(destination, indices, index_count, vertex_count);
}
void LLMeshOptimizer::optimizeVertexCacheU16(U16 * destination, const U16 * indices, U64 index_count, U64 vertex_count)
{
meshopt_optimizeVertexCache<unsigned short>(destination, indices, index_count, vertex_count);
}
size_t LLMeshOptimizer::generateRemapMultiU32(
unsigned int* remap,
const U32 * indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count)
{
meshopt_Stream streams[] = {
{(const float*)vertex_positions, sizeof(F32) * 3, sizeof(F32) * 4},
{(const float*)normals, sizeof(F32) * 3, sizeof(F32) * 4},
{(const float*)text_coords, sizeof(F32) * 2, sizeof(F32) * 2},
};
// Remap can function without indices,
// but providing indices helps with removing unused vertices
U64 indeces_cmp = indices ? index_count : vertex_count;
// meshopt_generateVertexRemapMulti will throw an assert if (indices[i] >= vertex_count)
return meshopt_generateVertexRemapMulti(&remap[0], indices, indeces_cmp, vertex_count, streams, sizeof(streams) / sizeof(streams[0]));
}
size_t LLMeshOptimizer::generateRemapMultiU16(
unsigned int* remap,
const U16 * indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count)
{
S32 out_of_range_count = 0;
U32* indices_u32 = NULL;
if (indices)
{
indices_u32 = (U32*)ll_aligned_malloc_32(index_count * sizeof(U32));
for (U64 i = 0; i < index_count; i++)
{
if (indices[i] < vertex_count)
{
indices_u32[i] = (U32)indices[i];
}
else
{
out_of_range_count++;
indices_u32[i] = 0;
}
}
}
if (out_of_range_count)
{
LL_WARNS() << out_of_range_count << " indices are out of range." << LL_ENDL;
}
size_t unique = generateRemapMultiU32(remap, indices_u32, index_count, vertex_positions, normals, text_coords, vertex_count);
ll_aligned_free_32(indices_u32);
return unique;
}
void LLMeshOptimizer::remapIndexBufferU32(U32 * destination_indices,
const U32 * indices,
U64 index_count,
const unsigned int* remap)
{
meshopt_remapIndexBuffer<unsigned int>(destination_indices, indices, index_count, remap);
}
void LLMeshOptimizer::remapIndexBufferU16(U16 * destination_indices,
const U16 * indices,
U64 index_count,
const unsigned int* remap)
{
meshopt_remapIndexBuffer<unsigned short>(destination_indices, indices, index_count, remap);
}
void LLMeshOptimizer::remapPositionsBuffer(LLVector4a * destination_vertices,
const LLVector4a * vertex_positions,
U64 vertex_count,
const unsigned int* remap)
{
meshopt_remapVertexBuffer((float*)destination_vertices, (const float*)vertex_positions, vertex_count, sizeof(LLVector4a), remap);
}
void LLMeshOptimizer::remapNormalsBuffer(LLVector4a * destination_normalss,
const LLVector4a * normals,
U64 mormals_count,
const unsigned int* remap)
{
meshopt_remapVertexBuffer((float*)destination_normalss, (const float*)normals, mormals_count, sizeof(LLVector4a), remap);
}
void LLMeshOptimizer::remapUVBuffer(LLVector2 * destination_uvs,
const LLVector2 * uv_positions,
U64 uv_count,
const unsigned int* remap)
{
meshopt_remapVertexBuffer((float*)destination_uvs, (const float*)uv_positions, uv_count, sizeof(LLVector2), remap);
}
//static
U64 LLMeshOptimizer::simplifyU32(U32 *destination,
const U32 *indices,

View File

@ -28,7 +28,8 @@
#include "linden_common.h"
#include "llmath.h"
class LLVector4a;
class LLVector2;
class LLMeshOptimizer
{
@ -36,13 +37,85 @@ public:
LLMeshOptimizer();
~LLMeshOptimizer();
static void generateShadowIndexBuffer(
static void generateShadowIndexBufferU32(
U32 *destination,
const U32 *indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count);
static void generateShadowIndexBufferU16(
U16 *destination,
const U16 *indices,
U64 index_count,
const LLVector4a *vertex_positions,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count);
static void optimizeVertexCacheU32(
U32 *destination,
const U32 *indices,
U64 index_count,
U64 vertex_count);
static void optimizeVertexCacheU16(
U16 *destination,
const U16 *indices,
U64 index_count,
U64 vertex_count);
// Remap functions
// Welds indentical vertexes together.
// Removes unused vertices if indices were provided.
static size_t generateRemapMultiU32(
unsigned int* remap,
const U32 * indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count);
static size_t generateRemapMultiU16(
unsigned int* remap,
const U16 * indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count);
static void remapIndexBufferU32(U32 * destination_indices,
const U32 * indices,
U64 index_count,
const unsigned int* remap);
static void remapIndexBufferU16(U16 * destination_indices,
const U16 * indices,
U64 index_count,
const unsigned int* remap);
static void remapPositionsBuffer(LLVector4a * destination_vertices,
const LLVector4a * vertex_positions,
U64 vertex_count,
U64 vertex_positions_stride);
const unsigned int* remap);
static void remapNormalsBuffer(LLVector4a * destination_normalss,
const LLVector4a * normals,
U64 mormals_count,
const unsigned int* remap);
static void remapUVBuffer(LLVector2 * destination_uvs,
const LLVector2 * uv_positions,
U64 uv_count,
const unsigned int* remap);
// Simplification
// returns amount of indices in destiantion
// sloppy engages a variant of a mechanizm that does not respect topology as much

View File

@ -196,6 +196,10 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU
LLAvatarNameCache::getInstance()->handleAvNameCacheSuccess(results, httpResults);
}
}
catch (const LLCoros::Stop&)
{
LL_DEBUGS("AvNameCache") << "Received a shutdown exception" << LL_ENDL;
}
catch (...)
{
LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName()

View File

@ -32,7 +32,6 @@
#include "llcoros.h"
#include "llcorehttputil.h"
#include "lluuid.h"
#include <boost/smart_ptr/shared_ptr.hpp>
class LLCoprocedurePool;
@ -84,7 +83,7 @@ public:
private:
typedef boost::shared_ptr<LLCoprocedurePool> poolPtr_t;
typedef std::shared_ptr<LLCoprocedurePool> poolPtr_t;
typedef std::map<std::string, poolPtr_t> poolMap_t;
poolMap_t mPoolMap;

View File

@ -127,13 +127,7 @@ BOOL LLDataPacker::unpackFixed(F32 &value, const char *name,
total_bits++;
}
S32 min_val;
U32 max_val;
if (is_signed)
{
min_val = 1 << int_bits;
min_val *= -1;
}
max_val = 1 << int_bits;
F32 fixed_val;

View File

@ -311,8 +311,9 @@ BOOL LLPartSysData::unpack(LLDataPacker &dp)
std::ostream& operator<<(std::ostream& s, const LLPartSysData &data)
{
s << "Flags: " << std::hex << data.mFlags;
s << " Pattern: " << std::hex << (U32) data.mPattern << "\n";
s << "Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n";
s << "Pattern: " << std::hex << (U32) data.mPattern << "\n";
s << "Source Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n";
s << "Particle Age: " << data.mPartData.mMaxAge << "\n";
s << "Angle: [" << data.mInnerAngle << ", " << data.mOuterAngle << "]\n";
s << "Burst Rate: " << data.mBurstRate << "\n";
s << "Burst Radius: " << data.mBurstRadius << "\n";

View File

@ -374,7 +374,6 @@ BOOL LLThrottleGroup::dynamicAdjust()
}
mDynamicAdjustTime = mt_sec;
S32 total = 0;
// Update historical information
for (i = 0; i < TC_EOF; i++)
{
@ -391,7 +390,6 @@ BOOL LLThrottleGroup::dynamicAdjust()
}
mBitsSentThisPeriod[i] = 0;
total += ll_round(mBitsSentHistory[i]);
}
// Look for busy channels

View File

@ -1386,6 +1386,7 @@ char const* const _PREHASH_RegionAllowAccessBlock = LLMessageStringTable::getIns
char const* const _PREHASH_RegionAllowAccessOverride = LLMessageStringTable::getInstance()->getString("RegionAllowAccessOverride");
char const* const _PREHASH_ParcelEnvironmentBlock = LLMessageStringTable::getInstance()->getString("ParcelEnvironmentBlock");
char const* const _PREHASH_ParcelEnvironmentVersion = LLMessageStringTable::getInstance()->getString("ParcelEnvironmentVersion");
char const* const _PREHASH_ParcelExtendedFlags = LLMessageStringTable::getInstance()->getString("ParcelExtendedFlags");
char const* const _PREHASH_RegionAllowEnvironmentOverride = LLMessageStringTable::getInstance()->getString("RegionAllowEnvironmentOverride");
char const* const _PREHASH_UCoord = LLMessageStringTable::getInstance()->getString("UCoord");
char const* const _PREHASH_VCoord = LLMessageStringTable::getInstance()->getString("VCoord");

View File

@ -1386,6 +1386,7 @@ extern char const* const _PREHASH_RegionAllowAccessBlock;
extern char const* const _PREHASH_RegionAllowAccessOverride;
extern char const* const _PREHASH_ParcelEnvironmentBlock;
extern char const* const _PREHASH_ParcelEnvironmentVersion;
extern char const* const _PREHASH_ParcelExtendedFlags;
extern char const* const _PREHASH_RegionAllowEnvironmentOverride;
extern char const* const _PREHASH_UCoord;
extern char const* const _PREHASH_VCoord;

View File

@ -1549,6 +1549,7 @@ void LLPluginClassMedia::seek(float time)
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME, "seek");
message.setValueReal("time", time);
mCurrentTime = time; // assume that it worked and we will receive an update later
sendMessage(message);
}

View File

@ -999,7 +999,7 @@ void LLPluginProcessParent::poll(F64 timeout)
while (itClean != sInstances.end())
{
if ((*itClean).second->isDone())
sInstances.erase(itClean++);
itClean = sInstances.erase(itClean);
else
++itClean;
}

View File

@ -2577,7 +2577,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
if (!mNoOptimize)
{
ret->optimizeVolumeFaces();
ret->remapVolumeFaces();
}
volume_faces = remainder.size();

View File

@ -107,6 +107,14 @@ void LLModel::offsetMesh( const LLVector3& pivotPoint )
}
}
void LLModel::remapVolumeFaces()
{
for (U32 i = 0; i < getNumVolumeFaces(); ++i)
{
mVolumeFaces[i].remap();
}
}
void LLModel::optimizeVolumeFaces()
{
for (U32 i = 0; i < getNumVolumeFaces(); ++i)
@ -371,6 +379,8 @@ void LLModel::setVolumeFaceData(
U32 num_verts,
U32 num_indices)
{
llassert(num_indices % 3 == 0);
LLVolumeFace& face = mVolumeFaces[f];
face.resizeVertices(num_verts);
@ -835,7 +845,7 @@ LLSD LLModel::writeModel(
{
LLVector3 pos(face.mPositions[j].getF32ptr());
weight_list& weights = high->getJointInfluences(pos);
weight_list& weights = model[idx]->getJointInfluences(pos);
S32 count = 0;
for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter)
@ -881,8 +891,6 @@ LLSD LLModel::writeModel(
LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BOOL as_slm)
{
U32 bytes = 0;
std::string::size_type cur_offset = 0;
LLSD header;
@ -904,7 +912,6 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO
header["skin"]["offset"] = (LLSD::Integer) cur_offset;
header["skin"]["size"] = (LLSD::Integer) size;
cur_offset += size;
bytes += size;
}
}
@ -920,7 +927,6 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO
header["physics_convex"]["offset"] = (LLSD::Integer) cur_offset;
header["physics_convex"]["size"] = (LLSD::Integer) size;
cur_offset += size;
bytes += size;
}
}
@ -942,7 +948,6 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO
header[model_names[i]]["offset"] = (LLSD::Integer) cur_offset;
header[model_names[i]]["size"] = (LLSD::Integer) size;
cur_offset += size;
bytes += size;
}
}
@ -1545,6 +1550,25 @@ void LLMeshSkinInfo::updateHash()
mHash = digest[0];
}
U32 LLMeshSkinInfo::sizeBytes() const
{
U32 res = sizeof(LLUUID); // mMeshID
res += sizeof(std::vector<std::string>) + sizeof(std::string) * mJointNames.size();
for (U32 i = 0; i < mJointNames.size(); ++i)
{
res += mJointNames[i].size(); // actual size, not capacity
}
res += sizeof(std::vector<S32>) + sizeof(S32) * mJointNums.size();
res += sizeof(std::vector<LLMatrix4>) + 16 * sizeof(float) * mInvBindMatrix.size();
res += sizeof(std::vector<LLMatrix4>) + 16 * sizeof(float) * mAlternateBindMatrix.size();
res += 16 * sizeof(float); //mBindShapeMatrix
res += sizeof(float) + 3 * sizeof(bool);
return res;
}
LLModel::Decomposition::Decomposition(LLSD& data)
{
fromLLSD(data);
@ -1651,6 +1675,30 @@ void LLModel::Decomposition::fromLLSD(LLSD& decomp)
}
}
U32 LLModel::Decomposition::sizeBytes() const
{
U32 res = sizeof(LLUUID); // mMeshID
res += sizeof(LLModel::convex_hull_decomposition) + sizeof(std::vector<LLVector3>) * mHull.size();
for (U32 i = 0; i < mHull.size(); ++i)
{
res += mHull[i].size() * sizeof(LLVector3);
}
res += sizeof(LLModel::hull) + sizeof(LLVector3) * mBaseHull.size();
res += sizeof(std::vector<LLModel::PhysicsMesh>) + sizeof(std::vector<LLModel::PhysicsMesh>) * mMesh.size();
for (U32 i = 0; i < mMesh.size(); ++i)
{
res += mMesh[i].sizeBytes();
}
res += sizeof(std::vector<LLModel::PhysicsMesh>) * 2;
res += mBaseHullMesh.sizeBytes() + mPhysicsShapeMesh.sizeBytes();
return res;
}
bool LLModel::Decomposition::hasHullList() const
{
return !mHull.empty() ;

View File

@ -50,6 +50,7 @@ public:
void fromLLSD(LLSD& data);
LLSD asLLSD(bool include_joints, bool lock_scale_if_joint_position) const;
void updateHash();
U32 sizeBytes() const;
LLUUID mMeshID;
std::vector<std::string> mJointNames;
@ -112,6 +113,14 @@ public:
{
return mPositions.empty();
}
U32 sizeBytes() const
{
U32 res = sizeof(std::vector<LLVector3>) * 2;
res += sizeof(LLVector3) * mPositions.size();
res += sizeof(LLVector3) * mNormals.size();
return res;
}
};
class Decomposition
@ -122,6 +131,7 @@ public:
void fromLLSD(LLSD& data);
LLSD asLLSD() const;
bool hasHullList() const;
U32 sizeBytes() const;
void merge(const Decomposition* rhs);
@ -184,6 +194,7 @@ public:
void sortVolumeFacesByMaterialName();
void normalizeVolumeFaces();
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
void remapVolumeFaces();
void optimizeVolumeFaces();
void offsetMesh( const LLVector3& pivotPoint );
void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out);

View File

@ -743,6 +743,14 @@ bool LLGLManager::initGL()
LL_WARNS("RenderInit") << "GL Drivers do not support GL_ARB_multitexture" << LL_ENDL;
return false;
}
if (!mHasFramebufferObject)
{
mHasRequirements = FALSE;
LL_WARNS("RenderInit") << "GL Drivers do not support GL_ARB_framebuffer_object" << LL_ENDL;
return false;
}
stop_glerror();
@ -758,12 +766,6 @@ bool LLGLManager::initGL()
//HACK always disable texture multisample, use FXAA instead
mHasTextureMultisample = FALSE;
#if LL_WINDOWS
if (mIsIntel && mGLVersion <= 3.f)
{ //never try to use framebuffer objects on older intel drivers (crashy)
mHasFramebufferObject = FALSE;
}
#endif
if (mHasFramebufferObject)
{

View File

@ -1248,7 +1248,12 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
if (pixels != nullptr)
{
use_scratch = true;
scratch = new U32[width * height];
scratch = new(std::nothrow) U32[width * height];
if (!scratch)
{
LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
<< " bytes for a manual image W" << width << " H" << height << LL_ENDL;
}
U32 pixel_count = (U32)(width * height);
for (U32 i = 0; i < pixel_count; i++)
@ -1268,7 +1273,12 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
if (pixels != nullptr)
{
use_scratch = true;
scratch = new U32[width * height];
scratch = new(std::nothrow) U32[width * height];
if (!scratch)
{
LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
<< " bytes for a manual image W" << width << " H" << height << LL_ENDL;
}
U32 pixel_count = (U32)(width * height);
for (U32 i = 0; i < pixel_count; i++)
@ -1291,7 +1301,12 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
if (pixels != nullptr)
{
use_scratch = true;
scratch = new U32[width * height];
scratch = new(std::nothrow) U32[width * height];
if (!scratch)
{
LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32))
<< " bytes for a manual image W" << width << " H" << height << LL_ENDL;
}
U32 pixel_count = (U32)(width * height);
for (U32 i = 0; i < pixel_count; i++)
@ -2432,7 +2447,7 @@ void LLImageGLThread::run()
// We must perform setup on this thread before actually servicing our
// WorkQueue, likewise cleanup afterwards.
mWindow->makeContextCurrent(mContext);
gGL.init();
gGL.init(false);
ThreadPool::run();
gGL.shutdown();
mWindow->destroySharedContext(mContext);

View File

@ -869,7 +869,7 @@ LLRender::~LLRender()
shutdown();
}
void LLRender::init()
void LLRender::init(bool needs_vertex_buffer)
{
#if LL_WINDOWS
if (gGLManager.mHasDebugOutput && gDebugGL)
@ -897,15 +897,27 @@ void LLRender::init()
#endif
}
if (needs_vertex_buffer)
{
initVertexBuffer();
}
}
llassert_always(mBuffer.isNull()) ;
stop_glerror();
mBuffer = new LLVertexBuffer(immediate_mask, 0);
mBuffer->allocateBuffer(4096, 0, TRUE);
mBuffer->getVertexStrider(mVerticesp);
mBuffer->getTexCoord0Strider(mTexcoordsp);
mBuffer->getColorStrider(mColorsp);
stop_glerror();
void LLRender::initVertexBuffer()
{
llassert_always(mBuffer.isNull());
stop_glerror();
mBuffer = new LLVertexBuffer(immediate_mask, 0);
mBuffer->allocateBuffer(4096, 0, TRUE);
mBuffer->getVertexStrider(mVerticesp);
mBuffer->getTexCoord0Strider(mTexcoordsp);
mBuffer->getColorStrider(mColorsp);
stop_glerror();
}
void LLRender::resetVertexBuffer()
{
mBuffer = NULL;
}
void LLRender::shutdown()
@ -923,7 +935,7 @@ void LLRender::shutdown()
delete mLightState[i];
}
mLightState.clear();
mBuffer = NULL ;
resetVertexBuffer();
}
void LLRender::refreshState(void)
@ -1625,25 +1637,34 @@ void LLRender::flush()
mCount = 0;
if (mBuffer->useVBOs() && !mBuffer->isLocked())
{ //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata)
mBuffer->getVertexStrider(mVerticesp, 0, count);
mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count);
mBuffer->getColorStrider(mColorsp, 0, count);
}
mBuffer->flush();
mBuffer->setBuffer(immediate_mask);
if (mBuffer)
{
if (mBuffer->useVBOs() && !mBuffer->isLocked())
{ //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata)
mBuffer->getVertexStrider(mVerticesp, 0, count);
mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count);
mBuffer->getColorStrider(mColorsp, 0, count);
}
mBuffer->flush();
mBuffer->setBuffer(immediate_mask);
if (mMode == LLRender::QUADS && sGLCoreProfile)
{
mBuffer->drawArrays(LLRender::TRIANGLES, 0, count);
mQuadCycle = 1;
}
else
{
mBuffer->drawArrays(mMode, 0, count);
}
}
else
{
// mBuffer is present in main thread and not present in an image thread
LL_ERRS() << "A flush call from outside main rendering thread" << LL_ENDL;
}
if (mMode == LLRender::QUADS && sGLCoreProfile)
{
mBuffer->drawArrays(LLRender::TRIANGLES, 0, count);
mQuadCycle = 1;
}
else
{
mBuffer->drawArrays(mMode, 0, count);
}
mVerticesp[0] = mVerticesp[count];
mTexcoordsp[0] = mTexcoordsp[count];

View File

@ -354,7 +354,9 @@ public:
LLRender();
~LLRender();
void init() ;
void init(bool needs_vertex_buffer);
void initVertexBuffer();
void resetVertexBuffer();
void shutdown();
// Refreshes renderer state to the cached values

View File

@ -133,7 +133,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo
mUsage = usage;
mUseDepth = depth;
if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject)
if (sUseFBO || use_fbo)
{
if (depth)
{

View File

@ -665,7 +665,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
if (file == NULL)
{
LL_SHADER_LOADING_WARNS() << "GLSL Shader file not found: " << open_file_name << LL_ENDL;
LL_WARNS("ShaderLoading") << "GLSL Shader file not found: " << open_file_name << LL_ENDL;
return 0;
}

View File

@ -1061,7 +1061,7 @@ void LLVertexBuffer::releaseIndices()
bool LLVertexBuffer::createGLBuffer(U32 size)
{
if (mGLBuffer)
if (mGLBuffer || mMappedData)
{
destroyGLBuffer();
}

View File

@ -266,7 +266,6 @@ public:
bool getTangentStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getTangentStrider(LLStrider<LLVector4a>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getTextureIndexStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getEmissiveStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getWeightStrider(LLStrider<F32>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getWeight4Strider(LLStrider<LLVector4>& strider, S32 index=0, S32 count = -1, bool map_range = false);

View File

@ -102,6 +102,7 @@ LLButton::Params::Params()
scale_image("scale_image", true),
hover_glow_amount("hover_glow_amount"),
commit_on_return("commit_on_return", true),
commit_on_capture_lost("commit_on_capture_lost", false),
display_pressed_state("display_pressed_state", true),
use_draw_context_alpha("use_draw_context_alpha", true),
badge("badge"),
@ -165,6 +166,7 @@ LLButton::LLButton(const LLButton::Params& p)
mBottomVPad(p.pad_bottom),
mHoverGlowStrength(p.hover_glow_amount),
mCommitOnReturn(p.commit_on_return),
mCommitOnCaptureLost(p.commit_on_capture_lost),
mFadeWhenDisabled(FALSE),
mForcePressedState(false),
mDisplayPressedState(p.display_pressed_state),
@ -475,6 +477,10 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
// We only handle the click if the click both started and ended within us
if( hasMouseCapture() )
{
// reset timers before focus change, to not cause
// additional commits if mCommitOnCaptureLost.
resetMouseDownTimer();
// Always release the mouse
gFocusMgr.setMouseCapture( NULL );
@ -489,8 +495,6 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
// Regardless of where mouseup occurs, handle callback
if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
resetMouseDownTimer();
// DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
// If mouseup in the widget, it's been clicked
if (pointInView(x, y))
@ -1195,6 +1199,18 @@ void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignmen
void LLButton::onMouseCaptureLost()
{
if (mCommitOnCaptureLost
&& mMouseDownTimer.getStarted())
{
if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
if (mIsToggle)
{
toggleState();
}
LLUICtrl::onCommit();
}
resetMouseDownTimer();
}

View File

@ -124,6 +124,7 @@ public:
Optional<bool> is_toggle,
scale_image,
commit_on_return,
commit_on_capture_lost,
display_pressed_state;
Optional<F32> hover_glow_amount;
@ -374,6 +375,7 @@ protected:
F32 mCurGlowStrength;
bool mCommitOnReturn;
bool mCommitOnCaptureLost;
bool mFadeWhenDisabled;
bool mForcePressedState;
bool mDisplayPressedState;

View File

@ -259,6 +259,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
mMinHeight(p.min_height),
mHeaderHeight(p.header_height),
mLegacyHeaderHeight(p.legacy_header_height),
mDefaultRectForGroup(true),
mMinimized(FALSE),
mForeground(FALSE),
mFirstLook(TRUE),
@ -761,17 +762,13 @@ void LLFloater::closeFloater(bool app_quitting)
for(handle_set_iter_t dependent_it = mDependents.begin();
dependent_it != mDependents.end(); )
{
LLFloater* floaterp = dependent_it->get();
if (floaterp)
{
++dependent_it;
floaterp->closeFloater(app_quitting);
}
else
{
mDependents.erase(dependent_it++);
}
dependent_it = mDependents.erase(dependent_it);
if (floaterp)
{
floaterp->mDependeeHandle = LLHandle<LLFloater>();
floaterp->closeFloater(app_quitting);
}
}
cleanupHandles();
@ -906,7 +903,10 @@ bool LLFloater::applyRectControl()
if (last_in_group && last_in_group != this)
{
// other floaters in our group, position ourselves relative to them and don't save the rect
mRectControl.clear();
if (mDefaultRectForGroup)
{
mRectControl.clear();
}
mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP;
}
else
@ -1439,7 +1439,7 @@ void LLFloater::cleanupHandles()
LLFloater* floaterp = dependent_it->get();
if (!floaterp)
{
mDependents.erase(dependent_it++);
dependent_it = mDependents.erase(dependent_it);
}
else
{
@ -3481,8 +3481,15 @@ void LLFloater::stackWith(LLFloater& other)
}
next_rect.translate(floater_offset, -floater_offset);
next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, getRect().getWidth(), getRect().getHeight());
const LLRect& rect = getControlGroup()->getRect(mRectControl);
if (rect.notEmpty() && !mDefaultRectForGroup && mResizable)
{
next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
}
else
{
next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, getRect().getWidth(), getRect().getHeight());
}
setShape(next_rect);
if (!other.getHost())

View File

@ -454,6 +454,7 @@ public:
protected:
bool mSaveRect;
bool mDefaultRectForGroup;
std::string mRectControl;
std::string mPosXControl;
std::string mPosYControl;

View File

@ -35,6 +35,7 @@
#include "llui.h"
#include "lluictrlfactory.h"
#include "lluiimage.h"
#include "llwindow.h"
static LLDefaultChildRegistry::Register<LLIconCtrl> r("icon");
@ -42,6 +43,7 @@ LLIconCtrl::Params::Params()
: image("image_name"),
color("color"),
use_draw_context_alpha("use_draw_context_alpha", true),
interactable("interactable", false),
scale_image("scale_image"),
min_width("min_width", 0),
min_height("min_height", 0)
@ -52,6 +54,7 @@ LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p)
mColor(p.color()),
mImagep(p.image),
mUseDrawContextAlpha(p.use_draw_context_alpha),
mInteractable(p.interactable),
mPriority(0),
mMinWidth(p.min_width),
mMinHeight(p.min_height),
@ -81,6 +84,16 @@ void LLIconCtrl::draw()
LLUICtrl::draw();
}
BOOL LLIconCtrl::handleHover(S32 x, S32 y, MASK mask)
{
if (mInteractable && getEnabled())
{
getWindow()->setCursor(UI_CURSOR_HAND);
return TRUE;
}
return LLUICtrl::handleHover(x, y, mask);
}
// virtual
// value might be a string or a UUID
void LLIconCtrl::setValue(const LLSD& value)

View File

@ -48,7 +48,8 @@ public:
{
Optional<LLUIImage*> image;
Optional<LLUIColor> color;
Optional<bool> use_draw_context_alpha;
Optional<bool> use_draw_context_alpha,
interactable;
Optional<S32> min_width,
min_height;
Ignored scale_image;
@ -67,6 +68,9 @@ public:
// llview overrides
virtual void draw();
// llview overrides
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
// lluictrl overrides
virtual void setValue(const LLSD& value );
@ -88,6 +92,7 @@ protected:
// If set to true (default), use the draw context transparency.
// If false, will use transparency returned by getCurrentTransparency(). See STORM-698.
bool mUseDrawContextAlpha;
bool mInteractable;
private:
LLUIColor mColor;

View File

@ -395,7 +395,6 @@ void LLLayoutStack::updateLayout()
space_to_distribute += panelp ? ll_round((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0;
S32 remaining_space = space_to_distribute;
F32 fraction_distributed = 0.f;
if (space_to_distribute > 0 && total_visible_fraction > 0.f)
{ // give space proportionally to visible auto resize panels
BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
@ -404,7 +403,6 @@ void LLLayoutStack::updateLayout()
{
F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction);
S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute);
fraction_distributed += fraction_to_distribute;
panelp->mTargetDim += delta;
remaining_space -= delta;
}

View File

@ -40,6 +40,7 @@ void LLMenuButton::MenuPositions::declareValues()
declare("topleft", MP_TOP_LEFT);
declare("topright", MP_TOP_RIGHT);
declare("bottomleft", MP_BOTTOM_LEFT);
declare("bottomright", MP_BOTTOM_RIGHT);
}
LLMenuButton::Params::Params()
@ -212,6 +213,13 @@ void LLMenuButton::updateMenuOrigin()
mY = rect.mBottom;
break;
}
case MP_BOTTOM_RIGHT:
{
const LLRect& menu_rect = menu->getRect();
mX = rect.mRight - menu_rect.getWidth();
mY = rect.mBottom;
break;
}
}
}

View File

@ -41,7 +41,8 @@ public:
{
MP_TOP_LEFT,
MP_TOP_RIGHT,
MP_BOTTOM_LEFT
MP_BOTTOM_LEFT,
MP_BOTTOM_RIGHT
} EMenuPosition;
struct MenuPositions

View File

@ -3960,8 +3960,8 @@ void LLTearOffMenu::draw()
{
// animate towards target height
reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), (F32)mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f))));
mMenu->needsArrange();
}
mMenu->needsArrange();
LLFloater::draw();
}

View File

@ -100,7 +100,10 @@ void LLModalDialog::onOpen(const LLSD& key)
if (!sModalStack.empty())
{
LLModalDialog* front = sModalStack.front();
front->setVisible(FALSE);
if (front != this)
{
front->setVisible(FALSE);
}
}
// This is a modal dialog. It sucks up all mouse and keyboard operations.
@ -108,7 +111,14 @@ void LLModalDialog::onOpen(const LLSD& key)
LLUI::getInstance()->addPopup(this);
setFocus(TRUE);
sModalStack.push_front( this );
std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
if (iter != sModalStack.end())
{
// if already present, we want to move it to front.
sModalStack.erase(iter);
}
sModalStack.push_front(this);
}
}

View File

@ -69,16 +69,22 @@ void LLProgressBar::draw()
static LLTimer timer;
F32 alpha = getDrawContext().mAlpha;
LLColor4 image_bar_color = mColorBackground.get();
image_bar_color.setAlpha(alpha);
mImageBar->draw(getLocalRect(), image_bar_color);
if (mImageBar) // optional according to parameters
{
LLColor4 image_bar_color = mColorBackground.get();
image_bar_color.setAlpha(alpha);
mImageBar->draw(getLocalRect(), image_bar_color);
}
alpha *= 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32()));
LLColor4 bar_color = mColorBar.get();
bar_color.mV[VALPHA] *= alpha; // modulate alpha
LLRect progress_rect = getLocalRect();
progress_rect.mRight = ll_round(getRect().getWidth() * (mPercentDone / 100.f));
mImageFill->draw(progress_rect, bar_color);
if (mImageFill)
{
alpha *= 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32()));
LLColor4 bar_color = mColorBar.get();
bar_color.mV[VALPHA] *= alpha; // modulate alpha
LLRect progress_rect = getLocalRect();
progress_rect.mRight = ll_round(getRect().getWidth() * (mPercentDone / 100.f));
mImageFill->draw(progress_rect, bar_color);
}
}
void LLProgressBar::setValue(const LLSD& value)

View File

@ -1388,6 +1388,84 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen
return found;
}
U32 LLScrollListCtrl::searchItems(const std::string& substring, bool case_sensitive, bool focus)
{
return searchItems(utf8str_to_wstring(substring), case_sensitive, focus);
}
U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitive, bool focus)
{
U32 found = 0;
LLWString substring_trimmed(substring);
S32 len = substring_trimmed.size();
if (0 == len)
{
// at the moment search for empty element is not supported
return 0;
}
else
{
deselectAllItems(TRUE);
if (!case_sensitive)
{
// do comparisons in lower case
LLWStringUtil::toLower(substring_trimmed);
}
for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
{
LLScrollListItem* item = *iter;
// Only select enabled items with matching names
if (!item->getEnabled())
{
continue;
}
LLScrollListCell* cellp = item->getColumn(getSearchColumn());
if (!cellp)
{
continue;
}
LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
if (!case_sensitive)
{
LLWStringUtil::toLower(item_label);
}
// remove extraneous whitespace from searchable label
LLWStringUtil::trim(item_label);
size_t found_iter = item_label.find(substring_trimmed);
if (found_iter != std::string::npos)
{
// find offset of matching text
cellp->highlightText(found_iter, substring_trimmed.size());
selectItem(item, -1, FALSE);
found++;
if (!mAllowMultipleSelection)
{
break;
}
}
}
}
if (focus && found != 0)
{
mNeedsScroll = true;
}
if (mCommitOnSelectionChange)
{
commitIfChanged();
}
return found;
}
const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const
{
LLScrollListItem* item;
@ -1912,6 +1990,7 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
registrar.add("Url.SendIM", boost::bind(&LLScrollListCtrl::sendIM, id));
registrar.add("Url.AddFriend", boost::bind(&LLScrollListCtrl::addFriend, id));
registrar.add("Url.RemoveFriend", boost::bind(&LLScrollListCtrl::removeFriend, id));
registrar.add("Url.ReportAbuse", boost::bind(&LLScrollListCtrl::reportAbuse, id, is_group));
registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group));
registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group));
registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group));
@ -1975,6 +2054,15 @@ void LLScrollListCtrl::removeFriend(std::string id)
LLUrlAction::removeFriend(slurl);
}
void LLScrollListCtrl::reportAbuse(std::string id, bool is_group)
{
if (!is_group)
{
std::string slurl = "secondlife:///app/agent/" + id + "/about";
LLUrlAction::reportAbuse(slurl);
}
}
void LLScrollListCtrl::showNameDetails(std::string id, bool is_group)
{
// open the resident's details or the group details

View File

@ -267,6 +267,14 @@ public:
const std::string getSelectedItemLabel(S32 column = 0) const;
LLSD getSelectedValue();
// If multi select is on, select all element that include substring,
// otherwise select first match only.
// If focus is true will scroll to selection.
// Returns number of results.
// Note: at the moment search happens in one go and is expensive
U32 searchItems(const std::string& substring, bool case_sensitive = false, bool focus = true);
U32 searchItems(const LLWString& substring, bool case_sensitive = false, bool focus = true);
// DEPRECATED: Use LLSD versions of setCommentText() and getSelectedValue().
// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
// has an associated, unique UUID, and only one of which can be selected at a time.
@ -325,6 +333,7 @@ public:
// support right-click context menus for avatar/group lists
enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP };
void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; }
ContextMenuType getContextMenuType() { return mContextMenuType; }
// Overridden from LLView
/*virtual*/ void draw();
@ -460,6 +469,7 @@ private:
static void sendIM(std::string id);
static void addFriend(std::string id);
static void removeFriend(std::string id);
static void reportAbuse(std::string id, bool is_group);
static void showNameDetails(std::string id, bool is_group);
static void copyNameToClipboard(std::string id, bool is_group);
static void copySLURLToClipboard(std::string id, bool is_group);

View File

@ -103,6 +103,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height);
up_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
up_button_params.commit_on_capture_lost = true;
mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);
addChild(mUpBtn);
@ -111,6 +112,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height);
down_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
down_button_params.commit_on_capture_lost = true;
mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);
addChild(mDownBtn);

View File

@ -402,9 +402,13 @@ void LLTabContainer::draw()
S32 cur_scroll_pos = getScrollPos();
if (cur_scroll_pos > 0)
{
S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1);
if (!mIsVertical)
if (mIsVertical)
{
target_pixel_scroll = cur_scroll_pos * (BTN_HEIGHT + tabcntrv_pad);
}
else
{
S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1);
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
{
if (cur_scroll_pos == 0)
@ -1189,13 +1193,15 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
sendChildToFront(mNextArrowBtn);
sendChildToFront(mJumpPrevArrowBtn);
sendChildToFront(mJumpNextArrowBtn);
updateMaxScrollPos();
if( select )
{
selectLastTab();
mScrollPos = mMaxScrollPos;
}
updateMaxScrollPos();
}
void LLTabContainer::addPlaceholder(LLPanel* child, const std::string& label)
@ -2079,9 +2085,9 @@ void LLTabContainer::updateMaxScrollPos()
if( tab_total_height > available_height )
{
static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad);
S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad) - mNextArrowBtn->getRect().mBottom;
S32 additional_needed = tab_total_height - available_height_with_arrows;
setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT) ) );
setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT + tabcntrv_pad) ) );
no_scroll = FALSE;
}
}

View File

@ -163,6 +163,7 @@ LLTextBase::Params::Params()
font_shadow("font_shadow"),
wrap("wrap"),
trusted_content("trusted_content", true),
always_show_icons("always_show_icons", false),
use_ellipses("use_ellipses", false),
parse_urls("parse_urls", false),
force_urls_external("force_urls_external", false),
@ -212,6 +213,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
mClip(p.clip),
mClipPartial(p.clip_partial && !p.allow_scroll),
mTrustedContent(p.trusted_content),
mAlwaysShowIcons(p.always_show_icons),
mTrackEnd( p.track_end ),
mScrollIndex(-1),
mSelectionStart( 0 ),
@ -448,8 +450,48 @@ void LLTextBase::drawSelectionBackground()
++rect_it)
{
LLRect selection_rect = *rect_it;
selection_rect = *rect_it;
selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom);
if (mScroller)
{
// If scroller is On content_display_rect has correct rect and safe to use as is
// Note: we might need to account for border
selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom);
}
else
{
// If scroller is Off content_display_rect will have rect from document, adjusted to text width, heigh and position
// and we have to acount for offset depending on position
S32 v_delta = 0;
S32 h_delta = 0;
switch (mVAlign)
{
case LLFontGL::TOP:
v_delta = mVisibleTextRect.mTop - content_display_rect.mTop - mVPad;
break;
case LLFontGL::VCENTER:
v_delta = (llmax(mVisibleTextRect.getHeight() - content_display_rect.mTop, -content_display_rect.mBottom) + (mVisibleTextRect.mBottom - content_display_rect.mBottom)) / 2;
break;
case LLFontGL::BOTTOM:
v_delta = mVisibleTextRect.mBottom - content_display_rect.mBottom;
break;
default:
break;
}
switch (mHAlign)
{
case LLFontGL::LEFT:
h_delta = mVisibleTextRect.mLeft - content_display_rect.mLeft + mHPad;
break;
case LLFontGL::HCENTER:
h_delta = (llmax(mVisibleTextRect.getWidth() - content_display_rect.mLeft, -content_display_rect.mRight) + (mVisibleTextRect.mRight - content_display_rect.mRight)) / 2;
break;
case LLFontGL::RIGHT:
h_delta = mVisibleTextRect.mRight - content_display_rect.mRight;
break;
default:
break;
}
selection_rect.translate(h_delta, v_delta);
}
gl_rect_2d(selection_rect, selection_color);
}
}
@ -2007,6 +2049,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
registrar.add("Url.ShowProfile", boost::bind(&LLUrlAction::showProfile, url));
registrar.add("Url.AddFriend", boost::bind(&LLUrlAction::addFriend, url));
registrar.add("Url.RemoveFriend", boost::bind(&LLUrlAction::removeFriend, url));
registrar.add("Url.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, url));
registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, url));
registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url));
registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url));
@ -2116,7 +2159,7 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
LLUrlMatch match;
std::string text = new_text;
while ( LLUrlRegistry::instance().findUrl(text, match,
boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3),isContentTrusted()))
boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3),isContentTrusted() || mAlwaysShowIcons))
{
start = match.getStart();
end = match.getEnd()+1;
@ -2141,7 +2184,7 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
}
// add icon before url if need
LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted());
LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted() || mAlwaysShowIcons);
if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() )
{
setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon"));

View File

@ -321,7 +321,8 @@ public:
parse_highlights,
clip,
clip_partial,
trusted_content;
trusted_content,
always_show_icons;
Optional<S32> v_pad,
h_pad;
@ -369,6 +370,8 @@ public:
virtual void onFocusReceived();
virtual void onFocusLost();
void setParseHTML(bool parse_html) { mParseHTML = parse_html; }
// LLSpellCheckMenuHandler overrides
/*virtual*/ bool getSpellCheck() const;
@ -702,6 +705,8 @@ protected:
bool mAutoIndent;
S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
bool mSkipTripleClick;
bool mAlwaysShowIcons;
bool mSkipLinkUnderline;
// support widgets

View File

@ -196,6 +196,7 @@ public:
const LLUUID& getSourceID() const { return mSourceID; }
const LLTextSegmentPtr getPreviousSegment() const;
const LLTextSegmentPtr getLastSegment() const;
void getSelectedSegments(segment_vec_t& segments) const;
void setShowContextMenu(bool show) { mShowContextMenu = show; }

View File

@ -76,22 +76,6 @@ void LLTextUtil::textboxSetGreyedVal(LLTextBox *txtbox, const LLStyle::Params& n
txtbox->appendText(text.substr(greyed_begin + greyed_len), false, normal_style);
}
const std::string& LLTextUtil::formatPhoneNumber(const std::string& phone_str)
{
static const std::string PHONE_SEPARATOR = LLUI::getInstance()->mSettingGroups["config"]->getString("AvalinePhoneSeparator");
static const S32 PHONE_PART_LEN = 2;
static std::string formatted_phone_str;
formatted_phone_str = phone_str;
S32 separator_pos = (S32)(formatted_phone_str.size()) - PHONE_PART_LEN;
for (; separator_pos >= PHONE_PART_LEN; separator_pos -= PHONE_PART_LEN)
{
formatted_phone_str.insert(separator_pos, PHONE_SEPARATOR);
}
return formatted_phone_str;
}
bool LLTextUtil::processUrlMatch(LLUrlMatch* match,LLTextBase* text_base, bool is_content_trusted)
{
if (match == 0 || text_base == 0)

View File

@ -58,18 +58,6 @@ namespace LLTextUtil
const std::string& text,
const std::string& greyed);
/**
* Formats passed phone number to be more human readable.
*
* It just divides the number on parts by two digits from right to left. The first left part
* can have 2 or 3 digits, i.e. +44-33-33-44-55-66 or 12-34-56-78-90. Separator is set in
* application settings (AvalinePhoneSeparator)
*
* @param[in] phone_str string with original phone number
* @return reference to string with formatted phone number
*/
const std::string& formatPhoneNumber(const std::string& phone_str);
/**
* Adds icon before url if need.
*

View File

@ -222,6 +222,15 @@ void LLUrlAction::removeFriend(std::string url)
}
}
void LLUrlAction::reportAbuse(std::string url)
{
std::string id_str = getUserID(url);
if (LLUUID::validate(id_str))
{
executeSLURL("secondlife:///app/agent/" + id_str + "/reportAbuse");
}
}
void LLUrlAction::blockObject(std::string url)
{
std::string object_id = getObjectId(url);

View File

@ -82,6 +82,7 @@ public:
static void sendIM(std::string url);
static void addFriend(std::string url);
static void removeFriend(std::string url);
static void reportAbuse(std::string url);
static void blockObject(std::string url);
static void unblockObject(std::string url);

View File

@ -197,4 +197,10 @@ if (SDL_FOUND)
endif (SDL_FOUND)
target_link_libraries (llwindow ${llwindow_LINK_LIBRARIES})
if (DARWIN)
include(CMakeFindFrameworks)
find_library(CARBON_LIBRARY Carbon)
target_link_libraries(llwindow ${CARBON_LIBRARY})
endif (DARWIN)

View File

@ -148,6 +148,22 @@ void LLKeyboard::addKeyName(KEY key, const std::string& name)
sNamesToKeys[nameuc] = key;
}
void LLKeyboard::resetKeyDownAndHandle()
{
MASK mask = currentMask(FALSE);
for (S32 i = 0; i < KEY_COUNT; i++)
{
if (mKeyLevel[i])
{
mKeyDown[i] = FALSE;
mKeyLevel[i] = FALSE;
mKeyUp[i] = TRUE;
mCurTranslatedKey = (KEY)i;
mCallbacks->handleTranslatedKeyUp(i, mask);
}
}
}
// BUG this has to be called when an OS dialog is shown, otherwise modifier key state
// is wrong because the keyup event is never received by the main window. JC
void LLKeyboard::resetKeys()

View File

@ -58,7 +58,8 @@ public:
LLKeyboard();
virtual ~LLKeyboard();
void resetKeys();
void resetKeyDownAndHandle();
void resetKeys();
F32 getCurKeyElapsedTime() { return getKeyDown(mCurScanKey) ? getKeyElapsedTime( mCurScanKey ) : 0.f; }

View File

@ -495,14 +495,14 @@ attributedStringInfo getSegments(NSAttributedString *str)
// e.g. OS Window for upload something or Input Window...
// mModifiers instance variable is for insertText: or insertText:replacementRange: (by Pell Smit)
mModifiers = [theEvent modifierFlags];
unichar ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
bool acceptsText = mHasMarkedText ? false : callKeyDown(&eventData, keycode, mModifiers, ch);
bool acceptsText = mHasMarkedText ? false : callKeyDown(&eventData, keycode, mModifiers, [[theEvent characters] characterAtIndex:0]);
unichar ch;
if (acceptsText &&
!mMarkedTextAllowed &&
!(mModifiers & (NSControlKeyMask | NSCommandKeyMask)) && // commands don't invoke InputWindow
![(LLAppDelegate*)[NSApp delegate] romanScript] &&
(ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]) > ' ' &&
ch > ' ' &&
ch != NSDeleteCharacter &&
(ch < 0xF700 || ch > 0xF8FF)) // 0xF700-0xF8FF: reserved for function keys on the keyboard(from NSEvent.h)
{

View File

@ -100,13 +100,13 @@ const unsigned short *copyFromPBoard()
CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// extra retain on the NSCursor since we want it to live for the lifetime of the app.
NSCursor *cursor =
[[[NSCursor alloc]
initWithImage:
[[[NSImage alloc] initWithContentsOfFile:
[NSString stringWithFormat:@"%s", fullpath]
[NSString stringWithUTF8String:fullpath]
]autorelease]
hotSpot:NSMakePoint(hotspotX, hotspotY)
]retain];

View File

@ -621,8 +621,6 @@ void LLWindowMacOSX::getMouseDeltas(float* delta)
BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL enable_vsync)
{
BOOL glNeedsInit = FALSE;
mFullscreen = fullscreen;
if (mWindow == NULL)
@ -637,9 +635,6 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
mGLView = createOpenGLView(mWindow, mFSAASamples, enable_vsync);
mContext = getCGLContextObj(mGLView);
// Since we just created the context, it needs to be set up.
glNeedsInit = TRUE;
gGLManager.mVRAM = getVramSize(mGLView);
}
@ -1666,7 +1661,7 @@ void LLWindowMacOSX::hideCursor()
void LLWindowMacOSX::showCursor()
{
if(mCursorHidden)
if(mCursorHidden || !isCGCursorVisible())
{
// LL_INFOS() << "showCursor: showing" << LL_ENDL;
mCursorHidden = FALSE;
@ -1721,9 +1716,7 @@ void LLSplashScreenMacOSX::updateImpl(const std::string& mesg)
{
if(mWindow != NULL)
{
CFStringRef string = NULL;
string = CFStringCreateWithCString(NULL, mesg.c_str(), kCFStringEncodingUTF8);
CFStringCreateWithCString(NULL, mesg.c_str(), kCFStringEncodingUTF8);
}
}

View File

@ -874,21 +874,20 @@ void LLWindowWin32::close()
// Restore gamma to the system values.
restoreGamma();
if (mhDC)
{
if (!ReleaseDC(mWindowHandle, mhDC))
{
LL_WARNS("Window") << "Release of ghDC failed" << LL_ENDL;
}
mhDC = NULL;
}
LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
mWindowThread->post([=]()
{
if (IsWindow(mWindowHandle))
{
if (mhDC)
{
if (!ReleaseDC(mWindowHandle, mhDC))
{
LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL;
}
}
// Make sure we don't leave a blank toolbar button.
ShowWindow(mWindowHandle, SW_HIDE);
@ -914,6 +913,7 @@ void LLWindowWin32::close()
// Even though the above lambda might not yet have run, we've already
// bound mWindowHandle into it by value, which should suffice for the
// operations we're asking. That's the last time WE should touch it.
mhDC = NULL;
mWindowHandle = NULL;
mWindowThread->close();
}
@ -1506,12 +1506,10 @@ const S32 max_format = (S32)num_formats - 1;
{
wglDeleteContext (mhRC); // Release The Rendering Context
mhRC = 0; // Zero The Rendering Context
}
ReleaseDC (mWindowHandle, mhDC); // Release The Device Context
mhDC = 0; // Zero The Device Context
}
// will release and recreate mhDC, mWindowHandle
recreateWindow(window_rect, dw_ex_style, dw_style);
RECT rect;
@ -1661,7 +1659,8 @@ const S32 max_format = (S32)num_formats - 1;
void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style)
{
auto oldHandle = mWindowHandle;
auto oldWindowHandle = mWindowHandle;
auto oldDCHandle = mhDC;
// zero out mWindowHandle and mhDC before destroying window so window
// thread falls back to peekmessage
@ -1673,7 +1672,8 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
auto window_work =
[this,
self=mWindowThread,
oldHandle,
oldWindowHandle,
oldDCHandle,
// bind CreateWindowEx() parameters by value instead of
// back-referencing LLWindowWin32 members
windowClassName=mWindowClassName,
@ -1689,11 +1689,20 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
self->mWindowHandle = 0;
self->mhDC = 0;
// important to call DestroyWindow() from the window thread
if (oldHandle && !destroy_window_handler(oldHandle))
if (oldWindowHandle)
{
LL_WARNS("Window") << "Failed to properly close window before recreating it!"
<< LL_ENDL;
if (oldDCHandle && !ReleaseDC(oldWindowHandle, oldDCHandle))
{
LL_WARNS("Window") << "Failed to ReleaseDC" << LL_ENDL;
}
// important to call DestroyWindow() from the window thread
if (!destroy_window_handler(oldWindowHandle))
{
LL_WARNS("Window") << "Failed to properly close window before recreating it!"
<< LL_ENDL;
}
}
auto handle = CreateWindowEx(dw_ex_style,
@ -1731,7 +1740,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
};
// But how we pass window_work to the window thread depends on whether we
// already have a window handle.
if (! oldHandle)
if (!oldWindowHandle)
{
// Pass window_work using the WorkQueue: without an existing window
// handle, the window thread can't call GetMessage().
@ -1744,7 +1753,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
// PostMessage(oldHandle) because oldHandle won't be destroyed until
// the window thread has retrieved and executed window_work.
LL_DEBUGS("Window") << "posting window_work to message queue" << LL_ENDL;
mWindowThread->Post(oldHandle, window_work);
mWindowThread->Post(oldWindowHandle, window_work);
}
auto future = promise.get_future();

View File

@ -34,6 +34,7 @@
#include "llplugininstance.h"
#include "llpluginmessage.h"
#include "llpluginmessageclasses.h"
#include "llstring.h"
#include "volume_catcher.h"
#include "media_plugin_base.h"
@ -55,7 +56,7 @@ private:
bool init();
void onPageChangedCallback(const unsigned char* pixels, int x, int y, const int width, const int height);
void onCustomSchemeURLCallback(std::string url);
void onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect);
void onConsoleMessageCallback(std::string message, std::string source, int line);
void onStatusMessageCallback(std::string value);
void onTitleChangeCallback(std::string title);
@ -299,11 +300,18 @@ void MediaPluginCEF::onOpenPopupCallback(std::string url, std::string target)
////////////////////////////////////////////////////////////////////////////////
//
void MediaPluginCEF::onCustomSchemeURLCallback(std::string url)
void MediaPluginCEF::onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect)
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow");
message.setValue("uri", url);
message.setValue("nav_type", "clicked"); // TODO: differentiate between click and navigate to
message.setValue("uri", url);
// indicate if this interaction was from a user click (okay on a SLAPP) or
// via a navigation (e.g. a data URL - see SL-18151) (not okay on a SLAPP)
const std::string nav_type = user_gesture ? "clicked" : "navigated";
message.setValue("nav_type", nav_type);
message.setValueBoolean("is_redirect", is_redirect);
sendMessage(message);
}
@ -592,7 +600,7 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
// event callbacks from Dullahan
mCEFLib->setOnPageChangedCallback(std::bind(&MediaPluginCEF::onPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1));
mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
mCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
mCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1));
mCEFLib->setOnTitleChangeCallback(std::bind(&MediaPluginCEF::onTitleChangeCallback, this, std::placeholders::_1));
@ -616,9 +624,9 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
// dir as the executable that loaded it (SLPlugin.exe). The code in
// Dullahan that tried to figure out the location automatically uses
// the location of the exe which isn't helpful so we tell it explicitly.
char cur_dir_str[MAX_PATH];
GetCurrentDirectoryA(MAX_PATH, cur_dir_str);
settings.host_process_path = std::string(cur_dir_str);
std::vector<wchar_t> buffer(MAX_PATH + 1);
GetCurrentDirectoryW(MAX_PATH, &buffer[0]);
settings.host_process_path = ll_convert_wide_to_string(&buffer[0]);
#endif
settings.accept_language_list = mHostLanguage;

View File

@ -73,6 +73,7 @@ private:
static void display(void* data, void* id);
/*virtual*/ void setDirty(int left, int top, int right, int bottom) /* override, but that is not supported in gcc 4.6 */;
void setDurationDirty();
static void eventCallbacks(const libvlc_event_t* event, void* ptr);
@ -93,8 +94,8 @@ private:
bool mIsLooping;
float mCurTime;
float mDuration;
F64 mCurTime;
F64 mDuration;
EStatus mVlcStatus;
};
@ -213,6 +214,19 @@ void MediaPluginLibVLC::setDirty(int left, int top, int right, int bottom)
sendMessage(message);
}
////////////////////////////////////////////////////////////////////////////////
// *virtual*
void MediaPluginLibVLC::setDurationDirty()
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated");
message.setValueReal("current_time", mCurTime);
message.setValueReal("duration", mDuration);
message.setValueReal("current_rate", 1.0f);
sendMessage(message);
}
////////////////////////////////////////////////////////////////////////////////
//
void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr)
@ -233,6 +247,7 @@ void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr)
parent->mDuration = (float)(libvlc_media_get_duration(parent->mLibVLCMedia)) / 1000.0f;
parent->mVlcStatus = STATUS_PLAYING;
parent->setVolumeVLC();
parent->setDurationDirty();
break;
case libvlc_MediaPlayerPaused:
@ -245,6 +260,8 @@ void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr)
case libvlc_MediaPlayerEndReached:
parent->mVlcStatus = STATUS_DONE;
parent->mCurTime = parent->mDuration;
parent->setDurationDirty();
break;
case libvlc_MediaPlayerEncounteredError:
@ -253,6 +270,11 @@ void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr)
case libvlc_MediaPlayerTimeChanged:
parent->mCurTime = (float)libvlc_media_player_get_time(parent->mLibVLCMediaPlayer) / 1000.0f;
if (parent->mVlcStatus == STATUS_DONE && libvlc_media_player_is_playing(parent->mLibVLCMediaPlayer))
{
parent->mVlcStatus = STATUS_PLAYING;
}
parent->setDurationDirty();
break;
case libvlc_MediaPlayerPositionChanged:
@ -260,6 +282,7 @@ void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr)
case libvlc_MediaPlayerLengthChanged:
parent->mDuration = (float)libvlc_media_get_duration(parent->mLibVLCMedia) / 1000.0f;
parent->setDurationDirty();
break;
case libvlc_MediaPlayerTitleChanged:
@ -562,7 +585,24 @@ void MediaPluginLibVLC::receiveMessage(const char* message_string)
mTextureWidth = texture_width;
mTextureHeight = texture_height;
libvlc_time_t time = 1000.0 * mCurTime;
playMedia();
if (mLibVLCMediaPlayer)
{
libvlc_media_player_set_time(mLibVLCMediaPlayer, time);
time = libvlc_media_player_get_time(mLibVLCMediaPlayer);
if (time < 0)
{
// -1 if there is no media
mCurTime = 0;
}
else
{
mCurTime = (F64)time / 1000.0;
}
}
};
};
@ -594,6 +634,13 @@ void MediaPluginLibVLC::receiveMessage(const char* message_string)
{
if (mLibVLCMediaPlayer)
{
if (mVlcStatus == STATUS_DONE && !libvlc_media_player_is_playing(mLibVLCMediaPlayer))
{
// stop or vlc will ignore 'play', it will just
// make an MediaPlayerEndReached event even if
// seek was used
libvlc_media_player_stop(mLibVLCMediaPlayer);
}
libvlc_media_player_play(mLibVLCMediaPlayer);
}
}
@ -606,15 +653,32 @@ void MediaPluginLibVLC::receiveMessage(const char* message_string)
}
else if (message_name == "seek")
{
if (mDuration > 0)
{
F64 normalized_offset = message_in.getValueReal("time") / mDuration;
libvlc_media_player_set_position(mLibVLCMediaPlayer, normalized_offset);
}
if (mLibVLCMediaPlayer)
{
libvlc_time_t time = 1000.0 * message_in.getValueReal("time");
libvlc_media_player_set_time(mLibVLCMediaPlayer, time);
time = libvlc_media_player_get_time(mLibVLCMediaPlayer);
if (time < 0)
{
// -1 if there is no media
mCurTime = 0;
}
else
{
mCurTime = (F64)time / 1000.0;
}
if (!libvlc_media_player_is_playing(mLibVLCMediaPlayer))
{
// if paused, won't trigger update, update now
setDurationDirty();
}
}
}
else if (message_name == "set_loop")
{
mIsLooping = true;
bool loop = message_in.getValueBoolean("loop");
mIsLooping = loop;
}
else if (message_name == "set_volume")
{

View File

@ -234,12 +234,14 @@ set(viewer_SOURCE_FILES
llfloatercamera.cpp
llfloatercamerapresets.cpp
llfloaterchatvoicevolume.cpp
llfloaterclassified.cpp
llfloatercolorpicker.cpp
llfloaterconversationlog.cpp
llfloaterconversationpreview.cpp
llfloatercreatelandmark.cpp
llfloaterdeleteprefpreset.cpp
llfloaterdestinations.cpp
llfloaterdisplayname.cpp
llfloatereditenvironmentbase.cpp
llfloatereditextdaycycle.cpp
llfloaterenvironmentadjust.cpp
@ -295,9 +297,11 @@ set(viewer_SOURCE_FILES
llfloaterpay.cpp
llfloaterperms.cpp
llfloaterpostprocess.cpp
llfloaterprofile.cpp
llfloaterpreference.cpp
llfloaterpreferenceviewadvanced.cpp
llfloaterpreviewtrash.cpp
llfloaterprofiletexture.cpp
llfloaterproperties.cpp
llfloaterregiondebugconsole.cpp
llfloaterregioninfo.cpp
@ -330,7 +334,6 @@ set(viewer_SOURCE_FILES
llfloatervoiceeffect.cpp
llfloatervoicevolume.cpp
llfloaterwebcontent.cpp
llfloaterwebprofile.cpp
llfloaterwhitelistentry.cpp
llfloaterwindowsize.cpp
llfloaterworldmap.cpp
@ -476,7 +479,6 @@ set(viewer_SOURCE_FILES
llpanelmediasettingsgeneral.cpp
llpanelmediasettingspermissions.cpp
llpanelmediasettingssecurity.cpp
llpanelme.cpp
llpanelnearbymedia.cpp
llpanelobject.cpp
llpanelobjectinventory.cpp
@ -486,8 +488,6 @@ set(viewer_SOURCE_FILES
llpanelpeople.cpp
llpanelpeoplemenus.cpp
llpanelpermissions.cpp
llpanelpick.cpp
llpanelpicks.cpp
llpanelplaceinfo.cpp
llpanelplaceprofile.cpp
llpanelplaces.cpp
@ -496,6 +496,8 @@ set(viewer_SOURCE_FILES
llpanelpresetspulldown.cpp
llpanelprimmediacontrols.cpp
llpanelprofile.cpp
llpanelprofileclassifieds.cpp
llpanelprofilepicks.cpp
llpanelsnapshot.cpp
llpanelsnapshotinventory.cpp
llpanelsnapshotlocal.cpp
@ -657,6 +659,7 @@ set(viewer_SOURCE_FILES
llviewercontrol.cpp
llviewercontrollistener.cpp
llviewerdisplay.cpp
llviewerdisplayname.cpp
llviewerfloaterreg.cpp
llviewerfoldertype.cpp
llviewergenericmessage.cpp
@ -870,12 +873,14 @@ set(viewer_HEADER_FILES
llfloatercamerapresets.h
llfloatercamera.h
llfloaterchatvoicevolume.h
llfloaterclassified.h
llfloatercolorpicker.h
llfloaterconversationlog.h
llfloaterconversationpreview.h
llfloatercreatelandmark.h
llfloaterdeleteprefpreset.h
llfloaterdestinations.h
llfloaterdisplayname.h
llfloatereditenvironmentbase.h
llfloatereditextdaycycle.h
llfloaterenvironmentadjust.h
@ -934,9 +939,11 @@ set(viewer_HEADER_FILES
llfloaterpay.h
llfloaterperms.h
llfloaterpostprocess.h
llfloaterprofile.h
llfloaterpreference.h
llfloaterpreferenceviewadvanced.h
llfloaterpreviewtrash.h
llfloaterprofiletexture.h
llfloaterproperties.h
llfloaterregiondebugconsole.h
llfloaterregioninfo.h
@ -969,7 +976,6 @@ set(viewer_HEADER_FILES
llfloatervoiceeffect.h
llfloatervoicevolume.h
llfloaterwebcontent.h
llfloaterwebprofile.h
llfloaterwhitelistentry.h
llfloaterwindowsize.h
llfloaterworldmap.h
@ -1105,7 +1111,6 @@ set(viewer_HEADER_FILES
llpanelmediasettingsgeneral.h
llpanelmediasettingspermissions.h
llpanelmediasettingssecurity.h
llpanelme.h
llpanelnearbymedia.h
llpanelobject.h
llpanelobjectinventory.h
@ -1115,8 +1120,6 @@ set(viewer_HEADER_FILES
llpanelpeople.h
llpanelpeoplemenus.h
llpanelpermissions.h
llpanelpick.h
llpanelpicks.h
llpanelplaceinfo.h
llpanelplaceprofile.h
llpanelplaces.h
@ -1125,6 +1128,8 @@ set(viewer_HEADER_FILES
llpanelpresetspulldown.h
llpanelprimmediacontrols.h
llpanelprofile.h
llpanelprofileclassifieds.h
llpanelprofilepicks.h
llpanelsnapshot.h
llpanelteleporthistory.h
llpaneltiptoast.h
@ -1287,6 +1292,7 @@ set(viewer_HEADER_FILES
llviewercontrol.h
llviewercontrollistener.h
llviewerdisplay.h
llviewerdisplayname.h
llviewerfloaterreg.h
llviewerfoldertype.h
llviewergenericmessage.h

View File

@ -1 +1 @@
6.6.2
6.6.8

View File

@ -167,10 +167,8 @@
icon="Command_Picks_Icon"
label_ref="Command_Picks_Label"
tooltip_ref="Command_Picks_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
execute_parameters="picks"
is_running_function="Floater.IsOpen"
is_running_parameters="picks"
execute_function="Avatar.TogglePicks"
is_running_function="Avatar.IsPicksTabOpen"
/>
<command name="places"
available_in_toybox="true"

View File

@ -85,7 +85,6 @@
<binding key="DOWN" mask="CTL_ALT_SHIFT" command="pan_down"/>
<binding key="" mask="NONE" mouse="MMB" command="toggle_voice"/>
<binding key="" mask="NONE" mouse="LMB" command="walk_to"/>
<binding key="" mask="NONE" mouse="LMB" command="script_trigger_lbutton"/>
</third_person>

Some files were not shown because too many files have changed in this diff Show More