diff options
| author | Euclid Linden <euclid@lindenlab.com> | 2022-01-28 16:46:58 +0000 | 
|---|---|---|
| committer | Euclid Linden <euclid@lindenlab.com> | 2022-01-28 16:46:58 +0000 | 
| commit | 40fe5277e1390c975d9a3184ff8fc46d69dfb450 (patch) | |
| tree | 5f0432b7f1c3fe6b0b373fdf904bda4f5e58cf5e /indra | |
| parent | af830e5fc5840194be95140f644a27011b9b7e06 (diff) | |
| parent | d28a271fa819c076e2cedb87d9f305468e436b25 (diff) | |
Merged in euclid-16418 (pull request #846)
SL-16418 move media texture updates to background thread
Approved-by: Dave Parks
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llimage/llimage.cpp | 96 | ||||
| -rw-r--r-- | indra/llrender/llimagegl.cpp | 66 | ||||
| -rw-r--r-- | indra/llrender/llimagegl.h | 10 | ||||
| -rw-r--r-- | indra/newview/llviewermedia.cpp | 427 | ||||
| -rw-r--r-- | indra/newview/llviewermedia.h | 37 | ||||
| -rw-r--r-- | indra/newview/llviewertexture.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llviewerwindow.cpp | 6 | 
7 files changed, 430 insertions, 214 deletions
| diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 15b07e5318..ba7ee0b465 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -925,47 +925,73 @@ bool LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,  void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)  { -	llassert( getComponents() <= 4 ); -	// This is fairly bogus, but it'll do for now. -	if (isBufferInvalid()) -	{ -		LL_WARNS() << "Invalid image buffer" << LL_ENDL; -		return; -	} +    LL_PROFILE_ZONE_SCOPED; +    S8 components = getComponents(); +    llassert( components > 0 && components <= 4 ); -	U8 *pos = getData(); -	U32 x, y; -	for (x = 0; x < getWidth(); x++) -	{ -		for (y = 0; y < getHeight(); y++) -		{ -			*pos = r; -			pos++; -			if (getComponents() == 1) -			{ -				continue; -			} -			*pos = g; -			pos++; -			if (getComponents() == 2) -			{ -				continue; -			} -			*pos = b; -			pos++; -			if (getComponents() == 3) -			{ -				continue; -			} -			*pos = a; -			pos++; -		} -	} +    // This is fairly bogus, but it'll do for now. +    if (isBufferInvalid()) +    { +        LL_WARNS() << "Invalid image buffer" << LL_ENDL; +        return; +    } + +    switch (components) +    { +    case 1: +        { +            U8 *dst = getData(); +            S32 count = getWidth() * getHeight(); +            llassert(count == getDataSize()); +            std::fill_n(dst, count, r); +            break; +        } +    case 2: +        { +            U16 *dst = (U16 *)getData(); +            S32 count = getWidth() * getHeight(); +            llassert(count == getDataSize() / 2); +#ifdef LL_LITTLE_ENDIAN +            U16 val = (U16)(r | g << 8); +#else +            U16 val = (U16)(r << 8 | g); +#endif +            std::fill_n(dst, count, val); +            break; +        } +    case 3: +        { +            U8 *dst = getData(); +            S32 count = getWidth() * getHeight(); +            llassert(count == getDataSize() / 3); +            for (S32 i = 0; i < count; i++) +            { +                *dst++ = r; +                *dst++ = g; +                *dst++ = b; +            } +            break; +        } +    case 4: +        { +            U32 *dst = (U32 *)getData(); +            S32 count = getWidth() * getHeight(); +            llassert(count == getDataSize() / 4); +#ifdef LL_LITTLE_ENDIAN +            U32 val = (U32)(r | g << 8 | b << 16 | a << 24); +#else +            U32 val = (U32)(r << 24 | g << 16 | b << 8 | a); +#endif +            std::fill_n(dst, count, val); +            break; +        } +    }  }  // Reverses the order of the rows in the image  void LLImageRaw::verticalFlip()  { +    LL_PROFILE_ZONE_SCOPED;  	S32 row_bytes = getWidth() * getComponents();  	llassert(row_bytes > 0);  	std::vector<U8> line_buffer(row_bytes); diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 1e9b9f642e..4b4f071171 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1215,9 +1215,8 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt          if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE)          { //GL_ALPHA is deprecated, convert to RGBA              use_scratch = true; -            scratch = new U32[width * height]; -              U32 pixel_count = (U32)(width * height); +            scratch = new U32[pixel_count];              for (U32 i = 0; i < pixel_count; i++)              {                  U8* pix = (U8*)&scratch[i]; @@ -1232,9 +1231,8 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt          if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE)          { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA              use_scratch = true; -            scratch = new U32[width * height]; -              U32 pixel_count = (U32)(width * height); +            scratch = new U32[pixel_count];              for (U32 i = 0; i < pixel_count; i++)              {                  U8 lum = ((U8*)pixels)[i * 2 + 0]; @@ -1252,9 +1250,8 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt          if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE)          { //GL_LUMINANCE_ALPHA is deprecated, convert to RGB              use_scratch = true; -            scratch = new U32[width * height]; -              U32 pixel_count = (U32)(width * height); +            scratch = new U32[pixel_count];              for (U32 i = 0; i < pixel_count; i++)              {                  U8 lum = ((U8*)pixels)[i]; @@ -1314,10 +1311,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt      }      stop_glerror(); -    if (use_scratch) -    { -        delete[] scratch; -    } +    if (scratch) delete[] scratch;  }  //create an empty GL texture: just create a texture name @@ -1471,7 +1465,49 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S  	return createGLTexture(discard_level, rawdata, FALSE, usename);  } -BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename) +// Create a new GL name and allocate tex storage (unitialized) for it +// Used to break resource contention stall in background media tex updates +void LLImageGL::allocNewTexName() +{ +    LL_PROFILE_ZONE_SCOPED; +    if (on_main_thread()) return;       // Should only be called on bg update thread +    if (!mGLTextureCreated) return; +    if (0 != mNewTexName) return;       // only 1 rename in flight at a time + +    generateTextures(1, &mNewTexName);  // create new GL name + +    mHasMipMaps = false;                // Media textures aren't mipped +    mMipLevels = 0; +    // Set state to match current +    gGL.getTexUnit(0)->bind(this, false, false, mNewTexName); +    glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0); +    glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel); +    glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_WIDTH, mWidth); +    glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_HEIGHT, mHeight); +    gGL.getTexUnit(0)->setHasMipMaps(mHasMipMaps); +    gGL.getTexUnit(0)->setTextureAddressMode(mAddressMode); +    gGL.getTexUnit(0)->setTextureFilteringOption(mFilterOption); + +    setManualImage(mTarget, 0, mFormatInternal, mWidth, mHeight, mFormatPrimary, mFormatType,  +                   (GLvoid *) nullptr, mAllowCompression); +     +    gGL.getTexUnit(0)->unbind(mBindTarget); + +    mLastBindTime = sLastFrameTime; +} + +// Delete no-longer-needed GL tex name (on main thread)  +void LLImageGL::swapTexName() +{ +    if (mNewTexName) +    { +        deleteTextures(1, &mTexName); +        mTexName = mNewTexName; +        mNewTexName = 0; +    } +} + +BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips /* = FALSE */, S32 usename /* = 0 */)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;      checkActiveThread(); @@ -1715,6 +1751,14 @@ void LLImageGL::destroyGLTexture()  		mTexName = 0;		  		mGLTextureCreated = FALSE ;  	}	 + +    // clean up any in-flight name change +    if (0 != mNewTexName) +    { +        // Memory is transient, not tracked by sGlobalTextuerMemory +        LLImageGL::deleteTextures(1, &mNewTexName); +        mNewTexName = 0; +    }  }  //force to invalidate the gl texture, most likely a sculpty texture diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index d6f4b13a51..f311170823 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -109,15 +109,17 @@ public:  	static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);  	BOOL createGLTexture() ; -	BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, -		S32 category = sMaxCategories-1); +	BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, S32 category = sMaxCategories-1);  	BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0); -	void setImage(const LLImageRaw* imageraw); +    void setImage(const LLImageRaw* imageraw);  	BOOL setImage(const U8* data_in, BOOL data_hasmips = FALSE, S32 usename = 0);  	BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE);  	BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE);  	BOOL setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height); -	 + +    void allocNewTexName(); +    void swapTexName(); +  	// Read back a raw image for this discard level, if it exists  	BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const;  	void destroyGLTexture(); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 63f57e81cc..6c04321539 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -39,6 +39,7 @@  #include "llfilepicker.h"  #include "llfloaterwebcontent.h"	// for handling window close requests and geometry change requests in media browser windows.  #include "llfocusmgr.h" +#include "llimagegl.h"  #include "llkeyboard.h"  #include "lllogininstance.h"  #include "llmarketplacefunctions.h" @@ -78,6 +79,9 @@  #include <boost/bind.hpp>	// for SkinFolder listener  #include <boost/signals2.hpp> +// Statics +bool LLMediaTextureUpdateThread::sEnabled = false; +  class LLMediaFilePicker : public LLFilePickerThread // deletes itself when done  {  public: @@ -635,6 +639,7 @@ static bool proximity_comparitor(const LLViewerMediaImpl* i1, const LLViewerMedi  static LLTrace::BlockTimerStatHandle FTM_MEDIA_UPDATE("Update Media");  static LLTrace::BlockTimerStatHandle FTM_MEDIA_SPARE_IDLE("Spare Idle");  static LLTrace::BlockTimerStatHandle FTM_MEDIA_UPDATE_INTEREST("Update/Interest"); +static LLTrace::BlockTimerStatHandle FTM_MEDIA_UPDATE_VOLUME("Update/Volume");  static LLTrace::BlockTimerStatHandle FTM_MEDIA_SORT("Media Sort");  static LLTrace::BlockTimerStatHandle FTM_MEDIA_SORT2("Media Sort 2");  static LLTrace::BlockTimerStatHandle FTM_MEDIA_MISC("Misc"); @@ -1590,6 +1595,8 @@ LLViewerMediaImpl::LLViewerMediaImpl(	  const LLUUID& texture_id,  		media_tex->setMediaImpl();  	} +    mMainQueue = LL::WorkQueue::getInstance("mainloop"); +    mTexUpdateQueue = LL::WorkQueue::getInstance("LLMediaTextureUpdate"); // Share work queue with tex loader.  }  ////////////////////////////////////////////////////////////////////////////////////////// @@ -1604,6 +1611,25 @@ LLViewerMediaImpl::~LLViewerMediaImpl()  }  ////////////////////////////////////////////////////////////////////////////////////////// +//static  +void LLViewerMediaImpl::initClass(LLWindow* window, bool multi_threaded /* = false */) +{ +    LL_PROFILE_ZONE_SCOPED; +    if (multi_threaded) +    { +        LLMediaTextureUpdateThread::createInstance(window); +    } +} + +////////////////////////////////////////////////////////////////////////////////////////// +//static  +void LLViewerMediaImpl::cleanupClass() +{ +    LL_PROFILE_ZONE_SCOPED; +    LLMediaTextureUpdateThread::deleteSingleton(); +} + +//////////////////////////////////////////////////////////////////////////////////////////  void LLViewerMediaImpl::emitEvent(LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event)  {  	// Broadcast to observers using the superclass version @@ -2087,6 +2113,7 @@ void LLViewerMediaImpl::setMute(bool mute)  //////////////////////////////////////////////////////////////////////////////////////////  void LLViewerMediaImpl::updateVolume()  { +    LL_RECORD_BLOCK_TIME(FTM_MEDIA_UPDATE_VOLUME);  	if(mMediaSource)  	{  		// always scale the volume by the global media volume @@ -2799,199 +2826,252 @@ static LLTrace::BlockTimerStatHandle FTM_MEDIA_SET_SUBIMAGE("Set Subimage");  void LLViewerMediaImpl::update()  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; //LL_RECORD_BLOCK_TIME(FTM_MEDIA_DO_UPDATE); -	if(mMediaSource == NULL) -	{ -		if(mPriority == LLPluginClassMedia::PRIORITY_UNLOADED) -		{ -			// This media source should not be loaded. -		} -		else if(mPriority <= LLPluginClassMedia::PRIORITY_SLIDESHOW) -		{ -			// Don't load new instances that are at PRIORITY_SLIDESHOW or below.  They're just kept around to preserve state. -		} +    if(mMediaSource == NULL) +    { +        if(mPriority == LLPluginClassMedia::PRIORITY_UNLOADED) +        { +            // This media source should not be loaded. +        } +        else if(mPriority <= LLPluginClassMedia::PRIORITY_SLIDESHOW) +        { +            // Don't load new instances that are at PRIORITY_SLIDESHOW or below.  They're just kept around to preserve state. +        }          else if (!mMimeProbe.expired()) -		{ -			// this media source is doing a MIME type probe -- don't try loading it again. -		} -		else -		{ -			// This media may need to be loaded. -			if(sMediaCreateTimer.hasExpired()) -			{ -				LL_DEBUGS("PluginPriority") << this << ": creating media based on timer expiration" << LL_ENDL; -				createMediaSource(); -				sMediaCreateTimer.setTimerExpirySec(LLVIEWERMEDIA_CREATE_DELAY); -			} -			else -			{ -				LL_DEBUGS("PluginPriority") << this << ": NOT creating media (waiting on timer)" << LL_ENDL; -			} -		} -	} -	else -	{ -		updateVolume(); +        { +            // this media source is doing a MIME type probe -- don't try loading it again. +        } +        else +        { +            // This media may need to be loaded. +            if(sMediaCreateTimer.hasExpired()) +            { +                LL_DEBUGS("PluginPriority") << this << ": creating media based on timer expiration" << LL_ENDL; +                createMediaSource(); +                sMediaCreateTimer.setTimerExpirySec(LLVIEWERMEDIA_CREATE_DELAY); +            } +            else +            { +                LL_DEBUGS("PluginPriority") << this << ": NOT creating media (waiting on timer)" << LL_ENDL; +            } +        } +    } +    else +    { +        updateVolume(); -		// TODO: this is updated every frame - is this bad? -		// Removing this as part of the post viewer64 media update -		// Removed as not implemented in CEF embedded browser -		// See MAINT-8194 for a more fuller description -		// updateJavascriptObject(); -	} +        // TODO: this is updated every frame - is this bad? +        // Removing this as part of the post viewer64 media update +        // Removed as not implemented in CEF embedded browser +        // See MAINT-8194 for a more fuller description +        // updateJavascriptObject(); +    } -	if(mMediaSource == NULL) -	{ -		return; -	} +    if(mMediaSource == NULL) +    { +        return; +    } -	// Make sure a navigate doesn't happen during the idle -- it can cause mMediaSource to get destroyed, which can cause a crash. -	setNavigateSuspended(true); +    // Make sure a navigate doesn't happen during the idle -- it can cause mMediaSource to get destroyed, which can cause a crash. +    setNavigateSuspended(true); -	mMediaSource->idle(); +    mMediaSource->idle(); -	setNavigateSuspended(false); +    setNavigateSuspended(false); -	if(mMediaSource == NULL) -	{ -		return; -	} +    if(mMediaSource == NULL) +    { +        return; +    } -	if(mMediaSource->isPluginExited()) -	{ -		resetPreviousMediaState(); -		destroyMediaSource(); -		return; -	} +    if(mMediaSource->isPluginExited()) +    { +        resetPreviousMediaState(); +        destroyMediaSource(); +        return; +    } -	if(!mMediaSource->textureValid()) -	{ -		return; -	} +    if(!mMediaSource->textureValid()) +    { +        return; +    } -	if(mSuspendUpdates || !mVisible) -	{ -		return; -	} +    if(mSuspendUpdates || !mVisible) +    { +        return; +    } -	LLViewerMediaTexture* placeholder_image = updatePlaceholderImage(); -	if(placeholder_image) -	{ -		LLRect dirty_rect; +    // Push update to worker thread +    auto main_queue = LLMediaTextureUpdateThread::sEnabled ? mMainQueue.lock() : nullptr; +    ref();  // protect texture from deletion while active on bg queue +    if (main_queue) +    { +        // replace GL name +        //llassert(!mTextureId.isNull()); +        //LLImageGL* base_image = LLViewerTextureManager::getMediaTexture(mTextureId)->getGLTexture(); +        //GLuint retired_name = base_image->allocNew(); +        main_queue->postTo( +            mTexUpdateQueue, // Worker thread queue +            [this]() // work done on update worker thread +            { +                doMediaTexUpdate(); +            }, +            [this]() // callback to main thread +            { +                endMediaTexUpdate(); +                unref(); +            }); +    } +    else +    { +        doMediaTexUpdate(); // otherwise, update on main thread +        unref(); +    } +} -		// Since we're updating this texture, we know it's playing.  Tell the texture to do its replacement magic so it gets rendered. -		placeholder_image->setPlaying(TRUE); +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::doMediaTexUpdate() +{ +    LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; +    LLViewerMediaTexture* media_tex = updateMediaImage(); -		if(mMediaSource->getDirty(&dirty_rect)) -		{ -			// Constrain the dirty rect to be inside the texture -			S32 x_pos = llmax(dirty_rect.mLeft, 0); -			S32 y_pos = llmax(dirty_rect.mBottom, 0); -			S32 width = llmin(dirty_rect.mRight, placeholder_image->getWidth()) - x_pos; -			S32 height = llmin(dirty_rect.mTop, placeholder_image->getHeight()) - y_pos; +    if (media_tex && mMediaSource) +    { +        llassert(mMediaSource);  +         +        LLRect dirty_rect; +        S32 media_width = mMediaSource->getTextureWidth(); +        S32 media_height = mMediaSource->getTextureHeight(); +        S32 media_depth = mMediaSource->getTextureDepth(); -			if(width > 0 && height > 0) -			{ +        // Since we're updating this texture, we know it's playing.  Tell the texture to do its replacement magic so it gets rendered. +        media_tex->setPlaying(TRUE); -				U8* data = NULL; -				{ -					LL_PROFILE_ZONE_NAMED_CATEGORY_MEDIA("media get data"); //LL_RECORD_BLOCK_TIME(FTM_MEDIA_GET_DATA); -					data = mMediaSource->getBitsData(); -				} +        if (mMediaSource->getDirty(&dirty_rect)) +        { +            // Constrain the dirty rect to be inside the texture +            S32 x_pos = llmax(dirty_rect.mLeft, 0); +            S32 y_pos = llmax(dirty_rect.mBottom, 0); +            S32 width = llmin(dirty_rect.mRight, media_width) - x_pos; +            S32 height = llmin(dirty_rect.mTop, media_height) - y_pos; -				if(data != NULL) -				{ -					// Offset the pixels pointer to match x_pos and y_pos -					data += ( x_pos * mMediaSource->getTextureDepth() * mMediaSource->getBitsWidth() ); -					data += ( y_pos * mMediaSource->getTextureDepth() ); +            if (width > 0 && height > 0) +            { +                U8* data = NULL; +                S32 data_width = mMediaSource->getBitsWidth(); +                S32 data_height = mMediaSource->getBitsHeight(); +                data = mMediaSource->getBitsData(); -					{ -						LL_PROFILE_ZONE_NAMED_CATEGORY_MEDIA("media set subimage"); //LL_RECORD_BLOCK_TIME(FTM_MEDIA_SET_SUBIMAGE); -									placeholder_image->setSubImage( -									data, -									mMediaSource->getBitsWidth(), -									mMediaSource->getBitsHeight(), -									x_pos, -									y_pos, -									width, -									height); -					} -				} +                if (data != NULL) +                { +                    // Offset the pixels pointer to match x_pos and y_pos +                    data += (x_pos * media_depth * data_width); +                    data += (y_pos * media_depth); + +                    { +                        LL_PROFILE_ZONE_NAMED_CATEGORY_MEDIA("media set subimage"); //LL_RECORD_BLOCK_TIME(FTM_MEDIA_SET_SUBIMAGE); +                        media_tex->setSubImage( +                            data, +                            data_width, +                            data_height, +                            x_pos, +                            y_pos, +                            width, +                            height); +                    } +                } -			} +            } -			mMediaSource->resetDirty(); -		} -	} +            mMediaSource->resetDirty(); +        } +    }  } +////////////////////////////////////////////////////////////////////////////////////////// +// runs on main thread, but only called when bg thread updates are active +void LLViewerMediaImpl::endMediaTexUpdate() +{ +    LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; +    LLViewerMediaTexture* base_image = LLViewerTextureManager::findMediaTexture(mTextureId); +    llassert(base_image); + +    LLImageGL* image = base_image->getGLTexture(); +    image->swapTexName(); // retire old GL name +}  //////////////////////////////////////////////////////////////////////////////////////////  void LLViewerMediaImpl::updateImagesMediaStreams()  {  } -  ////////////////////////////////////////////////////////////////////////////////////////// -LLViewerMediaTexture* LLViewerMediaImpl::updatePlaceholderImage() +LLViewerMediaTexture* LLViewerMediaImpl::updateMediaImage()  { -	if(mTextureId.isNull()) -	{ -		// The code that created this instance will read from the plugin's bits. -		return NULL; -	} - -	LLViewerMediaTexture* placeholder_image = LLViewerTextureManager::getMediaTexture( mTextureId ); - -	if (mNeedsNewTexture -		|| placeholder_image->getUseMipMaps() -		|| (placeholder_image->getWidth() != mMediaSource->getTextureWidth()) -		|| (placeholder_image->getHeight() != mMediaSource->getTextureHeight()) -		|| (mTextureUsedWidth != mMediaSource->getWidth()) -		|| (mTextureUsedHeight != mMediaSource->getHeight()) -		) -	{ -		LL_DEBUGS("Media") << "initializing media placeholder" << LL_ENDL; -		LL_DEBUGS("Media") << "movie image id " << mTextureId << LL_ENDL; - -		int texture_width = mMediaSource->getTextureWidth(); -		int texture_height = mMediaSource->getTextureHeight(); -		int texture_depth = mMediaSource->getTextureDepth(); - -		// MEDIAOPT: check to see if size actually changed before doing work -		placeholder_image->destroyGLTexture(); -		// MEDIAOPT: apparently just calling setUseMipMaps(FALSE) doesn't work? -		placeholder_image->reinit(FALSE);	// probably not needed - -		// MEDIAOPT: seems insane that we actually have to make an imageraw then -		// immediately discard it -		LLPointer<LLImageRaw> raw = new LLImageRaw(texture_width, texture_height, texture_depth); -		// Clear the texture to the background color, ignoring alpha. -		// convert background color channels from [0.0, 1.0] to [0, 255]; -		raw->clear(int(mBackgroundColor.mV[VX] * 255.0f), int(mBackgroundColor.mV[VY] * 255.0f), int(mBackgroundColor.mV[VZ] * 255.0f), 0xff); -		int discard_level = 0; - -		// ask media source for correct GL image format constants -		placeholder_image->setExplicitFormat(mMediaSource->getTextureFormatInternal(), -											 mMediaSource->getTextureFormatPrimary(), -											 mMediaSource->getTextureFormatType(), -											 mMediaSource->getTextureFormatSwapBytes()); - -		placeholder_image->createGLTexture(discard_level, raw); - -		// MEDIAOPT: set this dynamically on play/stop -		// FIXME -//		placeholder_image->mIsMediaTexture = true; -		mNeedsNewTexture = false; +    LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; +    if (!mMediaSource) +    { +        return nullptr; // not ready for updating +    } -		// If the amount of the texture being drawn by the media goes down in either width or height, -		// recreate the texture to avoid leaving parts of the old image behind. -		mTextureUsedWidth = mMediaSource->getWidth(); -		mTextureUsedHeight = mMediaSource->getHeight(); -	} +    llassert(!mTextureId.isNull()); +    LLViewerMediaTexture* media_tex = LLViewerTextureManager::getMediaTexture( mTextureId ); +  +    // if on bg thread, create new GL tex image resource to avoid resource contention +    if (!on_main_thread()) +    { +        LLImageGL* image = media_tex->getGLTexture(); +        image->allocNewTexName(); +    } -	return placeholder_image; +    if ( mNeedsNewTexture +        || media_tex->getUseMipMaps() +        || (media_tex->getWidth() != mMediaSource->getTextureWidth()) +        || (media_tex->getHeight() != mMediaSource->getTextureHeight()) +        || (mTextureUsedWidth != mMediaSource->getWidth()) +        || (mTextureUsedHeight != mMediaSource->getHeight()) +        ) +    { +        LL_DEBUGS("Media") << "initializing media placeholder" << LL_ENDL; +        LL_DEBUGS("Media") << "movie image id " << mTextureId << LL_ENDL; + +        int texture_width = mMediaSource->getTextureWidth(); +        int texture_height = mMediaSource->getTextureHeight(); +        int texture_depth = mMediaSource->getTextureDepth(); + +        // MEDIAOPT: check to see if size actually changed before doing work +        media_tex->destroyGLTexture(); +        // MEDIAOPT: apparently just calling setUseMipMaps(FALSE) doesn't work? +        media_tex->reinit(FALSE);	// probably not needed + +        // MEDIAOPT: seems insane that we actually have to make an imageraw then +        // immediately discard it +        LLPointer<LLImageRaw> raw = new LLImageRaw(texture_width, texture_height, texture_depth); +        // Clear the texture to the background color, ignoring alpha. +        // convert background color channels from [0.0, 1.0] to [0, 255]; +        raw->clear(int(mBackgroundColor.mV[VX] * 255.0f), int(mBackgroundColor.mV[VY] * 255.0f), int(mBackgroundColor.mV[VZ] * 255.0f), 0xff); + +        // ask media source for correct GL image format constants +        media_tex->setExplicitFormat(mMediaSource->getTextureFormatInternal(), +            mMediaSource->getTextureFormatPrimary(), +            mMediaSource->getTextureFormatType(), +            mMediaSource->getTextureFormatSwapBytes()); + +        int discard_level = 0; +        media_tex->createGLTexture(discard_level, raw); + +        // MEDIAOPT: set this dynamically on play/stop +        // FIXME +//		media_tex->mIsMediaTexture = true; +        mNeedsNewTexture = false; + +        // If the amount of the texture being drawn by the media goes down in either width or height, +        // recreate the texture to avoid leaving parts of the old image behind. +        mTextureUsedWidth = mMediaSource->getWidth(); +        mTextureUsedHeight = mMediaSource->getHeight(); +    } +    return media_tex;  } @@ -3920,3 +4000,30 @@ bool LLViewerMediaImpl::isObjectInAgentParcel(LLVOVolume *obj)  {  	return (LLViewerParcelMgr::getInstance()->inAgentParcel(obj->getPositionGlobal()));  } + +LLMediaTextureUpdateThread::LLMediaTextureUpdateThread(LLWindow* window) +// We want exactly one thread, of moderate capacity: there are likely only a handful +// of media texture frames in-flight at any one time  +    : ThreadPool("LLMediaTextureUpdate", 1, 4096) +    , mWindow(window) +{ +    LL_PROFILE_ZONE_SCOPED; +    sEnabled = true; +    mFinished = false; + +    mContext = mWindow->createSharedContext(); +    ThreadPool::start(); +} + +void LLMediaTextureUpdateThread::run() +{ +    LL_PROFILE_ZONE_SCOPED; +    // We must perform setup on this thread before actually servicing our +    // WorkQueue, likewise cleanup afterwards. +    mWindow->makeContextCurrent(mContext); +    gGL.init(); +    ThreadPool::run(); +    LLMediaTextureUpdateThread::sEnabled = false; +    gGL.shutdown(); +    mWindow->destroySharedContext(mContext); +} diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 71cec5125d..03a14421ff 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -197,7 +197,10 @@ public:  		U8 media_loop);  	~LLViewerMediaImpl(); -	 + +    static void initClass(LLWindow* window, bool multi_threaded = false); +    static void cleanupClass(); +  	// Override inherited version from LLViewerMediaEventEmitter   	virtual void emitEvent(LLPluginClassMedia* self, LLViewerMediaObserver::EMediaEvent event); @@ -266,6 +269,8 @@ public:  	void scaleTextureCoords(const LLVector2& texture_coords, S32 *x, S32 *y);  	void update(); +    void doMediaTexUpdate(); +    void endMediaTexUpdate();  	void updateImagesMediaStreams();  	LLUUID getMediaTextureID() const; @@ -489,7 +494,35 @@ private:      bool mCanceling;  private: -	LLViewerMediaTexture *updatePlaceholderImage(); +	LLViewerMediaTexture *updateMediaImage(); +    LL::WorkQueue::weak_t mMainQueue; +    LL::WorkQueue::weak_t mTexUpdateQueue; + +}; + +// Define a worker thread pool for media updates (ref LLImageGLThread) +class LLMediaTextureUpdateThread : public LLSimpleton<LLMediaTextureUpdateThread>, LL::ThreadPool +{ +public: +    // follows gSavedSettings "RenderGLMultiThreaded" +    static bool sEnabled; + +    LLMediaTextureUpdateThread(LLWindow* window); + +    // post a function to be executed on the LLMediaTextureUpdateThread background thread +    template <typename CALLABLE> +    bool post(CALLABLE&& func) +    { +        return getQueue().postIfOpen(std::forward<CALLABLE>(func)); +    } + +    void run() override; + +private: +    LLWindow* mWindow; +    void* mContext = nullptr; +    LLAtomicBool mFinished;  }; +  #endif	// LLVIEWERMEDIA_H diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 5fed46f437..ccf4c5bbec 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1629,7 +1629,6 @@ void LLViewerFetchedTexture::scheduleCreateTexture()          mNeedsCreateTexture = TRUE;          if (preCreateTexture())          { -            ref();  #if LL_IMAGEGL_THREAD_CHECK              //grab a copy of the raw image data to make sure it isn't modified pending texture creation              U8* data = mRawImage->getData(); @@ -1643,6 +1642,7 @@ void LLViewerFetchedTexture::scheduleCreateTexture()  #endif              mNeedsCreateTexture = TRUE;              auto mainq = LLImageGLThread::sEnabled ? mMainQueue.lock() : nullptr; +            ref(); // protect texture from deletion while active on bg queue              if (mainq)              {                  mainq->postTo( diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index b9a5e90df0..ba93f4f5f9 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2027,6 +2027,9 @@ LLViewerWindow::LLViewerWindow(const Params& p)  	gTextureList.init();  	LLViewerTextureManager::init() ;  	gBumpImageList.init(); + +    // Init Media texture worker queue +    LLViewerMediaImpl::initClass(mWindow, gSavedSettings.getBOOL("RenderGLMultiThreaded"));  	// Create container for all sub-views  	LLView::Params rvp; @@ -2416,7 +2419,8 @@ void LLViewerWindow::shutdownGL()  	LLViewerTextureManager::cleanup() ;  	SUBSYSTEM_CLEANUP(LLImageGL) ; - +    SUBSYSTEM_CLEANUP(LLViewerMediaImpl); +      	LL_INFOS() << "All textures and llimagegl images are destroyed!" << LL_ENDL ;  	LL_INFOS() << "Cleaning up select manager" << LL_ENDL; | 
