SL-16418 rename media tex image per-update to avoid contention stall

meow-7.2.2
Dave Houlton 2022-01-27 12:57:30 -07:00
parent 7dcca7f180
commit 8d0efb54db
6 changed files with 237 additions and 186 deletions

View File

@ -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 );

View File

@ -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

View File

@ -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();

View File

@ -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.
}
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;
}
// 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();
setNavigateSuspended(false);
if(mMediaSource == NULL)
{
return;
}
if(mMediaSource->isPluginExited())
{
resetPreviousMediaState();
destroyMediaSource();
return;
}
if(!mMediaSource->textureValid())
{
return;
}
if(mSuspendUpdates || !mVisible)
{
return;
}
ref();
if (preUpdateMediaTexture())
LL_RECORD_BLOCK_TIME(FTM_MEDIA_DO_UPDATE);
if(mMediaSource == NULL)
{
// Push update to worker thread
auto main_queue = LLMediaTextureUpdateThread::sEnabled ? mMainQueue.lock() : nullptr;
if (main_queue)
if(mPriority == LLPluginClassMedia::PRIORITY_UNLOADED)
{
main_queue->postTo(
mTexUpdateQueue, // Worker thread queue
[this]() // work done on update worker thread
{
doMediaTexUpdate();
},
[this]() // callback to main thread
{
postUpdateMediaTexture();
//unref();
});
// 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
{
doMediaTexUpdate(); // otherwise, update on main thread
//unref();
// 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;
}
}
}
unref();
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();
}
//////////////////////////////////////////////////////////////////////////////////////////
bool LLViewerMediaImpl::preUpdateMediaTexture()
{
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////
bool LLViewerMediaImpl::postUpdateMediaTexture()
{
return true;
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);
mMediaSource->idle();
setNavigateSuspended(false);
if(mMediaSource == NULL)
{
return;
}
if(mMediaSource->isPluginExited())
{
resetPreviousMediaState();
destroyMediaSource();
return;
}
if(!mMediaSource->textureValid())
{
return;
}
if(mSuspendUpdates || !mVisible)
{
return;
}
// 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,
@ -2997,75 +2989,87 @@ 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;
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();
}
int texture_width = mMediaSource->getTextureWidth();
int texture_height = mMediaSource->getTextureHeight();
int texture_depth = mMediaSource->getTextureDepth();
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;
// 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
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);
}

View File

@ -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:

View File

@ -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(