diff options
author | Dave Houlton <euclid@lindenlab.com> | 2022-01-27 12:57:30 -0700 |
---|---|---|
committer | Dave Houlton <euclid@lindenlab.com> | 2022-01-27 17:09:29 -0700 |
commit | 8d0efb54db96c87a2adb4a824998870ff397846e (patch) | |
tree | 929ccf5fb9d7ce49c3ddd18e179f878cadab4cc5 | |
parent | 7dcca7f180c2204daefbc3648ebe766a46c7cf85 (diff) |
SL-16418 rename media tex image per-update to avoid contention stall
-rw-r--r-- | indra/llimage/llimage.cpp | 2 | ||||
-rw-r--r-- | indra/llrender/llimagegl.cpp | 66 | ||||
-rw-r--r-- | indra/llrender/llimagegl.h | 10 | ||||
-rw-r--r-- | indra/newview/llviewermedia.cpp | 331 | ||||
-rw-r--r-- | indra/newview/llviewermedia.h | 7 | ||||
-rw-r--r-- | indra/newview/llviewertexture.cpp | 1 |
6 files changed, 234 insertions, 183 deletions
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 2855612158..fb02a131fd 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -925,7 +925,7 @@ bool LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height, void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a) { - LL_PROFILE_ZONE_NAMED("djh imageraw clear") + LL_PROFILE_ZONE_SCOPED; S8 components = getComponents(); llassert( components > 0 && components <= 4 ); 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 8515d61f64..de7aaf3173 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" @@ -2824,165 +2825,156 @@ 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. - } + 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. + } 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(); - - // 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; - } + { + // 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(); - // Make sure a navigate doesn't happen during the idle -- it can cause mMediaSource to get destroyed, which can cause a crash. - setNavigateSuspended(true); + // 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(); + } - mMediaSource->idle(); - setNavigateSuspended(false); + 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); - if(mMediaSource->isPluginExited()) - { - resetPreviousMediaState(); - destroyMediaSource(); - return; - } + mMediaSource->idle(); - if(!mMediaSource->textureValid()) - { - return; - } + setNavigateSuspended(false); - if(mSuspendUpdates || !mVisible) - { - return; - } + if(mMediaSource == NULL) + { + return; + } - ref(); + if(mMediaSource->isPluginExited()) + { + resetPreviousMediaState(); + destroyMediaSource(); + return; + } - if (preUpdateMediaTexture()) + if(!mMediaSource->textureValid()) { - // Push update to worker thread - auto main_queue = LLMediaTextureUpdateThread::sEnabled ? mMainQueue.lock() : nullptr; - if (main_queue) - { - main_queue->postTo( - mTexUpdateQueue, // Worker thread queue - [this]() // work done on update worker thread - { - doMediaTexUpdate(); - }, - [this]() // callback to main thread - { - postUpdateMediaTexture(); - //unref(); - }); - } - else - { - doMediaTexUpdate(); // otherwise, update on main thread - //unref(); - } + return; } - unref(); -} + if(mSuspendUpdates || !mVisible) + { + return; + } -////////////////////////////////////////////////////////////////////////////////////////// -bool LLViewerMediaImpl::preUpdateMediaTexture() -{ - return true; -} -////////////////////////////////////////////////////////////////////////////////////////// -bool LLViewerMediaImpl::postUpdateMediaTexture() -{ - return true; + // 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(); + } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::doMediaTexUpdate() { - LLViewerMediaTexture* placeholder_image = updatePlaceholderImage(); - if (placeholder_image) + LLViewerMediaTexture* media_tex = updateMediaImage(); + + if (media_tex && mMediaSource) { + llassert(mMediaSource); + LLRect dirty_rect; + S32 media_width = mMediaSource->getTextureWidth(); + S32 media_height = mMediaSource->getTextureHeight(); + S32 media_depth = mMediaSource->getTextureDepth(); // 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); + media_tex->setPlaying(TRUE); 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; + S32 width = llmin(dirty_rect.mRight, media_width) - x_pos; + S32 height = llmin(dirty_rect.mTop, media_height) - y_pos; if (width > 0 && height > 0) { - U8* data = NULL; - { - LL_RECORD_BLOCK_TIME(FTM_MEDIA_GET_DATA); - data = mMediaSource->getBitsData(); - } + S32 data_width = mMediaSource->getBitsWidth(); + S32 data_height = mMediaSource->getBitsHeight(); + data = mMediaSource->getBitsData(); 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()); + data += (x_pos * media_depth * data_width); + data += (y_pos * media_depth); { LL_RECORD_BLOCK_TIME(FTM_MEDIA_SET_SUBIMAGE); - placeholder_image->setSubImage( + media_tex->setSubImage( data, - mMediaSource->getBitsWidth(), - mMediaSource->getBitsHeight(), + data_width, + data_height, x_pos, y_pos, width, @@ -2998,74 +2990,86 @@ void LLViewerMediaImpl::doMediaTexUpdate() } ////////////////////////////////////////////////////////////////////////////////////////// +// runs on main thread, but only called when bg thread updates are active +void LLViewerMediaImpl::endMediaTexUpdate() +{ + 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; - } + if (!mMediaSource) + { + return nullptr; // not ready for updating + } - 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 + 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(); + } + + 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]; - {LL_PROFILE_ZONE_NAMED("djh clear raw"); 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 - {LL_PROFILE_ZONE_NAMED("djh setformat raw"); - placeholder_image->setExplicitFormat(mMediaSource->getTextureFormatInternal(), + media_tex->setExplicitFormat(mMediaSource->getTextureFormatInternal(), mMediaSource->getTextureFormatPrimary(), mMediaSource->getTextureFormatType(), mMediaSource->getTextureFormatSwapBytes()); - } - { - LL_PROFILE_ZONE_NAMED("djh create ph"); - int discard_level = 0; - placeholder_image->createGLTexture(discard_level, raw); - } - // MEDIAOPT: set this dynamically on play/stop - // FIXME -// placeholder_image->mIsMediaTexture = true; - mNeedsNewTexture = false; + int discard_level = 0; + media_tex->createGLTexture(discard_level, raw); - // 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(); - } + // MEDIAOPT: set this dynamically on play/stop + // FIXME +// media_tex->mIsMediaTexture = true; + mNeedsNewTexture = false; - return placeholder_image; + // 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; } @@ -4017,6 +4021,7 @@ void LLMediaTextureUpdateThread::run() 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 c2e6564166..03a14421ff 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -269,9 +269,8 @@ public: void scaleTextureCoords(const LLVector2& texture_coords, S32 *x, S32 *y); void update(); - bool preUpdateMediaTexture(); void doMediaTexUpdate(); - bool postUpdateMediaTexture(); + void endMediaTexUpdate(); void updateImagesMediaStreams(); LLUUID getMediaTextureID() const; @@ -495,13 +494,13 @@ 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 ( LLImageGLThread) +// Define a worker thread pool for media updates (ref LLImageGLThread) class LLMediaTextureUpdateThread : public LLSimpleton<LLMediaTextureUpdateThread>, LL::ThreadPool { public: diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 5fed46f437..d652b76794 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1643,6 +1643,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( |