diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 1f7815b19..c1f362d32 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -284,13 +284,14 @@ void LLAudioEngine::updateChannels() checkStates(); } -typedef std::pair audio_source_t; struct SourcePriorityComparator { - bool operator() (const audio_source_t& lhs, const audio_source_t& rhs) const + bool operator() (const LLAudioSource* lhs, const LLAudioSource* rhs) const { - //Sort by priority value. If equal, sync masters get higher priority. - return lhs.second < rhs.second || (lhs.second == rhs.second && !lhs.first->isSyncMaster() && rhs.first->isSyncMaster()); + if(rhs->getPriority() != lhs->getPriority()) + return rhs->getPriority() > lhs->getPriority(); + else + return ((rhs->isSyncMaster() && !lhs->isSyncMaster()) || (rhs->isSyncSlave() && (!lhs->isSyncMaster() && !lhs->isSyncSlave()))); } }; @@ -319,10 +320,21 @@ void LLAudioEngine::idle(F32 max_decode_time) LLAudioSource *sync_masterp = NULL; //Highest priority syncmaster LLAudioSource *sync_slavep = NULL; //Highest priority syncslave + //Priority queue that will be filled with soundsources that have a high likeleyhood of being able to start [re]playing this idle tick. + //Sort order: Primary: priority. Secondary: syncmaster. Tertiary: syncslave + std::priority_queue >,SourcePriorityComparator> queue; + + //Have to put syncslaves into a temporary list until the syncmaster state is determined. + //If the syncmaster might be started, or just looped, insert all pending/looping syncslaves into the priority queue. + //If the there is no active syncmaster, then stop any currently looping syncslaves and add none to the priority queue. + //This is necessary as any looping soundsources to be stopped, MUST be stopped before iterating down the priority queue. + static std::vector slave_list; + slave_list.clear(); + //Iterate over all sources. Update their decode or 'done' state, as well as their priorities. - //Also add sources that might be able to start playing to a priority queue. - //Only sources without channels, or are waiting for a syncmaster, should be added to this queue. - std::priority_queue >,SourcePriorityComparator> queue; + //Also add sources that might be able to start playing to the priority queue. + //Only sources without channels should be added to priority queue. + //Syncslaves must put into the slave list instead of the priority queue. for (source_map::iterator iter = mAllSources.begin(); iter != mAllSources.end();) { LLAudioSource *sourcep = iter->second; @@ -360,34 +372,32 @@ void LLAudioEngine::idle(F32 max_decode_time) { if(!sync_masterp || sourcep->getPriority() > sync_masterp->getPriority()) { + if(sync_masterp && !sync_masterp->getChannel()) + queue.push(sync_masterp); //Add lower-priority soundmaster to the queue as a normal sound. sync_masterp = sourcep; + //Don't add master to the queue yet. + //Add it after highest-priority sound slave is found so we can outrank its priority. + continue; } - //This is a hack. Don't add master to the queue yet. - //Add it after highest-priority sound slave is found so we can outrank its priority. - continue; + //Else fall through like a normal sound. } else if(sourcep->isSyncSlave()) { - //Only care about syncslaves without channel, or are looping. - if(!sourcep->getChannel() || sourcep->isLoop() ) + if(!sync_slavep || sourcep->getPriority() > sync_slavep->getPriority()) { - if(!sync_slavep || sourcep->getPriority() > sync_slavep->getPriority()) - { - sync_slavep = sourcep; - } - } - else - { - continue; + sync_slavep = sourcep; } + //Don't add to the priority queue quite yet. Best primary syncmaster candidate may not have been determined yet. + slave_list.push_back(sourcep); + continue; } - else if(sourcep->getChannel()) + if(sourcep->getChannel()) { //If this is just a regular sound and is currently playing then do nothing special. continue; } //Add to our queue. Highest priority will be at the front. - queue.push(std::make_pair(sourcep,sourcep->getPriority())); + queue.push(sourcep); } // Do this BEFORE we update the channels @@ -395,47 +405,71 @@ void LLAudioEngine::idle(F32 max_decode_time) // such as changing what sound was playing. updateChannels(); - //Syncmaster must have priority greater than or equal to priority of highest priority syncslave. - if(sync_masterp && !sync_masterp->getChannel()) + //Loop over all syncsaves. If no syncmaster, stop looping ones, else add ones that need [re]started to the priority queue. + //Normally there are zero to one syncslaves, so this loop isn't typically expensive. + for(std::vector::iterator iter=slave_list.begin();iter!=slave_list.end();++iter) { + if(!sync_masterp) + { + //Stop any looping syncslaves. I'm not sure this behavior is correct, but letting looping syncslaves run + //off on their own seems wrong. The lsl documentation is unclear. + if((*iter)->getChannel() && (*iter)->isLoop()) + { + (*iter)->getChannel()->cleanup(); + } + } + //If the syncmaster already has a channel and hasn't looped, then we can skip syncslaves this idle tick (there's nothing to do). + else if((!sync_masterp->getChannel() || sync_masterp->getChannel()->mLoopedThisFrame)) + { + //Add syncslaves that we might want to [re]start. + if(!(*iter)->getChannel() || (*iter)->isLoop()) + queue.push((*iter)); + } + } + + if(sync_masterp) + { + //Syncmaster must have priority greater than or equal to priority of highest priority syncslave. //The case of slave priority equaling master priority is handled in the comparator (SourcePriorityComparator). - //Could have added an arbiturary small value to the priority, but the extra logic in the comparator is more correct. if(sync_slavep) - queue.push(std::make_pair(sync_masterp, llmax(sync_slavep->getPriority(), sync_masterp->getPriority()))); - else - queue.push(std::make_pair(sync_masterp, sync_masterp->getPriority())); + sync_masterp->setPriority(sync_slavep->getPriority()); + if(!sync_masterp->getChannel()) + { + queue.push(sync_masterp); + } } // Dispatch all soundsources. - bool syncmaster_started = false; + bool syncmaster_started = sync_masterp && sync_masterp->getChannel() && sync_masterp->getChannel()->mLoopedThisFrame; while(!queue.empty()) { - // This is lame, instead of this I could actually iterate through all the sources - // attached to each channel, since only those with active channels - // can have anything interesting happen with their queue? (Maybe not true) - LLAudioSource *sourcep = queue.top().first; + LLAudioSource *sourcep = queue.top(); queue.pop(); - if (sourcep->isSyncSlave()) + //If the syncmaster hasn't just started playing, or hasn't just looped then skip this soundslave, + //as there's nothing to do with it yet. + if (sourcep->isSyncSlave() && !syncmaster_started) { - //If the syncmaster hasn't just started playing, or hasn't just looped then skip this soundslave. - if(!sync_masterp || (!syncmaster_started && !(sync_masterp->getChannel() && sync_masterp->getChannel()->mLoopedThisFrame))) - continue; + continue; } LLAudioChannel *channelp = sourcep->getChannel(); if (!channelp) { - if(!(channelp = gAudiop->getFreeChannel(sourcep->getPriority()))) - continue; //No more channels. Don't abort the whole loop, however. Soundslaves might want to start up! - - //If this is the syncmaster that just started then we need to update non-playing syncslaves. - if(sourcep == sync_masterp) + //No more channels. We can break here, as in theory, any lower priority sounds should have had their + //channel already stolen. There should be nothing playing, nor playable, when iterating beyond this point. + if(!(channelp = getFreeChannel(sourcep->getPriority()))) { - syncmaster_started = true; + break; } - channelp->setSource(sourcep); //setSource calls updateBuffer and update3DPosition, and resets the source mAgeTimer + + //If this is the primary syncmaster that we just started, then [re]start syncslaves further down in the priority queue. + //Due to sorting and priority fudgery, the syncmaster is ALWAYS before any syncslaves in this loop. + syncmaster_started |= (sourcep == sync_masterp); + + //setSource calls updateBuffer and update3DPosition, and resets the source mAgeTimer + channelp->setSource(sourcep); } if(sourcep->isSyncSlave()) @@ -594,7 +628,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) } } - if (min_priority > priority || !min_channelp) + if (!min_channelp || min_priority >= priority) { // All playing channels have higher priority, return. return NULL; @@ -1411,11 +1445,11 @@ void LLAudioSource::updatePriority() { if (isAmbient()) { - mPriority = 1.f; + setPriority(1.f); } else if (isMuted()) { - mPriority = 0.f; + setPriority(0.f); } else { @@ -1425,7 +1459,7 @@ void LLAudioSource::updatePriority() dist_vec -= gAudiop->getListenerPos(); F32 dist_squared = llmax(1.f, dist_vec.magVecSquared()); - mPriority = mGain / dist_squared; + setPriority(mGain / dist_squared); } } diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index d1e23de96..4067f23a7 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -316,6 +316,7 @@ public: LLVector3d getPositionGlobal() const { return mPositionGlobal; } LLVector3 getVelocity() const { return mVelocity; } F32 getPriority() const { return mPriority; } + void setPriority(F32 priority) { mPriority = priority; } // Gain should always be clamped between 0 and 1. F32 getGain() const { return mGain; } diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp index 116a37314..01beaa9d5 100644 --- a/indra/llaudio/llaudioengine_fmod.cpp +++ b/indra/llaudio/llaudioengine_fmod.cpp @@ -590,6 +590,7 @@ void LLAudioChannelFMOD::cleanup() } mChannelID = 0; + mLastSamplePos = 0; } diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 506c8d4ad..300264d91 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -810,6 +810,7 @@ void LLAudioChannelFMODEX::cleanup() Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); mChannelp = NULL; + mLastSamplePos = 0; } diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index 27596c107..b511b9d28 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -338,6 +338,7 @@ void LLAudioBufferOpenAL::cleanup() LL_WARNS("OpenAL") << "Error: " << alutGetErrorString( error ) << " when cleaning up a buffer" << LL_ENDL; } mALBuffer = AL_NONE; + mLastSamplePos = 0; } } diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 977245b6e..9c8af6888 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -51,7 +51,7 @@ void check_framebuffer_status() } bool LLRenderTarget::sUseFBO = false; -U32 LLRenderTarget::sCurFBO = 0; +LLRenderTarget* LLRenderTarget::sCurFBO = 0; LLRenderTarget::LLRenderTarget() : mResX(0), @@ -161,7 +161,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0); stop_glerror(); } - glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO ? sCurFBO->getFBO() : 0); } stop_glerror(); @@ -248,7 +248,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) check_framebuffer_status(); - glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO ? sCurFBO->getFBO() : 0); } mTex.push_back(tex); @@ -343,7 +343,7 @@ void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target) check_framebuffer_status(); - glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO ? sCurFBO->getFBO() : 0); target.mUseDepth = true; } @@ -365,6 +365,7 @@ void LLRenderTarget::release() { glBindFramebuffer(GL_FRAMEBUFFER, mFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER,0); } LLImageGL::deleteTextures(mUsage, 0, 0, 1, &mDepth, true); stop_glerror(); @@ -386,6 +387,7 @@ void LLRenderTarget::release() { //attached as a texture glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), 0, 0); } + glBindFramebuffer(GL_FRAMEBUFFER,0); mUseDepth = false; } @@ -414,17 +416,18 @@ void LLRenderTarget::bindTarget() if (mFBO) { stop_glerror(); + mPreviousFBO = sCurFBO; if (mSampleBuffer) { mSampleBuffer->bindTarget(this); - sCurFBO = mSampleBuffer->mFBO; + sCurFBO = mSampleBuffer; stop_glerror(); } else { glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - sCurFBO = mFBO; + sCurFBO = this; stop_glerror(); if (gGLManager.mHasDrawBuffers) @@ -577,8 +580,18 @@ void LLRenderTarget::flush(bool fetch_depth) } glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } - glBindFramebuffer(GL_FRAMEBUFFER, mPreviousFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mPreviousFBO ? mPreviousFBO->getFBO() : 0); sCurFBO = mPreviousFBO; + + if(mPreviousFBO) + { + glViewport(0, 0, mPreviousFBO->mResX, mPreviousFBO->mResY); + mPreviousFBO = NULL; + } + else + { + glViewport(gGLViewport[0],gGLViewport[1],gGLViewport[2],gGLViewport[3]); + } stop_glerror(); } } @@ -613,7 +626,7 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, stop_glerror(); glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1); stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO ? sCurFBO->getFBO() : 0); stop_glerror(); } else @@ -636,7 +649,7 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, stop_glerror(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO ? sCurFBO->getFBO() : 0); stop_glerror(); } } @@ -670,7 +683,7 @@ void LLRenderTarget::copyContentsToFramebuffer(LLRenderTarget& source, S32 srcX0 if(mask) glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO ? sCurFBO->getFBO() : 0); stop_glerror(); } } @@ -819,7 +832,7 @@ bool LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth } stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO ? sCurFBO->getFBO() : 0); stop_glerror(); @@ -898,7 +911,7 @@ bool LLMultisampleBuffer::addColorAttachment(U32 color_fmt) glBindFramebuffer(GL_FRAMEBUFFER, mFBO); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset, GL_RENDERBUFFER, tex); check_framebuffer_status(); - glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO ? sCurFBO->getFBO() : 0); } mTex.push_back(tex); diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index c2696aae6..f2401eed4 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -65,7 +65,7 @@ public: //whether or not to use FBO implementation static bool sUseFBO; static U32 sBytesAllocated; - static U32 sCurFBO; + static LLRenderTarget* sCurFBO; LLRenderTarget(); virtual ~LLRenderTarget(); @@ -154,7 +154,7 @@ protected: std::vector mTex; std::vector mInternalFormat; U32 mFBO; - U32 mPreviousFBO; + LLRenderTarget* mPreviousFBO; U32 mDepth; bool mStencil; bool mUseDepth;