From 892f7d98d5da2c329472a7075a88e010277b86b0 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 17 Feb 2022 20:55:32 +0200 Subject: [PATCH 01/11] SL-16861 Animesh and avatar color change fix --- indra/newview/llvovolume.cpp | 7 +++++-- indra/newview/llvovolume.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index a60bece037..49be0765a6 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -225,6 +225,7 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re mNumFaces = 0; mLODChanged = FALSE; mSculptChanged = FALSE; + mColorChanged = FALSE; mSpotLightPriority = 0.f; mMediaImplList.resize(getNumTEs()); @@ -2031,7 +2032,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) was_regen_faces = lodOrSculptChanged(drawable, compiled); drawable->setState(LLDrawable::REBUILD_VOLUME); } - else if (mSculptChanged || mLODChanged) + else if (mSculptChanged || mLODChanged || mColorChanged) { compiled = TRUE; was_regen_faces = lodOrSculptChanged(drawable, compiled); @@ -2043,7 +2044,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) genBBoxes(FALSE); } - else if (mLODChanged || mSculptChanged) + else if (mLODChanged || mSculptChanged || mColorChanged) { dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1)); compiled = TRUE; @@ -2075,6 +2076,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) mLODChanged = FALSE; mSculptChanged = FALSE; mFaceMappingChanged = FALSE; + mColorChanged = FALSE; return LLViewerObject::updateGeometry(drawable); } @@ -2213,6 +2215,7 @@ S32 LLVOVolume::setTEColor(const U8 te, const LLColor4& color) if (mDrawable.notNull() && retval) { // These should only happen on updates which are not the initial update. + mColorChanged = TRUE; mDrawable->setState(LLDrawable::REBUILD_COLOR); dirtyMesh(); } diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index d158044f5d..4cb7a5481c 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -413,6 +413,7 @@ private: S32 mLOD; BOOL mLODChanged; BOOL mSculptChanged; + BOOL mColorChanged; F32 mSpotLightPriority; LLMatrix4 mRelativeXform; LLMatrix3 mRelativeXformInvTrans; From 0d6aa3c0fe184ae00899304cb3f71315f5c73314 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Thu, 17 Feb 2022 22:52:23 +0000 Subject: [PATCH 02/11] SL-16815 Remove frame stalls from occlusion queries, bumpmap updates, and querying for available video memory. --- indra/llrender/llgl.cpp | 90 +------------ indra/llrender/llgl.h | 45 ------- indra/llrender/llgltexture.cpp | 14 +-- indra/llrender/llgltexture.h | 7 +- indra/llrender/llimagegl.cpp | 119 +++++++++++++----- indra/llrender/llimagegl.h | 17 ++- indra/llwindow/llwindowwin32.cpp | 1 + indra/newview/lldrawpoolbump.cpp | 203 +++++++++++++++++++----------- indra/newview/lldrawpoolbump.h | 2 + indra/newview/llviewerdisplay.cpp | 3 - indra/newview/llviewermedia.cpp | 7 +- indra/newview/llvieweroctree.cpp | 97 +++++++++----- indra/newview/llviewertexture.cpp | 24 ++-- indra/newview/pipeline.cpp | 5 - 14 files changed, 318 insertions(+), 316 deletions(-) diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 50a5972d28..639d1fba32 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -2150,95 +2150,6 @@ LLGLUserClipPlane::~LLGLUserClipPlane() disable(); } -LLGLNamePool::LLGLNamePool() -{ -} - -LLGLNamePool::~LLGLNamePool() -{ -} - -void LLGLNamePool::upkeep() -{ - std::sort(mNameList.begin(), mNameList.end(), CompareUsed()); -} - -void LLGLNamePool::cleanup() -{ - for (name_list_t::iterator iter = mNameList.begin(); iter != mNameList.end(); ++iter) - { - releaseName(iter->name); - } - - mNameList.clear(); -} - -GLuint LLGLNamePool::allocate() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; -#if LL_GL_NAME_POOLING - for (name_list_t::iterator iter = mNameList.begin(); iter != mNameList.end(); ++iter) - { - if (!iter->used) - { - iter->used = TRUE; - return iter->name; - } - } - - NameEntry entry; - entry.name = allocateName(); - entry.used = TRUE; - mNameList.push_back(entry); - - return entry.name; -#else - return allocateName(); -#endif -} - -void LLGLNamePool::release(GLuint name) -{ -#if LL_GL_NAME_POOLING - for (name_list_t::iterator iter = mNameList.begin(); iter != mNameList.end(); ++iter) - { - if (iter->name == name) - { - if (iter->used) - { - iter->used = FALSE; - return; - } - else - { - LL_ERRS() << "Attempted to release a pooled name that is not in use!" << LL_ENDL; - } - } - } - LL_ERRS() << "Attempted to release a non pooled name!" << LL_ENDL; -#else - releaseName(name); -#endif -} - -//static -void LLGLNamePool::upkeepPools() -{ - for (auto& pool : instance_snapshot()) - { - pool.upkeep(); - } -} - -//static -void LLGLNamePool::cleanupPools() -{ - for (auto& pool : instance_snapshot()) - { - pool.cleanup(); - } -} - LLGLDepthTest::LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled, GLenum depth_func) : mPrevDepthEnabled(sDepthEnabled), mPrevDepthFunc(sDepthFunc), mPrevWriteEnabled(sWriteEnabled) { @@ -2460,3 +2371,4 @@ extern "C" } #endif + diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index ec97eb0faa..52338364e6 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -366,51 +366,6 @@ public: ~LLGLSquashToFarClip(); }; -/* - Generic pooling scheme for things which use GL names (used for occlusion queries and vertex buffer objects). - Prevents thrashing of GL name caches by avoiding calls to glGenFoo and glDeleteFoo. -*/ -class LLGLNamePool : public LLInstanceTracker -{ -public: - typedef LLInstanceTracker tracker_t; - - struct NameEntry - { - GLuint name; - BOOL used; - }; - - struct CompareUsed - { - bool operator()(const NameEntry& lhs, const NameEntry& rhs) - { - return lhs.used < rhs.used; //FALSE entries first - } - }; - - typedef std::vector name_list_t; - name_list_t mNameList; - - LLGLNamePool(); - virtual ~LLGLNamePool(); - - void upkeep(); - void cleanup(); - - GLuint allocate(); - void release(GLuint name); - - static void upkeepPools(); - static void cleanupPools(); - -protected: - typedef std::vector pool_list_t; - - virtual GLuint allocateName() = 0; - virtual void releaseName(GLuint name) = 0; -}; - /* Interface for objects that need periodic GL updates applied to them. Used to synchronize GL updates with GL thread. diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index c7de8bc338..e012eb9a62 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -164,11 +164,11 @@ BOOL LLGLTexture::createGLTexture() return mGLTexturep->createGLTexture() ; } -BOOL LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename, BOOL to_create, S32 category, bool defer_copy) +BOOL LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename, BOOL to_create, S32 category, bool defer_copy, LLGLuint* tex_name) { - llassert(mGLTexturep.notNull()) ; + llassert(mGLTexturep.notNull()); - BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category, defer_copy) ; + BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category, defer_copy, tex_name) ; if(ret) { @@ -260,20 +260,20 @@ LLTexUnit::eTextureType LLGLTexture::getTarget(void) const return mGLTexturep->getTarget() ; } -BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, bool use_new_name /* = false */) +BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; llassert(mGLTexturep.notNull()) ; - return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height, 0, use_new_name) ; + return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height, 0, use_name) ; } -BOOL LLGLTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, bool use_new_name /* = false */) +BOOL LLGLTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; llassert(mGLTexturep.notNull()) ; - return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height, 0, use_new_name) ; + return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height, 0, use_name) ; } void LLGLTexture::setGLTextureCreated (bool initialized) diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index 216e7f640d..8cfe7b62de 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -132,13 +132,14 @@ public: // to_create - set to FALSE to force gl texture to not be created // category - LLGLTexture category for this LLGLTexture // defer_copy - set to true to allocate GL texture but NOT initialize with imageraw data - BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, S32 category = LLGLTexture::OTHER, bool defer_copy = false); + // tex_name - if not null, will be set to the GL name of the texture created + BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, S32 category = LLGLTexture::OTHER, bool defer_copy = false, LLGLuint* tex_name = nullptr); void setFilteringOption(LLTexUnit::eTextureFilterOptions option); void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, BOOL swap_bytes = FALSE); void setAddressMode(LLTexUnit::eTextureAddressMode mode); - BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, bool use_new_name = false); - BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, bool use_new_name = false); + BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name = 0); + BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, LLGLuint use_name = 0); void setGLTextureCreated (bool initialized); void setCategory(S32 category) ; void setTexName(LLGLuint); // for forcing w/ externally created textures only diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 694c4a7b06..110074bdc0 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -40,6 +40,7 @@ #include "llglslshader.h" #include "llrender.h" #include "llwindow.h" +#include "llframetimer.h" #if !LL_IMAGEGL_THREAD_CHECK #define checkActiveThread() @@ -1075,14 +1076,15 @@ void LLImageGL::postAddToAtlas() stop_glerror(); } -BOOL LLImageGL::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 use_new_name /* = false */) +BOOL LLImageGL::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 */, LLGLuint use_name) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; if (!width || !height) { return TRUE; } - if (0 == (use_new_name ? mNewTexName : mTexName)) + LLGLuint tex_name = use_name != 0 ? use_name : mTexName; + if (0 == tex_name) { // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18 //LL_WARNS() << "Setting subimage on image without GL texture" << LL_ENDL; @@ -1098,7 +1100,7 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 // HACK: allow the caller to explicitly force the fast path (i.e. using glTexSubImage2D here instead of calling setImage) even when updating the full texture. if (!force_fast_update && x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height) { - setImage(datap, FALSE, use_new_name ? mNewTexName : mTexName); + setImage(datap, FALSE, tex_name); } else { @@ -1150,7 +1152,7 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 datap += (y_pos * data_width + x_pos) * getComponents(); // Update the GL texture - BOOL res = gGL.getTexUnit(0)->bindManual(mBindTarget, use_new_name ? mNewTexName : mTexName); + BOOL res = gGL.getTexUnit(0)->bindManual(mBindTarget, tex_name); if (!res) LL_ERRS() << "LLImageGL::setSubImage(): bindTexture failed" << LL_ENDL; stop_glerror(); @@ -1171,10 +1173,10 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 return TRUE; } -BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update /* = FALSE */, bool use_new_name /* = false */) +BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update /* = FALSE */, LLGLuint use_name) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update, use_new_name); + return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update, use_name); } // Copy sub image from frame buffer @@ -1361,7 +1363,7 @@ BOOL LLImageGL::createGLTexture() return TRUE ; } -BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, BOOL to_create, S32 category, bool defer_copy) +BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, BOOL to_create, S32 category, bool defer_copy, LLGLuint* tex_name) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; checkActiveThread(); @@ -1474,15 +1476,17 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S setCategory(category); const U8* rawdata = imageraw->getData(); - return createGLTexture(discard_level, rawdata, FALSE, usename, defer_copy); + return createGLTexture(discard_level, rawdata, FALSE, usename, defer_copy, tex_name); } -BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename, bool defer_copy) +BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename, bool defer_copy, LLGLuint* tex_name) // Call with void data, vmem is allocated but unitialized { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; checkActiveThread(); + bool main_thread = on_main_thread(); + if (defer_copy) { data_in = nullptr; @@ -1501,29 +1505,40 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ } discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel); - if (!defer_copy // <--- hacky way to force creation of mNewTexName from media texture update + if (main_thread // <--- always force creation of mNewTexName when not on main thread && mTexName != 0 && discard_level == mCurrentDiscardLevel) { + LL_PROFILE_ZONE_NAMED("cglt - early setImage"); // This will only be true if the size has not changed + if (tex_name != nullptr) + { + *tex_name = mTexName; + } return setImage(data_in, data_hasmips); } GLuint old_texname = mTexName; - + GLuint new_texname = 0; if (usename != 0) { - mNewTexName = usename; + llassert(main_thread); + new_texname = usename; } else { - LLImageGL::generateTextures(1, &mNewTexName); + LLImageGL::generateTextures(1, &new_texname); { - gGL.getTexUnit(0)->bind(this, false, false, mNewTexName); + gGL.getTexUnit(0)->bind(this, false, false, new_texname); glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel - discard_level); } } + if (tex_name != nullptr) + { + *tex_name = new_texname; + } + if (mUseMipMaps) { mAutoGenMips = gGLManager.mHasMipMapGeneration; @@ -1531,9 +1546,12 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ mCurrentDiscardLevel = discard_level; - if (!setImage(data_in, data_hasmips, mNewTexName)) { - return FALSE; + LL_PROFILE_ZONE_NAMED("cglt - late setImage"); + if (!setImage(data_in, data_hasmips, new_texname)) + { + return FALSE; + } } // Set texture options to our defaults. @@ -1550,11 +1568,11 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ } //if we're on the image loading thread, be sure to delete old_texname and update mTexName on the main thread - if (!on_main_thread()) + if (!main_thread) { if (!defer_copy) { - syncToMainThread(); + syncToMainThread(new_texname); } } else @@ -1564,8 +1582,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ { LLImageGL::deleteTextures(1, &old_texname); } - mTexName = mNewTexName; - mNewTexName = 0; + mTexName = new_texname; } mTextureMemory = (S32Bytes)getMipBytes(mCurrentDiscardLevel); @@ -1579,13 +1596,36 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ return TRUE; } -void LLImageGL::syncToMainThread() +void update_free_vram() { + LL_PROFILE_ZONE_SCOPED; + + if (gGLManager.mHasATIMemInfo) + { + S32 meminfo[4]; + glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo); + LLImageGLThread::sFreeVRAMMegabytes = meminfo[0]; + + } + else if (gGLManager.mHasNVXMemInfo) + { + S32 free_memory; + glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &free_memory); + LLImageGLThread::sFreeVRAMMegabytes = free_memory / 1024; + } +} + +void LLImageGL::syncToMainThread(LLGLuint new_tex_name) +{ + LL_PROFILE_ZONE_SCOPED; { LL_PROFILE_ZONE_NAMED("cglt - sync"); if (gGLManager.mHasSync) { // post a sync to the main thread (will execute before tex name swap lambda below) + // glFlush calls here are partly superstitious and partly backed by observation + // on AMD hardware + glFlush(); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); LL::WorkQueue::postMaybe( @@ -1615,17 +1655,27 @@ void LLImageGL::syncToMainThread() [=]() { LL_PROFILE_ZONE_NAMED("cglt - delete callback"); - if (mNewTexName != 0) + if (new_tex_name != 0) { if (mTexName != 0) { LLImageGL::deleteTextures(1, &mTexName); } - mTexName = mNewTexName; - mNewTexName = 0; + mTexName = new_tex_name; unref(); } }); + + + //update free vram periodically + static LLFrameTimer timer; + + if (timer.getElapsedTimeF32() > 1.f) //call this once per second. + { + update_free_vram(); + timer.reset(); + } + } BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const @@ -1756,14 +1806,6 @@ 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 @@ -2306,6 +2348,8 @@ void LLImageGL::checkActiveThread() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, nummips); */ +std::atomic LLImageGLThread::sFreeVRAMMegabytes; + LLImageGLThread::LLImageGLThread(LLWindow* window) // We want exactly one thread, but a very large capacity: we never want // anyone, especially inner-loop render code, to have to block on post() @@ -2332,3 +2376,14 @@ void LLImageGLThread::run() gGL.shutdown(); mWindow->destroySharedContext(mContext); } + +S32 LLImageGLThread::getFreeVRAMMegabytes() +{ + if (!sEnabled) + { + update_free_vram(); + } + + return sFreeVRAMMegabytes; +} + diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 6c6e327e42..50706890f9 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -110,17 +110,17 @@ public: BOOL createGLTexture() ; BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, - S32 category = sMaxCategories-1, bool defer_copy = false); - BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0, bool defer_copy = false); + S32 category = sMaxCategories-1, bool defer_copy = false, LLGLuint* tex_name = nullptr); + BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0, bool defer_copy = false, LLGLuint* tex_name = nullptr); 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 use_new_name = 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 use_new_name = false); + BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE, LLGLuint use_name = 0); + 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, LLGLuint use_name = 0); BOOL setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height); // wait for gl commands to finish on current thread and push // a lambda to main thread to swap mNewTexName and mTexName - void syncToMainThread(); + void syncToMainThread(LLGLuint new_tex_name); // Read back a raw image for this discard level, if it exists BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const; @@ -224,7 +224,7 @@ private: bool mGLTextureCreated ; LLGLuint mTexName; - LLGLuint mNewTexName = 0; // tex name set by background thread to be applied in main thread + //LLGLuint mNewTexName = 0; // tex name set by background thread to be applied in main thread U16 mWidth; U16 mHeight; S8 mCurrentDiscardLevel; @@ -324,6 +324,9 @@ public: // follows gSavedSettings "RenderGLMultiThreaded" static bool sEnabled; + // free video memory in megabytes + static std::atomic sFreeVRAMMegabytes; + LLImageGLThread(LLWindow* window); // post a function to be executed on the LLImageGL background thread @@ -335,6 +338,8 @@ public: void run() override; + static S32 getFreeVRAMMegabytes(); + private: LLWindow* mWindow; void* mContext = nullptr; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 8c792daac0..5ce51bb43e 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -3564,6 +3564,7 @@ void LLWindowWin32::swapBuffers() { LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; ASSERT_MAIN_THREAD(); + glFlush(); //superstitious flush for maybe frame stall removal? SwapBuffers(mhDC); LL_PROFILER_GPU_COLLECT diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 471b0e2c48..20287a7777 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -54,6 +54,8 @@ // static LLStandardBumpmap gStandardBumpmapList[TEM_BUMPMAP_COUNT]; +LL::WorkQueue::weak_t LLBumpImageList::sMainQueue; +LL::WorkQueue::weak_t LLBumpImageList::sTexUpdateQueue; // static U32 LLStandardBumpmap::sStandardBumpmapCount = 0; @@ -761,6 +763,8 @@ void LLBumpImageList::init() LLStandardBumpmap::init(); LLStandardBumpmap::restoreGL(); + sMainQueue = LL::WorkQueue::getInstance("mainloop"); + sTexUpdateQueue = LL::WorkQueue::getInstance("LLImageGL"); // Share work queue with tex loader. } void LLBumpImageList::clear() @@ -909,10 +913,7 @@ LLViewerTexture* LLBumpImageList::getBrightnessDarknessImage(LLViewerFetchedText } else { - LLPointer raw = new LLImageRaw(1,1,1); - raw->clear(0x77, 0x77, 0xFF, 0xFF); - - (*entries_list)[src_image->getID()] = LLViewerTextureManager::getLocalTexture( raw.get(), TRUE); + (*entries_list)[src_image->getID()] = LLViewerTextureManager::getLocalTexture( TRUE ); bump = (*entries_list)[src_image->getID()]; // In case callback was called immediately and replaced the image } @@ -1043,10 +1044,7 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI iter->second->getWidth() != src->getWidth() || iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution { //make sure an entry exists for this image - LLPointer raw = new LLImageRaw(1,1,1); - raw->clear(0x77, 0x77, 0xFF, 0xFF); - - entries_list[src_vi->getID()] = LLViewerTextureManager::getLocalTexture( raw.get(), TRUE); + entries_list[src_vi->getID()] = LLViewerTextureManager::getLocalTexture(TRUE); iter = entries_list.find(src_vi->getID()); } } @@ -1166,108 +1164,161 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI } //--------------------------------------------------- - // immediately assign bump to a global smart pointer in case some local smart pointer + // immediately assign bump to a smart pointer in case some local smart pointer // accidentally releases it. - LLPointer bump = LLViewerTextureManager::getLocalTexture( TRUE ); + LLPointer bump = iter->second; if (!LLPipeline::sRenderDeferred) { bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA); - bump->createGLTexture(0, dst_image); + + auto tex_queue = LLImageGLThread::sEnabled ? sTexUpdateQueue.lock() : nullptr; + + if (tex_queue) + { //dispatch creation to background thread + LLImageRaw* dst_ptr = dst_image; + LLViewerTexture* bump_ptr = bump; + dst_ptr->ref(); + bump_ptr->ref(); + tex_queue->post( + [=]() + { + LL_PROFILE_ZONE_NAMED("bil - create texture"); + bump_ptr->createGLTexture(0, dst_ptr); + bump_ptr->unref(); + dst_ptr->unref(); + }); + } + else + { + bump->createGLTexture(0, dst_image); + } } else { //convert to normal map //disable compression on normal maps to prevent errors below bump->getGLTexture()->setAllowCompression(false); + bump->getGLTexture()->setUseMipMaps(TRUE); - { - bump->setExplicitFormat(GL_RGBA8, GL_ALPHA); - bump->createGLTexture(0, dst_image); - } + auto* bump_ptr = bump.get(); + auto* dst_ptr = dst_image.get(); - { - gPipeline.mScreen.bindTarget(); - - LLGLDepthTest depth(GL_FALSE); - LLGLDisable cull(GL_CULL_FACE); - LLGLDisable blend(GL_BLEND); - gGL.setColorMask(TRUE, TRUE); - gNormalMapGenProgram.bind(); + bump_ptr->ref(); + dst_ptr->ref(); - static LLStaticHashedString sNormScale("norm_scale"); - static LLStaticHashedString sStepX("stepX"); - static LLStaticHashedString sStepY("stepY"); + bump_ptr->setExplicitFormat(GL_RGBA8, GL_ALPHA); - gNormalMapGenProgram.uniform1f(sNormScale, gSavedSettings.getF32("RenderNormalMapScale")); - gNormalMapGenProgram.uniform1f(sStepX, 1.f/bump->getWidth()); - gNormalMapGenProgram.uniform1f(sStepY, 1.f/bump->getHeight()); + auto create_texture = [bump_ptr, dst_ptr]() + { + LL_PROFILE_ZONE_NAMED("bil - create texture deferred"); + bump_ptr->createGLTexture(0, dst_ptr); + }; - LLVector2 v((F32) bump->getWidth()/gPipeline.mScreen.getWidth(), - (F32) bump->getHeight()/gPipeline.mScreen.getHeight()); + auto gen_normal_map = [bump_ptr, dst_ptr]() + { + LL_PROFILE_ZONE_NAMED("bil - generate normal map"); + gPipeline.mScreen.bindTarget(); - gGL.getTexUnit(0)->bind(bump); - - S32 width = bump->getWidth(); - S32 height = bump->getHeight(); + LLGLDepthTest depth(GL_FALSE); + LLGLDisable cull(GL_CULL_FACE); + LLGLDisable blend(GL_BLEND); + gGL.setColorMask(TRUE, TRUE); + gNormalMapGenProgram.bind(); - S32 screen_width = gPipeline.mScreen.getWidth(); - S32 screen_height = gPipeline.mScreen.getHeight(); + static LLStaticHashedString sNormScale("norm_scale"); + static LLStaticHashedString sStepX("stepX"); + static LLStaticHashedString sStepY("stepY"); - glViewport(0, 0, screen_width, screen_height); + gNormalMapGenProgram.uniform1f(sNormScale, gSavedSettings.getF32("RenderNormalMapScale")); + gNormalMapGenProgram.uniform1f(sStepX, 1.f / bump_ptr->getWidth()); + gNormalMapGenProgram.uniform1f(sStepY, 1.f / bump_ptr->getHeight()); - for (S32 left = 0; left < width; left += screen_width) - { - S32 right = left + screen_width; - right = llmin(right, width); - - F32 left_tc = (F32) left/ width; - F32 right_tc = (F32) right/width; + LLVector2 v((F32)bump_ptr->getWidth() / gPipeline.mScreen.getWidth(), + (F32)bump_ptr->getHeight() / gPipeline.mScreen.getHeight()); - for (S32 bottom = 0; bottom < height; bottom += screen_height) - { - S32 top = bottom+screen_height; - top = llmin(top, height); + gGL.getTexUnit(0)->bind(bump_ptr); - F32 bottom_tc = (F32) bottom/height; - F32 top_tc = (F32)(bottom+screen_height)/height; - top_tc = llmin(top_tc, 1.f); + S32 width = bump_ptr->getWidth(); + S32 height = bump_ptr->getHeight(); - F32 screen_right = (F32) (right-left)/screen_width; - F32 screen_top = (F32) (top-bottom)/screen_height; + S32 screen_width = gPipeline.mScreen.getWidth(); + S32 screen_height = gPipeline.mScreen.getHeight(); - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(left_tc, bottom_tc); - gGL.vertex2f(0, 0); + glViewport(0, 0, screen_width, screen_height); - gGL.texCoord2f(left_tc, top_tc); - gGL.vertex2f(0, screen_top); + for (S32 left = 0; left < width; left += screen_width) + { + S32 right = left + screen_width; + right = llmin(right, width); - gGL.texCoord2f(right_tc, bottom_tc); - gGL.vertex2f(screen_right, 0); + F32 left_tc = (F32)left / width; + F32 right_tc = (F32)right / width; - gGL.texCoord2f(right_tc, top_tc); - gGL.vertex2f(screen_right, screen_top); + for (S32 bottom = 0; bottom < height; bottom += screen_height) + { + S32 top = bottom + screen_height; + top = llmin(top, height); - gGL.end(); + F32 bottom_tc = (F32)bottom / height; + F32 top_tc = (F32)(bottom + screen_height) / height; + top_tc = llmin(top_tc, 1.f); - gGL.flush(); + F32 screen_right = (F32)(right - left) / screen_width; + F32 screen_top = (F32)(top - bottom) / screen_height; - S32 w = right-left; - S32 h = top-bottom; + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(left_tc, bottom_tc); + gGL.vertex2f(0, 0); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, 0, 0, w, h); - } - } + gGL.texCoord2f(left_tc, top_tc); + gGL.vertex2f(0, screen_top); - glGenerateMipmap(GL_TEXTURE_2D); + gGL.texCoord2f(right_tc, bottom_tc); + gGL.vertex2f(screen_right, 0); - gPipeline.mScreen.flush(); + gGL.texCoord2f(right_tc, top_tc); + gGL.vertex2f(screen_right, screen_top); - gNormalMapGenProgram.unbind(); - - //generateNormalMapFromAlpha(dst_image, nrm_image); - } + gGL.end(); + + gGL.flush(); + + S32 w = right - left; + S32 h = top - bottom; + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, 0, 0, w, h); + } + } + + glGenerateMipmap(GL_TEXTURE_2D); + + gPipeline.mScreen.flush(); + + gNormalMapGenProgram.unbind(); + + //generateNormalMapFromAlpha(dst_image, nrm_image); + + bump_ptr->unref(); + dst_ptr->unref(); + }; + + auto main_queue = LLImageGLThread::sEnabled ? sMainQueue.lock() : nullptr; + + if (main_queue) + { //dispatch creation to background thread + LLImageRaw* dst_ptr = dst_image; + LLViewerTexture* bump_ptr = bump; + dst_ptr->ref(); + bump_ptr->ref(); + + main_queue->postTo(sTexUpdateQueue, create_texture, gen_normal_map); + } + else + { + create_texture(); + gen_normal_map(); + } } iter->second = bump; // derefs (and deletes) old image diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h index 38744a7d98..6e21859738 100644 --- a/indra/newview/lldrawpoolbump.h +++ b/indra/newview/lldrawpoolbump.h @@ -161,6 +161,8 @@ private: typedef std::unordered_map > bump_image_map_t; bump_image_map_t mBrightnessEntries; bump_image_map_t mDarknessEntries; + static LL::WorkQueue::weak_t sMainQueue; + static LL::WorkQueue::weak_t sTexUpdateQueue; }; extern LLBumpImageList gBumpImageList; diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index b5d3dc5d30..4fc1bdbec3 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -649,9 +649,6 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD_PARTICLES); } - //upkeep gl name pools - LLGLNamePool::upkeepPools(); - stop_glerror(); display_update_camera(); stop_glerror(); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index bd60b9f1e2..42bd5d8367 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2986,11 +2986,12 @@ void LLViewerMediaImpl::doMediaTexUpdate(LLViewerMediaTexture* media_tex, U8* da LLPointer raw = new LLImageRaw(data, media_tex->getWidth(), media_tex->getHeight(), media_tex->getComponents(), true); // Allocate GL texture based on LLImageRaw but do NOT copy to GL - media_tex->createGLTexture(0, raw, 0, TRUE, LLGLTexture::OTHER, true); + LLGLuint tex_name = 0; + media_tex->createGLTexture(0, raw, 0, TRUE, LLGLTexture::OTHER, true, &tex_name); // copy just the subimage covered by the image raw to GL - media_tex->setSubImage(data, data_width, data_height, x_pos, y_pos, width, height, sync); - media_tex->getGLTexture()->syncToMainThread(); + media_tex->setSubImage(data, data_width, data_height, x_pos, y_pos, width, height, tex_name); + media_tex->getGLTexture()->syncToMainThread(tex_name); // release the data pointer before freeing raw so LLImageRaw destructor doesn't // free memory at data pointer diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp index 3cdef0ebff..65e9fa533d 100644 --- a/indra/newview/llvieweroctree.cpp +++ b/indra/newview/llvieweroctree.cpp @@ -787,42 +787,73 @@ void LLViewerOctreeGroup::checkStates() //occulsion culling functions and classes //------------------------------------------------------------------------------------------- std::set LLOcclusionCullingGroup::sPendingQueries; -class LLOcclusionQueryPool : public LLGLNamePool -{ -public: - LLOcclusionQueryPool() - { - } -protected: +static std::queue sFreeQueries; - virtual GLuint allocateName() - { - GLuint ret = 0; - - glGenQueriesARB(1, &ret); - - return ret; - } - - virtual void releaseName(GLuint name) - { -#if LL_TRACK_PENDING_OCCLUSION_QUERIES - LLOcclusionCullingGroup::sPendingQueries.erase(name); -#endif - glDeleteQueriesARB(1, &name); - } -}; - -static LLOcclusionQueryPool sQueryPool; U32 LLOcclusionCullingGroup::getNewOcclusionQueryObjectName() { - return sQueryPool.allocate(); + LL_PROFILE_ZONE_SCOPED; + // TODO: refactor this to a general purpose name pool + static GLuint occlusion_queries[2][1024]; + static std::atomic query_count[2]; + static S32 query_page = -1; + + //reuse any query names that have been freed + if (!sFreeQueries.empty()) + { + GLuint ret = sFreeQueries.front(); + sFreeQueries.pop(); + return ret; + } + + // first call, immediately fill entire name pool + if (query_page == -1) + { + glGenQueriesARB(1024, occlusion_queries[0]); + glGenQueriesARB(1024, occlusion_queries[1]); + query_page = 0; + query_count[0] = 1024; + query_count[1] = 1024; + } + + if (query_count[query_page] == 0) //this page is empty + { + //check the other page + query_page = (query_page + 1) % 2; + + if (query_count[query_page] == 0) + { + //the other page is also empty, generate immediately and return + GLuint ret; + glGenQueriesARB(1, &ret); + return ret; + } + } + + GLuint ret = occlusion_queries[query_page][--query_count[query_page]]; + + if (query_count[query_page] == 0) + { //exhausted this page, replenish on background thread + S32 page = query_page; + LL::WorkQueue::postMaybe(LL::WorkQueue::getInstance("LLImageGL"), + [=]() + { + LL_PROFILE_ZONE_NAMED("glGenQueries bg"); + if (query_count[page] == 0) // <-- protect against redundant attempts to replenish + { + glGenQueriesARB(1024, occlusion_queries[page]); + query_count[page] = 1024; + glFlush(); + } + }); + } + + return ret; } void LLOcclusionCullingGroup::releaseOcclusionQueryObjectName(GLuint name) { - sQueryPool.release(name); + sFreeQueries.push(name); } //===================================== @@ -1243,7 +1274,10 @@ void LLOcclusionCullingGroup::doOcclusion(LLCamera* camera, const LLVector4a* sh //store which frame this query was issued on mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount; - glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]); + { + LL_PROFILE_ZONE_NAMED("glBeginQuery"); + glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]); + } LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(shader); @@ -1282,7 +1316,10 @@ void LLOcclusionCullingGroup::doOcclusion(LLCamera* camera, const LLVector4a* sh } } - glEndQueryARB(mode); + { + LL_PROFILE_ZONE_NAMED("glEndQuery"); + glEndQueryARB(mode); + } } } diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 5fed46f437..fa2c070ea0 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -515,9 +515,10 @@ void LLViewerTexture::getGPUMemoryForTextures(S32Megabytes &gpu, S32Megabytes &p { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; static LLFrameTimer timer; + static S32Megabytes gpu_res = S32Megabytes(S32_MAX); static S32Megabytes physical_res = S32Megabytes(S32_MAX); - + if (timer.getElapsedTimeF32() < GPU_MEMORY_CHECK_WAIT_TIME) //call this once per second. { gpu = gpu_res; @@ -527,22 +528,11 @@ void LLViewerTexture::getGPUMemoryForTextures(S32Megabytes &gpu, S32Megabytes &p timer.reset(); { - if (gGLManager.mHasATIMemInfo) - { - S32 meminfo[4]; - glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo); - gpu_res = (S32Megabytes)meminfo[0]; - - //check main memory, only works for windows. - LLMemory::updateMemoryInfo(); - physical_res = LLMemory::getAvailableMemKB(); - } - else if (gGLManager.mHasNVXMemInfo) - { - S32 free_memory; - glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &free_memory); - gpu_res = (S32Megabytes)(free_memory / 1024); - } + gpu_res = (S32Megabytes) LLImageGLThread::getFreeVRAMMegabytes(); + + //check main memory, only works for windows. + LLMemory::updateMemoryInfo(); + physical_res = LLMemory::getAvailableMemKB(); gpu = gpu_res; physical = physical_res; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 8d45e64bf8..ac242c2bf7 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -7267,11 +7267,6 @@ void LLPipeline::doResetVertexBuffers(bool forced) SUBSYSTEM_CLEANUP(LLVertexBuffer); - //delete all name pool caches - LLGLNamePool::cleanupPools(); - - - if (LLVertexBuffer::sGLCount > 0) { LL_WARNS() << "VBO wipe failed -- " << LLVertexBuffer::sGLCount << " buffers remaining." << LL_ENDL; From a8b5ef66a97e0a34c55a738a6fd275cafd4cd273 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 18 Feb 2022 13:14:16 -0600 Subject: [PATCH 03/11] SL-16886 Fix for bad batching parameters for some rigged meshes. --- indra/newview/llvovolume.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 49be0765a6..e583f7176d 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -5910,23 +5910,25 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) U32 geometryBytes = 0; + // generate render batches for static geometry U32 extra_mask = LLVertexBuffer::MAP_TEXTURE_INDEX; - geometryBytes += genDrawInfo(group, simple_mask | extra_mask, sSimpleFaces[0], simple_count[0], FALSE, batch_textures); - geometryBytes += genDrawInfo(group, fullbright_mask | extra_mask, sFullbrightFaces[0], fullbright_count[0], FALSE, batch_textures); - geometryBytes += genDrawInfo(group, alpha_mask | extra_mask, sAlphaFaces[0], alpha_count[0], TRUE, batch_textures); - geometryBytes += genDrawInfo(group, bump_mask | extra_mask, sBumpFaces[0], bump_count[0], FALSE, FALSE); - geometryBytes += genDrawInfo(group, norm_mask | extra_mask, sNormFaces[0], norm_count[0], FALSE, FALSE); - geometryBytes += genDrawInfo(group, spec_mask | extra_mask, sSpecFaces[0], spec_count[0], FALSE, FALSE); - geometryBytes += genDrawInfo(group, normspec_mask | extra_mask, sNormSpecFaces[0], normspec_count[0], FALSE, FALSE); + BOOL alpha_sort = TRUE; + BOOL rigged = FALSE; + for (int i = 0; i < 2; ++i) //two sets, static and rigged) + { + geometryBytes += genDrawInfo(group, simple_mask | extra_mask, sSimpleFaces[i], simple_count[i], FALSE, batch_textures, rigged); + geometryBytes += genDrawInfo(group, fullbright_mask | extra_mask, sFullbrightFaces[i], fullbright_count[i], FALSE, batch_textures, rigged); + geometryBytes += genDrawInfo(group, alpha_mask | extra_mask, sAlphaFaces[i], alpha_count[i], alpha_sort, batch_textures, rigged); + geometryBytes += genDrawInfo(group, bump_mask | extra_mask, sBumpFaces[i], bump_count[i], FALSE, FALSE, rigged); + geometryBytes += genDrawInfo(group, norm_mask | extra_mask, sNormFaces[i], norm_count[i], FALSE, FALSE, rigged); + geometryBytes += genDrawInfo(group, spec_mask | extra_mask, sSpecFaces[i], spec_count[i], FALSE, FALSE, rigged); + geometryBytes += genDrawInfo(group, normspec_mask | extra_mask, sNormSpecFaces[i], normspec_count[i], FALSE, FALSE, rigged); - extra_mask |= LLVertexBuffer::MAP_WEIGHT4; - geometryBytes += genDrawInfo(group, simple_mask | extra_mask, sSimpleFaces[1], simple_count[1], FALSE, batch_textures, TRUE); - geometryBytes += genDrawInfo(group, fullbright_mask | extra_mask, sFullbrightFaces[1], fullbright_count[1], FALSE, batch_textures, TRUE); - geometryBytes += genDrawInfo(group, alpha_mask | extra_mask, sAlphaFaces[1], alpha_count[1], TRUE, batch_textures, TRUE); - geometryBytes += genDrawInfo(group, bump_mask | extra_mask, sBumpFaces[1], bump_count[1], FALSE, TRUE); - geometryBytes += genDrawInfo(group, norm_mask | extra_mask, sNormFaces[1], norm_count[1], FALSE, TRUE); - geometryBytes += genDrawInfo(group, spec_mask | extra_mask, sSpecFaces[1], spec_count[1], FALSE, TRUE); - geometryBytes += genDrawInfo(group, normspec_mask | extra_mask, sNormSpecFaces[1], normspec_count[1], FALSE, TRUE); + // for rigged set, add weights and disable alpha sorting (rigged items use depth buffer) + extra_mask |= LLVertexBuffer::MAP_WEIGHT4; + alpha_sort = FALSE; + rigged = TRUE; + } group->mGeometryBytes = geometryBytes; From 74ac4ae0ebc52ad76366bb688ba9c9635b26d6c2 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 18 Feb 2022 19:41:54 +0200 Subject: [PATCH 04/11] SL-16811 Shape preview images do not display all of the attachments. --- indra/newview/lltoolmorph.cpp | 13 +-- indra/newview/pipeline.cpp | 161 ++++++++++++++++++++++++++++++++++ indra/newview/pipeline.h | 1 + 3 files changed, 164 insertions(+), 11 deletions(-) diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp index b2e8348ffd..b5eb2880ae 100644 --- a/indra/newview/lltoolmorph.cpp +++ b/indra/newview/lltoolmorph.cpp @@ -239,17 +239,8 @@ BOOL LLVisualParamHint::render() LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE); - if (gAgentAvatarp->mDrawable.notNull() && - gAgentAvatarp->mDrawable->getFace(0)) - { - LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)gAgentAvatarp->mDrawable->getFace(0)->getPool(); - LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE); - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_REPLACE); - avatarPoolp->renderAvatars(gAgentAvatarp); // renders only one avatar - gGL.setSceneBlendType(LLRender::BT_ALPHA); - gGL.flush(); - } + gPipeline.previewAvatar(gAgentAvatarp); + gAgentAvatarp->setVisualParamWeight(mVisualParam->getID(), mLastParamWeight); mWearablePtr->setVisualParamWeight(mVisualParam->getID(), mLastParamWeight); LLViewerWearable* wearable = (LLViewerWearable*)mWearablePtr; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index ac242c2bf7..5fe883b714 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -11133,6 +11133,167 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar) LLGLState::checkTextureChannels(); } +static LLTrace::BlockTimerStatHandle FTM_PREVIEW_AVATAR("Preview Avatar"); + +void LLPipeline::previewAvatar(LLVOAvatar* avatar) +{ + LL_RECORD_BLOCK_TIME(FTM_PREVIEW_AVATAR); + + LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE); + gGL.flush(); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + + static LLCullResult result; + result.clear(); + grabReferences(result); + + if (!avatar || !avatar->mDrawable) + { + LL_WARNS_ONCE("AvatarRenderPipeline") << "Avatar is " << (avatar ? "not drawable" : "null") << LL_ENDL; + return; + } + LL_DEBUGS_ONCE("AvatarRenderPipeline") << "Avatar " << avatar->getID() << " is drawable" << LL_ENDL; + + assertInitialized(); + + pushRenderTypeMask(); + + { + //hide world geometry + clearRenderTypeMask( + RENDER_TYPE_SKY, + RENDER_TYPE_WL_SKY, + RENDER_TYPE_GROUND, + RENDER_TYPE_TERRAIN, + RENDER_TYPE_GRASS, + RENDER_TYPE_CONTROL_AV, // Animesh + RENDER_TYPE_TREE, + RENDER_TYPE_VOIDWATER, + RENDER_TYPE_WATER, + RENDER_TYPE_PASS_GRASS, + RENDER_TYPE_HUD, + RENDER_TYPE_PARTICLES, + RENDER_TYPE_CLOUDS, + RENDER_TYPE_HUD_PARTICLES, + END_RENDER_TYPES + ); + } + + S32 occlusion = sUseOcclusion; + sUseOcclusion = 0; + + sReflectionRender = !sRenderDeferred; + + sShadowRender = true; + sImpostorRender = true; // Likely not needed for previews + + LLViewerCamera* viewer_camera = LLViewerCamera::getInstance(); + + { + markVisible(avatar->mDrawable, *viewer_camera); + + LLVOAvatar::attachment_map_t::iterator iter; + for (iter = avatar->mAttachmentPoints.begin(); + iter != avatar->mAttachmentPoints.end(); + ++iter) + { + LLViewerJointAttachment *attachment = iter->second; + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + if (LLViewerObject* attached_object = attachment_iter->get()) + { + markVisible(attached_object->mDrawable->getSpatialBridge(), *viewer_camera); + } + } + } + } + + stateSort(*LLViewerCamera::getInstance(), result); + + LLCamera camera = *viewer_camera; + + F32 old_alpha = LLDrawPoolAvatar::sMinimumAlpha; + + if (LLPipeline::sRenderDeferred) + { + renderGeomDeferred(camera); + + renderGeomPostDeferred(camera); + } + else + { + renderGeom(camera); + } + + LLDrawPoolAvatar::sMinimumAlpha = old_alpha; + + { //create alpha mask based on depth buffer + if (LLPipeline::sRenderDeferred) + { + GLuint buff = GL_COLOR_ATTACHMENT0; + LL_PROFILER_GPU_ZONEC("gl.DrawBuffersARB", 0x8000FF); + glDrawBuffersARB(1, &buff); + } + + LLGLDisable blend(GL_BLEND); + + gGL.setColorMask(false, true); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_GREATER); + + gGL.flush(); + + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + + static const F32 clip_plane = 0.99999f; + + gDebugProgram.bind(); + + gGL.begin(LLRender::QUADS); + gGL.vertex3f(-1, -1, clip_plane); + gGL.vertex3f(1, -1, clip_plane); + gGL.vertex3f(1, 1, clip_plane); + gGL.vertex3f(-1, 1, clip_plane); + gGL.end(); + gGL.flush(); + + gDebugProgram.unbind(); + + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + } + + sUseOcclusion = occlusion; + sReflectionRender = false; + sImpostorRender = false; + sShadowRender = false; + popRenderTypeMask(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + + LLVertexBuffer::unbind(); + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.flush(); +} + bool LLPipeline::hasRenderBatches(const U32 type) const { return sCull->getRenderMapSize(type) > 0; diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 6114aa4f6c..df5c060417 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -135,6 +135,7 @@ public: void resetVertexBuffers(LLDrawable* drawable); void generateImpostor(LLVOAvatar* avatar); + void previewAvatar(LLVOAvatar* avatar); void bindScreenToTexture(); void renderFinalize(); From 7c90b82ff772df4f8e200ef83c2ecf577b6bdec0 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 18 Feb 2022 15:37:55 -0600 Subject: [PATCH 05/11] SL-16815 Better occlusion query name pool. Leverage pool FILO nature to avoid reusing a query while the GPU is still chewing on it. --- indra/newview/llvieweroctree.cpp | 70 +++++++++----------------------- 1 file changed, 20 insertions(+), 50 deletions(-) diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp index 65e9fa533d..5eda75753e 100644 --- a/indra/newview/llvieweroctree.cpp +++ b/indra/newview/llvieweroctree.cpp @@ -790,70 +790,36 @@ std::set LLOcclusionCullingGroup::sPendingQueries; static std::queue sFreeQueries; +#define QUERY_POOL_SIZE 1024 + U32 LLOcclusionCullingGroup::getNewOcclusionQueryObjectName() { LL_PROFILE_ZONE_SCOPED; - // TODO: refactor this to a general purpose name pool - static GLuint occlusion_queries[2][1024]; - static std::atomic query_count[2]; - static S32 query_page = -1; - //reuse any query names that have been freed - if (!sFreeQueries.empty()) + if (sFreeQueries.empty()) { - GLuint ret = sFreeQueries.front(); - sFreeQueries.pop(); - return ret; - } - - // first call, immediately fill entire name pool - if (query_page == -1) - { - glGenQueriesARB(1024, occlusion_queries[0]); - glGenQueriesARB(1024, occlusion_queries[1]); - query_page = 0; - query_count[0] = 1024; - query_count[1] = 1024; - } - - if (query_count[query_page] == 0) //this page is empty - { - //check the other page - query_page = (query_page + 1) % 2; - - if (query_count[query_page] == 0) + //seed 1024 query names into the free query pool + GLuint queries[1024]; + glGenQueriesARB(1024, queries); + for (int i = 0; i < 1024; ++i) { - //the other page is also empty, generate immediately and return - GLuint ret; - glGenQueriesARB(1, &ret); - return ret; + sFreeQueries.push(queries[i]); } } - GLuint ret = occlusion_queries[query_page][--query_count[query_page]]; - - if (query_count[query_page] == 0) - { //exhausted this page, replenish on background thread - S32 page = query_page; - LL::WorkQueue::postMaybe(LL::WorkQueue::getInstance("LLImageGL"), - [=]() - { - LL_PROFILE_ZONE_NAMED("glGenQueries bg"); - if (query_count[page] == 0) // <-- protect against redundant attempts to replenish - { - glGenQueriesARB(1024, occlusion_queries[page]); - query_count[page] = 1024; - glFlush(); - } - }); - } - + // pull from pool + GLuint ret = sFreeQueries.front(); + sFreeQueries.pop(); return ret; } void LLOcclusionCullingGroup::releaseOcclusionQueryObjectName(GLuint name) { - sFreeQueries.push(name); + if (name != 0) + { + LL_PROFILE_ZONE_SCOPED; + sFreeQueries.push(name); + } } //===================================== @@ -1276,6 +1242,10 @@ void LLOcclusionCullingGroup::doOcclusion(LLCamera* camera, const LLVector4a* sh { LL_PROFILE_ZONE_NAMED("glBeginQuery"); + + //get an occlusion query that hasn't been used in awhile + releaseOcclusionQueryObjectName(mOcclusionQuery[LLViewerCamera::sCurCameraID]); + mOcclusionQuery[LLViewerCamera::sCurCameraID] = getNewOcclusionQueryObjectName(); glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]); } From 1461e4d437996784cc8afe5d4b3bfa873d3dbfa1 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 18 Feb 2022 15:52:24 -0600 Subject: [PATCH 06/11] SL-16815 Fix for crash when disabling ALM. --- indra/newview/lldrawpoolbump.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 20287a7777..7d03cb7609 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -1218,6 +1218,10 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI auto gen_normal_map = [bump_ptr, dst_ptr]() { LL_PROFILE_ZONE_NAMED("bil - generate normal map"); + if (gNormalMapGenProgram.mProgramObject == 0) + { + return; + } gPipeline.mScreen.bindTarget(); LLGLDepthTest depth(GL_FALSE); From 9f10853c03293f845d9a17adcf8de7ea23a0a778 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 21 Feb 2022 16:07:58 +0200 Subject: [PATCH 07/11] SL-16887 FIXED Crash in LLNetMap::draw --- indra/newview/llnetmap.cpp | 4 ++++ indra/newview/lltracker.cpp | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 937f36b6fc..1240ce7c0f 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -147,6 +147,10 @@ void LLNetMap::setScale( F32 scale ) void LLNetMap::draw() { + if (!LLWorld::instanceExists()) + { + return; + } LL_PROFILE_ZONE_SCOPED; static LLFrameTimer map_timer; static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white); diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp index 728d0c9417..320554c39e 100644 --- a/indra/newview/lltracker.cpp +++ b/indra/newview/lltracker.cpp @@ -109,7 +109,12 @@ void LLTracker::stopTracking(bool clear_ui) // static virtual void LLTracker::drawHUDArrow() { - if (!gSavedSettings.getBOOL("RenderTrackerBeacon")) return; + if (!LLWorld::instanceExists()) + { + return; + } + + if (!gSavedSettings.getBOOL("RenderTrackerBeacon")) return; if (gViewerWindow->getProgressView()->getVisible()) return; From 97f6736d7fe2575023eff3a6429855c19d9f92f5 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 21 Feb 2022 17:46:27 +0200 Subject: [PATCH 08/11] Cached setting should be used in draw() calls --- indra/newview/lltracker.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp index 320554c39e..ab4ad5817b 100644 --- a/indra/newview/lltracker.cpp +++ b/indra/newview/lltracker.cpp @@ -113,8 +113,12 @@ void LLTracker::drawHUDArrow() { return; } - - if (!gSavedSettings.getBOOL("RenderTrackerBeacon")) return; + + static LLCachedControl render_beacon(gSavedSettings, "RenderTrackerBeacon", true); + if (!render_beacon) + { + return; + } if (gViewerWindow->getProgressView()->getVisible()) return; From e24acd81e0cf47c1a8f39e2485daf81c39d26ca3 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 21 Feb 2022 21:31:19 +0200 Subject: [PATCH 09/11] SL-16892 Fix crash at getCameraOffsetInitial getCameraOffsetInitial() call can happen before init() --- indra/newview/llagentcamera.cpp | 19 ++++++++++--------- indra/newview/llagentcamera.h | 6 ------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index ed6c3c307f..84a41113be 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -210,9 +210,6 @@ void LLAgentCamera::init() mCameraPreset = (ECameraPreset) gSavedSettings.getU32("CameraPresetType"); - mCameraOffsetInitial = gSavedSettings.getControl("CameraOffsetRearView"); - mFocusOffsetInitial = gSavedSettings.getControl("FocusOffsetRearView"); - mCameraCollidePlane.clearVec(); mCurrentCameraDistance = getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale"); mTargetCameraDistance = mCurrentCameraDistance; @@ -1672,8 +1669,8 @@ LLVector3d LLAgentCamera::calcThirdPersonFocusOffset() agent_rot *= ((LLViewerObject*)(gAgentAvatarp->getParent()))->getRenderRotation(); } - focus_offset = convert_from_llsd(mFocusOffsetInitial->get(), TYPE_VEC3D, ""); - return focus_offset * agent_rot; + static LLCachedControl focus_offset_initial(gSavedSettings, "FocusOffsetRearView", LLVector3d()); + return focus_offset_initial * agent_rot; } void LLAgentCamera::setupSitCamera() @@ -1810,8 +1807,9 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit) } else { - local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale"); - + static LLCachedControl camera_offset_scale(gSavedSettings, "CameraOffsetScale"); + local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * camera_offset_scale; + // are we sitting down? if (isAgentAvatarValid() && gAgentAvatarp->getParent()) { @@ -2028,12 +2026,15 @@ bool LLAgentCamera::isJoystickCameraUsed() LLVector3 LLAgentCamera::getCameraOffsetInitial() { - return convert_from_llsd(mCameraOffsetInitial->get(), TYPE_VEC3, ""); + // getCameraOffsetInitial and getFocusOffsetInitial can be called on update from idle before init() + static LLCachedControl camera_offset_initial (gSavedSettings, "CameraOffsetRearView", LLVector3()); + return camera_offset_initial; } LLVector3d LLAgentCamera::getFocusOffsetInitial() { - return convert_from_llsd(mFocusOffsetInitial->get(), TYPE_VEC3D, ""); + static LLCachedControl focus_offset_initial(gSavedSettings, "FocusOffsetRearView", LLVector3d()); + return focus_offset_initial; } F32 LLAgentCamera::getCameraMaxZoomDistance() diff --git a/indra/newview/llagentcamera.h b/indra/newview/llagentcamera.h index ec1ed433d7..89680f95dc 100644 --- a/indra/newview/llagentcamera.h +++ b/indra/newview/llagentcamera.h @@ -131,12 +131,6 @@ private: /** Camera preset in Third Person Mode */ ECameraPreset mCameraPreset; - /** Initial camera offset */ - LLPointer mCameraOffsetInitial; - - /** Initial focus offset */ - LLPointer mFocusOffsetInitial; - LLQuaternion mInitSitRot; //-------------------------------------------------------------------- From 4e31ff1d7e05ff376fc1c66156eb76cccbdd5f38 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 21 Feb 2022 23:11:17 +0200 Subject: [PATCH 10/11] SL-16056 Load cloud texture only once --- indra/newview/llvoavatar.cpp | 5 +++-- indra/newview/llvoavatar.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 2d34e28b93..31035f666d 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -619,6 +619,7 @@ F32 LLVOAvatar::sUnbakedTime = 0.f; F32 LLVOAvatar::sUnbakedUpdateTime = 0.f; F32 LLVOAvatar::sGreyTime = 0.f; F32 LLVOAvatar::sGreyUpdateTime = 0.f; +LLPointer LLVOAvatar::sCloudTexture = NULL; //----------------------------------------------------------------------------- // Helper functions @@ -1136,6 +1137,7 @@ void LLVOAvatar::initClass() LLControlAvatar::sRegionChangedSlot = gAgent.addRegionChangedCallback(&LLControlAvatar::onRegionChanged); + sCloudTexture = LLViewerTextureManager::getFetchedTextureFromFile("cloud-particle.j2c"); } @@ -3080,8 +3082,7 @@ void LLVOAvatar::idleUpdateLoadingEffect() particle_parameters.mPartData.mStartColor = LLColor4(1, 1, 1, 0.5f); particle_parameters.mPartData.mEndColor = LLColor4(1, 1, 1, 0.0f); particle_parameters.mPartData.mStartScale.mV[VX] = 0.8f; - LLViewerTexture* cloud = LLViewerTextureManager::getFetchedTextureFromFile("cloud-particle.j2c"); - particle_parameters.mPartImageID = cloud->getID(); + particle_parameters.mPartImageID = sCloudTexture->getID(); particle_parameters.mMaxAge = 0.f; particle_parameters.mPattern = LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE; particle_parameters.mInnerAngle = F_PI; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 7c2d71802e..ba67007fa9 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -336,7 +336,8 @@ public: static F32 sLODFactor; // user-settable LOD factor static F32 sPhysicsLODFactor; // user-settable physics LOD factor static BOOL sJointDebug; // output total number of joints being touched for each avatar - static BOOL sDebugAvatarRotation; + + static LLPointer sCloudTexture; //-------------------------------------------------------------------- // Region state From 900c13d361671c35cd5899dc99bbfbc9c5ec2200 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 22 Feb 2022 00:05:38 +0200 Subject: [PATCH 11/11] SL-16892 Fix incorrect width of dropdown menus --- indra/llui/llcombobox.cpp | 29 +++++++++++++++++++++++++++++ indra/llui/llcombobox.h | 3 +++ 2 files changed, 32 insertions(+) diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index bcc653a602..9ca05a16f3 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -113,6 +113,10 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p) } mArrowImage = button_params.image_unselected; + if (mArrowImage.notNull()) + { + mImageLoadedConnection = mArrowImage->addLoadedCallback(boost::bind(&LLComboBox::imageLoaded, this)); + } mButton = LLUICtrlFactory::create(button_params); @@ -183,6 +187,7 @@ LLComboBox::~LLComboBox() // explicitly disconect this signal, since base class destructor might fire top lost mTopLostSignalConnection.disconnect(); + mImageLoadedConnection.disconnect(); } @@ -1074,6 +1079,30 @@ void LLComboBox::onSetHighlight() const } } +void LLComboBox::imageLoaded() +{ + static LLUICachedControl drop_shadow_button("DropShadowButton", 0); + + if (mAllowTextEntry) + { + LLRect rect = getLocalRect(); + S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; + S32 shadow_size = drop_shadow_button; + mButton->setRect(LLRect(getRect().getWidth() - llmax(8, arrow_width) - 2 * shadow_size, + rect.mTop, rect.mRight, rect.mBottom)); + if (mButton->getVisible()) + { + // recalculate field size + if (mTextEntry) + { + LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + text_entry_rect.mRight -= llmax(8, arrow_width) + 2 * drop_shadow_button; + mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE); + } + } + } +} + //============================================================================ // LLCtrlListInterface functions diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index e17d6cdfb4..cac8850a25 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -105,6 +105,8 @@ protected: virtual std::string _getSearchText() const; virtual void onSetHighlight() const; + void imageLoaded(); + public: // LLView interface virtual void onFocusLost(); @@ -244,6 +246,7 @@ private: commit_callback_t mTextChangedCallback; commit_callback_t mSelectionCallback; boost::signals2::connection mTopLostSignalConnection; + boost::signals2::connection mImageLoadedConnection; commit_signal_t mOnReturnSignal; S32 mLastSelectedIndex; };