summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorEuclid Linden <euclid@lindenlab.com>2022-01-28 16:46:58 +0000
committerEuclid Linden <euclid@lindenlab.com>2022-01-28 16:46:58 +0000
commit40fe5277e1390c975d9a3184ff8fc46d69dfb450 (patch)
tree5f0432b7f1c3fe6b0b373fdf904bda4f5e58cf5e /indra
parentaf830e5fc5840194be95140f644a27011b9b7e06 (diff)
parentd28a271fa819c076e2cedb87d9f305468e436b25 (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.cpp96
-rw-r--r--indra/llrender/llimagegl.cpp66
-rw-r--r--indra/llrender/llimagegl.h10
-rw-r--r--indra/newview/llviewermedia.cpp427
-rw-r--r--indra/newview/llviewermedia.h37
-rw-r--r--indra/newview/llviewertexture.cpp2
-rw-r--r--indra/newview/llviewerwindow.cpp6
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;