diff options
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/llcommon/commoncontrol.cpp | 106 | ||||
| -rw-r--r-- | indra/llcommon/commoncontrol.h | 75 | ||||
| -rw-r--r-- | indra/llcommon/threadpool.cpp | 60 | ||||
| -rw-r--r-- | indra/llcommon/threadpool.h | 34 | ||||
| -rw-r--r-- | indra/llimage/llimageworker.cpp | 104 | ||||
| -rw-r--r-- | indra/llimage/llimageworker.h | 61 | ||||
| -rw-r--r-- | indra/llimage/tests/llimageworker_test.cpp | 66 | ||||
| -rw-r--r-- | indra/llrender/llimagegl.cpp | 6 | ||||
| -rw-r--r-- | indra/llwindow/llwindowwin32.cpp | 22 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | indra/newview/VIEWER_VERSION.txt | 2 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 4 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 20 | ||||
| -rw-r--r-- | indra/newview/lltexturefetch.cpp | 6 | ||||
| -rw-r--r-- | indra/newview/llviewertexture.cpp | 16 | ||||
| -rw-r--r-- | indra/newview/llviewerwindow.cpp | 1 | ||||
| -rw-r--r-- | indra/newview/tests/llviewercontrollistener_test.cpp | 174 | ||||
| -rw-r--r-- | indra/test/test.cpp | 2 | 
19 files changed, 586 insertions, 188 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index ca8b5e946f..4dbf1282c4 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -29,6 +29,7 @@ include_directories(  #     ${LLCOMMON_LIBRARIES})  set(llcommon_SOURCE_FILES +    commoncontrol.cpp      indra_constants.cpp      llallocator.cpp      llallocator_heap_profile.cpp @@ -129,6 +130,7 @@ set(llcommon_HEADER_FILES      CMakeLists.txt      chrono.h +    commoncontrol.h      ctype_workaround.h      fix_macros.h      indra_constants.h diff --git a/indra/llcommon/commoncontrol.cpp b/indra/llcommon/commoncontrol.cpp new file mode 100644 index 0000000000..81e66baf8c --- /dev/null +++ b/indra/llcommon/commoncontrol.cpp @@ -0,0 +1,106 @@ +/** + * @file   commoncontrol.cpp + * @author Nat Goodspeed + * @date   2022-06-08 + * @brief  Implementation for commoncontrol. + *  + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "commoncontrol.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llevents.h" +#include "llsdutil.h" + +LLSD LL::CommonControl::access(const LLSD& params) +{ +    // We can't actually introduce a link-time dependency on llxml, or on any +    // global LLControlGroup (*koff* gSavedSettings *koff*) but we can issue a +    // runtime query. If we're running as part of a viewer with +    // LLViewerControlListener, we can use that to interact with any +    // instantiated LLControGroup. +    LLSD response; +    { +        LLEventStream reply("reply"); +        LLTempBoundListener connection = reply.listen("listener", +                     [&response] (const LLSD& event) +                     { +                         response = event; +                         return false; +                     }); +        LLSD rparams{ params }; +        rparams["reply"] = reply.getName(); +        LLEventPumps::instance().obtain("LLViewerControl").post(rparams); +    } +    // LLViewerControlListener responds immediately. If it's listening at all, +    // it will already have set response. +    if (! response.isDefined()) +    { +        LLTHROW(NoListener("No LLViewerControl listener instantiated")); +    } +    LLSD error{ response["error"] }; +    if (error.isDefined()) +    { +        LLTHROW(ParamError(error)); +    } +    response.erase("error"); +    response.erase("reqid"); +    return response; +} + +/// set control group.key to defined default value +LLSD LL::CommonControl::set_default(const std::string& group, const std::string& key) +{ +    return access(llsd::map("op", "set", +                            "group", group, "key", key))["value"]; +} + +/// set control group.key to specified value +LLSD LL::CommonControl::set(const std::string& group, const std::string& key, const LLSD& value) +{ +    return access(llsd::map("op", "set", +                            "group", group, "key", key, "value", value))["value"]; +} + +/// toggle boolean control group.key +LLSD LL::CommonControl::toggle(const std::string& group, const std::string& key) +{ +    return access(llsd::map("op", "toggle", +                            "group", group, "key", key))["value"]; +} + +/// get the definition for control group.key, (! isDefined()) if bad +/// ["name"], ["type"], ["value"], ["comment"] +LLSD LL::CommonControl::get_def(const std::string& group, const std::string& key) +{ +    return access(llsd::map("op", "get", +                            "group", group, "key", key)); +} + +/// get the value of control group.key +LLSD LL::CommonControl::get(const std::string& group, const std::string& key) +{ +    return access(llsd::map("op", "get", +                            "group", group, "key", key))["value"]; +} + +/// get defined groups +std::vector<std::string> LL::CommonControl::get_groups() +{ +    auto groups{ access(llsd::map("op", "groups"))["groups"] }; +    return { groups.beginArray(), groups.endArray() }; +} + +/// get definitions for all variables in group +LLSD LL::CommonControl::get_vars(const std::string& group) +{ +    return access(llsd::map("op", "vars", "group", group))["vars"]; +} diff --git a/indra/llcommon/commoncontrol.h b/indra/llcommon/commoncontrol.h new file mode 100644 index 0000000000..07d4a45ac5 --- /dev/null +++ b/indra/llcommon/commoncontrol.h @@ -0,0 +1,75 @@ +/** + * @file   commoncontrol.h + * @author Nat Goodspeed + * @date   2022-06-08 + * @brief  Access LLViewerControl LLEventAPI, if process has one. + *  + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_COMMONCONTROL_H) +#define LL_COMMONCONTROL_H + +#include <vector> +#include "llexception.h" +#include "llsd.h" + +namespace LL +{ +    class CommonControl +    { +    public: +        struct Error: public LLException +        { +            Error(const std::string& what): LLException(what) {} +        }; + +        /// Exception thrown if there's no LLViewerControl LLEventAPI +        struct NoListener: public Error +        { +            NoListener(const std::string& what): Error(what) {} +        }; + +        struct ParamError: public Error +        { +            ParamError(const std::string& what): Error(what) {} +        }; + +        /// set control group.key to defined default value +        static +        LLSD set_default(const std::string& group, const std::string& key); + +        /// set control group.key to specified value +        static +        LLSD set(const std::string& group, const std::string& key, const LLSD& value); + +        /// toggle boolean control group.key +        static +        LLSD toggle(const std::string& group, const std::string& key); + +        /// get the definition for control group.key, (! isDefined()) if bad +        /// ["name"], ["type"], ["value"], ["comment"] +        static +        LLSD get_def(const std::string& group, const std::string& key); + +        /// get the value of control group.key +        static +        LLSD get(const std::string& group, const std::string& key); + +        /// get defined groups +        static +        std::vector<std::string> get_groups(); + +        /// get definitions for all variables in group +        static +        LLSD get_vars(const std::string& group); + +    private: +        static +        LLSD access(const LLSD& params); +    }; +} // namespace LL + +#endif /* ! defined(LL_COMMONCONTROL_H) */ diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp index ba914035e2..f49dd40a8b 100644 --- a/indra/llcommon/threadpool.cpp +++ b/indra/llcommon/threadpool.cpp @@ -17,14 +17,17 @@  // std headers  // external library headers  // other Linden headers +#include "commoncontrol.h"  #include "llerror.h"  #include "llevents.h" +#include "llsd.h"  #include "stringize.h"  LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity): +    super(name),      mQueue(name, capacity),      mName("ThreadPool:" + name), -    mThreadCount(threads) +    mThreadCount(getConfiguredWidth(name, threads))  {}  void LL::ThreadPool::start() @@ -86,3 +89,58 @@ void LL::ThreadPool::run()  {      mQueue.runUntilClose();  } + +//static +size_t LL::ThreadPool::getConfiguredWidth(const std::string& name, size_t dft) +{ +    LLSD poolSizes; +    try +    { +        poolSizes = LL::CommonControl::get("Global", "ThreadPoolSizes"); +        // "ThreadPoolSizes" is actually a map containing the sizes of +        // interest -- or should be, if this process has an +        // LLViewerControlListener instance and its settings include +        // "ThreadPoolSizes". If we failed to retrieve it, perhaps we're in a +        // program that doesn't define that, or perhaps there's no such +        // setting, or perhaps we're asking too early, before the LLEventAPI +        // itself has been instantiated. In any of those cases, it seems worth +        // warning. +        if (! poolSizes.isDefined()) +        { +            // Note: we don't warn about absence of an override key for a +            // particular ThreadPool name, that's fine. This warning is about +            // complete absence of a ThreadPoolSizes setting, which we expect +            // in a normal viewer session. +            LL_WARNS("ThreadPool") << "No 'ThreadPoolSizes' setting for ThreadPool '" +                                   << name << "'" << LL_ENDL; +        } +    } +    catch (const LL::CommonControl::Error& exc) +    { +        // We don't want ThreadPool to *require* LLViewerControlListener. +        // Just log it and carry on. +        LL_WARNS("ThreadPool") << "Can't check 'ThreadPoolSizes': " << exc.what() << LL_ENDL; +    } + +    LL_DEBUGS("ThreadPool") << "ThreadPoolSizes = " << poolSizes << LL_ENDL; +    // LLSD treats an undefined value as an empty map when asked to retrieve a +    // key, so we don't need this to be conditional. +    LLSD sizeSpec{ poolSizes[name] }; +    // We retrieve sizeSpec as LLSD, rather than immediately as LLSD::Integer, +    // so we can distinguish the case when it's undefined. +    return sizeSpec.isInteger() ? sizeSpec.asInteger() : dft; +} + +//static +size_t LL::ThreadPool::getWidth(const std::string& name, size_t dft) +{ +    auto instance{ getInstance(name) }; +    if (instance) +    { +        return instance->getWidth(); +    } +    else +    { +        return getConfiguredWidth(name, dft); +    } +} diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index b79c9b9090..b49d511257 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -22,14 +22,25 @@  namespace LL  { -    class ThreadPool +    class ThreadPool: public LLInstanceTracker<ThreadPool, std::string>      { +    private: +        using super = LLInstanceTracker<ThreadPool, std::string>;      public:          /**           * Pass ThreadPool a string name. This can be used to look up the           * relevant WorkQueue. +         * +         * The number of threads you pass sets the compile-time default. But +         * if the user has overridden the LLSD map in the "ThreadPoolSizes" +         * setting with a key matching this ThreadPool name, that setting +         * overrides this parameter. +         * +         * Pass an explicit capacity to limit the size of the queue. +         * Constraining the queue can cause a submitter to block. Do not +         * constrain any ThreadPool accepting work from the main thread.           */ -        ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024); +        ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024*1024);          virtual ~ThreadPool();          /** @@ -57,6 +68,25 @@ namespace LL           */          virtual void run(); +        /** +         * getConfiguredWidth() returns the setting, if any, for the specified +         * ThreadPool name. Returns dft if the "ThreadPoolSizes" map does not +         * contain the specified name. +         */ +        static +        size_t getConfiguredWidth(const std::string& name, size_t dft=0); + +        /** +         * This getWidth() returns the width of the instantiated ThreadPool +         * with the specified name, if any. If no instance exists, returns its +         * getConfiguredWidth() if any. If there's no instance and no relevant +         * override, return dft. Presumably dft should match the threads +         * parameter passed to the ThreadPool constructor call that will +         * eventually instantiate the ThreadPool with that name. +         */ +        static +        size_t getWidth(const std::string& name, size_t dft); +      private:          void run(const std::string& name); diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp index d8503396d7..0093958e6d 100644 --- a/indra/llimage/llimageworker.cpp +++ b/indra/llimage/llimageworker.cpp @@ -28,44 +28,88 @@  #include "llimageworker.h"  #include "llimagedxt.h" +#include "threadpool.h" + +/*--------------------------------------------------------------------------*/ +class ImageRequest +{ +public: +	ImageRequest(const LLPointer<LLImageFormatted>& image, +				 S32 discard, BOOL needs_aux, +				 const LLPointer<LLImageDecodeThread::Responder>& responder); +	virtual ~ImageRequest(); + +	/*virtual*/ bool processRequest(); +	/*virtual*/ void finishRequest(bool completed); + +private: +	// LLPointers stored in ImageRequest MUST be LLPointer instances rather +	// than references: we need to increment the refcount when storing these. +	// input +	LLPointer<LLImageFormatted> mFormattedImage; +	S32 mDiscardLevel; +	BOOL mNeedsAux; +	// output +	LLPointer<LLImageRaw> mDecodedImageRaw; +	LLPointer<LLImageRaw> mDecodedImageAux; +	BOOL mDecodedRaw; +	BOOL mDecodedAux; +	LLPointer<LLImageDecodeThread::Responder> mResponder; +}; +  //----------------------------------------------------------------------------  // MAIN THREAD -LLImageDecodeThread::LLImageDecodeThread(bool threaded) -	: LLQueuedThread("imagedecode", threaded) +LLImageDecodeThread::LLImageDecodeThread(bool /*threaded*/)  { -	mCreationMutex = new LLMutex(); +    mThreadPool.reset(new LL::ThreadPool("ImageDecode", 8)); +    mThreadPool->start();  }  //virtual   LLImageDecodeThread::~LLImageDecodeThread() -{ -	delete mCreationMutex ; -} +{}  // MAIN THREAD  // virtual  S32 LLImageDecodeThread::update(F32 max_time_ms)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	S32 res = LLQueuedThread::update(max_time_ms); -	return res; +    return getPending();  } -LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image,  -	S32 discard, BOOL needs_aux, Responder* responder) +S32 LLImageDecodeThread::getPending()  { -    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	handle_t handle = generateHandle(); +    return mThreadPool->getQueue().size(); +} -    ImageRequest* req = new ImageRequest(handle, image, -        discard, needs_aux, -        responder); +LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage( +    const LLPointer<LLImageFormatted>& image,  +    S32 discard, +    BOOL needs_aux, +    const LLPointer<LLImageDecodeThread::Responder>& responder) +{ +    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -    addRequest(req); +    // Instantiate the ImageRequest right in the lambda, why not? +    mThreadPool->getQueue().post( +        [req = ImageRequest(image, discard, needs_aux, responder)] +        () mutable +        { +            auto done = req.processRequest(); +            req.finishRequest(done); +        }); + +    // It's important to our consumer (LLTextureFetchWorker) that we return a +    // nonzero handle. It is NOT important that the nonzero handle be unique: +    // nothing is ever done with it except to compare it to zero, or zero it. +    return 17; +} -	return handle; +void LLImageDecodeThread::shutdown() +{ +    mThreadPool->close();  }  LLImageDecodeThread::Responder::~Responder() @@ -74,11 +118,10 @@ LLImageDecodeThread::Responder::~Responder()  //---------------------------------------------------------------------------- -LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatted* image,  -												S32 discard, BOOL needs_aux, -												LLImageDecodeThread::Responder* responder) -	: LLQueuedThread::QueuedRequest(handle, FLAG_AUTO_COMPLETE), -	  mFormattedImage(image), +ImageRequest::ImageRequest(const LLPointer<LLImageFormatted>& image,  +							S32 discard, BOOL needs_aux, +							const LLPointer<LLImageDecodeThread::Responder>& responder) +	: mFormattedImage(image),  	  mDiscardLevel(discard),  	  mNeedsAux(needs_aux),  	  mDecodedRaw(FALSE), @@ -87,7 +130,7 @@ LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatte  {  } -LLImageDecodeThread::ImageRequest::~ImageRequest() +ImageRequest::~ImageRequest()  {  	mDecodedImageRaw = NULL;  	mDecodedImageAux = NULL; @@ -98,7 +141,7 @@ LLImageDecodeThread::ImageRequest::~ImageRequest()  // Returns true when done, whether or not decode was successful. -bool LLImageDecodeThread::ImageRequest::processRequest() +bool ImageRequest::processRequest()  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;  	const F32 decode_time_slice = 0.f; //disable time slicing @@ -125,7 +168,7 @@ bool LLImageDecodeThread::ImageRequest::processRequest()  											  mFormattedImage->getHeight(),  											  mFormattedImage->getComponents());  		} -		done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms +		done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice);  		// some decoders are removing data when task is complete and there were errors  		mDecodedRaw = done && mDecodedImageRaw->getData();  	} @@ -138,14 +181,14 @@ bool LLImageDecodeThread::ImageRequest::processRequest()  											  mFormattedImage->getHeight(),  											  1);  		} -		done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms +		done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4);  		mDecodedAux = done && mDecodedImageAux->getData();  	}  	return done;  } -void LLImageDecodeThread::ImageRequest::finishRequest(bool completed) +void ImageRequest::finishRequest(bool completed)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;  	if (mResponder.notNull()) @@ -155,10 +198,3 @@ void LLImageDecodeThread::ImageRequest::finishRequest(bool completed)  	}  	// Will automatically be deleted  } - -// Used by unit test only -// Checks that a responder exists for this instance so that something can happen when completion is reached -bool LLImageDecodeThread::ImageRequest::tut_isOK() -{ -	return mResponder.notNull(); -} diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h index e0a94d2841..18398d9ae2 100644 --- a/indra/llimage/llimageworker.h +++ b/indra/llimage/llimageworker.h @@ -29,9 +29,13 @@  #include "llimage.h"  #include "llpointer.h" -#include "llworkerthread.h" -class LLImageDecodeThread : public LLQueuedThread +namespace LL +{ +    class ThreadPool; +} // namespace LL + +class LLImageDecodeThread  {  public:  	class Responder : public LLThreadSafeRefCount @@ -42,57 +46,24 @@ public:  		virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) = 0;  	}; -	class ImageRequest : public LLQueuedThread::QueuedRequest -	{ -	protected: -		virtual ~ImageRequest(); // use deleteRequest() -		 -	public: -		ImageRequest(handle_t handle, LLImageFormatted* image, -					 S32 discard, BOOL needs_aux, -					 LLImageDecodeThread::Responder* responder); - -		/*virtual*/ bool processRequest(); -		/*virtual*/ void finishRequest(bool completed); - -		// Used by unit tests to check the consitency of the request instance -		bool tut_isOK(); -		 -	private: -		// input -		LLPointer<LLImageFormatted> mFormattedImage; -		S32 mDiscardLevel; -		BOOL mNeedsAux; -		// output -		LLPointer<LLImageRaw> mDecodedImageRaw; -		LLPointer<LLImageRaw> mDecodedImageAux; -		BOOL mDecodedRaw; -		BOOL mDecodedAux; -		LLPointer<LLImageDecodeThread::Responder> mResponder; -	}; -	  public:  	LLImageDecodeThread(bool threaded = true);  	virtual ~LLImageDecodeThread(); -	handle_t decodeImage(LLImageFormatted* image, +	// meant to resemble LLQueuedThread::handle_t +	typedef U32 handle_t; +	handle_t decodeImage(const LLPointer<LLImageFormatted>& image,  						 S32 discard, BOOL needs_aux, -						 Responder* responder); +						 const LLPointer<Responder>& responder); +	S32 getPending();  	S32 update(F32 max_time_ms); +	void shutdown();  private: -	struct creation_info -	{ -		handle_t handle; -		LLPointer<LLImageFormatted> image; -		S32 discard; -		BOOL needs_aux; -		LLPointer<Responder> responder; -		creation_info(handle_t h, LLImageFormatted* i, U32 p, S32 d, BOOL aux, Responder* r) -			: handle(h), image(i), discard(d), needs_aux(aux), responder(r) -		{} -	}; -	LLMutex* mCreationMutex; +	// As of SL-17483, LLImageDecodeThread is no longer itself an +	// LLQueuedThread - instead this is the API by which we submit work to the +	// "ImageDecode" ThreadPool. +	std::unique_ptr<LL::ThreadPool> mThreadPool;  };  #endif diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index d36d35aba4..0a97b739b0 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -125,42 +125,11 @@ namespace tut  		}  	}; -	// Test wrapper declaration : image worker -	// Note: this class is not meant to be instantiated outside an LLImageDecodeThread instance -	// but it's not a bad idea to get its public API a good shake as part of a thorough unit test set. -	// Some gotcha with the destructor though (see below). -	struct imagerequest_test -	{ -		// Instance to be tested -		LLImageDecodeThread::ImageRequest* mRequest; -		bool done; - -		// Constructor and destructor of the test wrapper -		imagerequest_test() -		{ -			done = false; - -			mRequest = new LLImageDecodeThread::ImageRequest(0, 0, -											 0, FALSE, -											 new responder_test(&done)); -		} -		~imagerequest_test() -		{ -			// We should delete the object *but*, because its destructor is protected, that cannot be -			// done from outside an LLImageDecodeThread instance... So we leak memory here... It's fine... -			//delete mRequest; -		} -	}; -  	// Tut templating thingamagic: test group, object and test instance  	typedef test_group<imagedecodethread_test> imagedecodethread_t;  	typedef imagedecodethread_t::object imagedecodethread_object_t;  	tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread"); -	typedef test_group<imagerequest_test> imagerequest_t; -	typedef imagerequest_t::object imagerequest_object_t; -	tut::imagerequest_t tut_imagerequest("LLImageRequest"); -  	// ---------------------------------------------------------------------------------------  	// Test functions  	// Notes: @@ -172,21 +141,6 @@ namespace tut  	// ---------------------------------------------------------------------------------------  	// Test the LLImageDecodeThread interface  	// --------------------------------------------------------------------------------------- -	// -	// Note on Unit Testing Queued Thread Classes -	// -	// Since methods on such a class are called on a separate loop and that we can't insert tut -	// ensure() calls in there, we exercise the class with 2 sets of tests: -	// - 1: Test as a single threaded instance: We declare the class but ask for no thread -	//   to be spawned (easy with LLThreads since there's a boolean argument on the constructor -	//   just for that). We can then unit test each public method like we do on a normal class. -	// - 2: Test as a threaded instance: We let the thread launch and check that its external  -	//   behavior is as expected (i.e. it runs, can accept a work order and processes -	//   it). Typically though there's no guarantee that this exercises all the methods of the -	//   class which is why we also need the previous "non threaded" set of unit tests for -	//   complete coverage. -	// -	// ---------------------------------------------------------------------------------------  	template<> template<>  	void imagedecodethread_object_t::test<1>() @@ -211,24 +165,4 @@ namespace tut  		// Verifies that the responder has now been called  		ensure("LLImageDecodeThread: threaded work unit not processed", done == true);  	} - -	// --------------------------------------------------------------------------------------- -	// Test the LLImageDecodeThread::ImageRequest interface -	// --------------------------------------------------------------------------------------- -	 -	template<> template<> -	void imagerequest_object_t::test<1>() -	{ -		// Test that we start with a correct request at creation -		ensure("LLImageDecodeThread::ImageRequest::ImageRequest() constructor test failed", mRequest->tut_isOK()); -		bool res = mRequest->processRequest(); -		// Verifies that we processed the request successfully -		ensure("LLImageDecodeThread::ImageRequest::processRequest() processing request test failed", res == true); -		// Check that we can call the finishing call safely -		try { -			mRequest->finishRequest(false); -		} catch (...) { -			fail("LLImageDecodeThread::ImageRequest::finishRequest() test failed"); -		} -	}  } diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 46f0e1d69e..ae10142e7a 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -2444,10 +2444,8 @@ void LLImageGL::checkActiveThread()  */    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() -    // because we're full. -    : ThreadPool("LLImageGL", 1, 1024*1024) +    // We want exactly one thread. +    : ThreadPool("LLImageGL", 1)      , mWindow(window)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index da79660239..20443988ab 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -65,7 +65,6 @@  #include <d3d9.h>  #include <dxgi1_4.h> -  // Require DirectInput version 8  #define DIRECTINPUT_VERSION 0x0800 @@ -4649,23 +4648,34 @@ void LLWindowWin32::LLWindowWin32Thread::updateVRAMUsage()          mDXGIAdapter->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &info);          // try to use no more than the available reserve minus 10% -        U32 target = info.AvailableForReservation / 1024 / 1024; -        target -= target / 10; +        U32 target = info.Budget / 1024 / 1024; + +        // EXPERIMENTAL +        // Trying to zero in on a good target usage, code here should be tuned against observed behavior +        // of various hardware. +        if (target > 4096)  // if 4GB are installed, try to leave 2GB free  +        { +            target -= 2048; +        } +        else // if less than 4GB are installed, try not to use more than half of it +        { +            target /= 2; +        }          U32 used_vram = info.CurrentUsage / 1024 / 1024;          mAvailableVRAM = used_vram < target ? target - used_vram : 0; -        /*LL_INFOS() << "\nLocal\nAFR: " << info.AvailableForReservation / 1024 / 1024 +        LL_INFOS("Window") << "\nLocal\nAFR: " << info.AvailableForReservation / 1024 / 1024              << "\nBudget: " << info.Budget / 1024 / 1024              << "\nCR: " << info.CurrentReservation / 1024 / 1024              << "\nCU: " << info.CurrentUsage / 1024 / 1024 << LL_ENDL;          mDXGIAdapter->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &info); -        LL_INFOS() << "\nNon-Local\nAFR: " << info.AvailableForReservation / 1024 / 1024 +        LL_INFOS("Window") << "\nNon-Local\nAFR: " << info.AvailableForReservation / 1024 / 1024              << "\nBudget: " << info.Budget / 1024 / 1024              << "\nCR: " << info.CurrentReservation / 1024 / 1024 -            << "\nCU: " << info.CurrentUsage / 1024 / 1024 << LL_ENDL;*/ +            << "\nCU: " << info.CurrentUsage / 1024 / 1024 << LL_ENDL;      }      else if (mD3DDevice != NULL)      { // fallback to D3D9 diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c253eca94e..f13ce85495 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2506,6 +2506,19 @@ if (LL_TESTS)      "${test_libs}"      ) +  set(llviewercontrollistener_test_sources +    llviewercontrollistener.cpp +    ../llxml/llcontrol.cpp +    ../llxml/llxmltree.cpp +    ../llxml/llxmlparser.cpp +    ../llcommon/commoncontrol.cpp +    ) + +  LL_ADD_INTEGRATION_TEST(llviewercontrollistener +    "${llviewercontrollistener_test_sources}" +    "${test_libs}" +    ) +    LL_ADD_INTEGRATION_TEST(llviewernetwork       llviewernetwork.cpp      "${test_libs}" diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 826f5ce030..09a7391e4e 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.6.0 +6.6.1 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 6426964190..bc4945eca5 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12645,7 +12645,9 @@        <key>Value</key>        <map>          <key>General</key> -        <integer>4</integer> +        <integer>1</integer> +        <key>ImageDecode</key> +        <integer>9</integer>        </map>      </map>      <key>ThrottleBandwidthKBPS</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 0b80d32ac3..55c0b31bf6 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1548,7 +1548,6 @@ bool LLAppViewer::doFrame()  			{  				S32 non_interactive_ms_sleep_time = 100;  				LLAppViewer::getTextureCache()->pause(); -				LLAppViewer::getImageDecodeThread()->pause();  				ms_sleep(non_interactive_ms_sleep_time);  			} @@ -1568,7 +1567,6 @@ bool LLAppViewer::doFrame()  					ms_sleep(milliseconds_to_sleep);  					// also pause worker threads during this wait period  					LLAppViewer::getTextureCache()->pause(); -					LLAppViewer::getImageDecodeThread()->pause();  				}  			} @@ -1617,7 +1615,6 @@ bool LLAppViewer::doFrame()  			{  				LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df getTextureCache" )  				LLAppViewer::getTextureCache()->pause(); -				LLAppViewer::getImageDecodeThread()->pause();  				LLAppViewer::getTextureFetch()->pause();  			}  			if(!total_io_pending) //pause file threads if nothing to process. @@ -2049,10 +2046,10 @@ bool LLAppViewer::cleanup()  	sTextureCache->shutdown();  	sImageDecodeThread->shutdown();  	sPurgeDiskCacheThread->shutdown(); -    if (mGeneralThreadPool) -    { -        mGeneralThreadPool->close(); -    } +	if (mGeneralThreadPool) +	{ +		mGeneralThreadPool->close(); +	}  	sTextureFetch->shutDownTextureCacheThread() ;  	sTextureFetch->shutDownImageDecodeThread() ; @@ -2173,14 +2170,7 @@ void LLAppViewer::initGeneralThread()          return;      } -    LLSD poolSizes{ gSavedSettings.getLLSD("ThreadPoolSizes") }; -    LLSD sizeSpec{ poolSizes["General"] }; -    LLSD::Integer poolSize{ sizeSpec.isInteger() ? sizeSpec.asInteger() : 3 }; -    LL_DEBUGS("ThreadPool") << "Instantiating General pool with " -        << poolSize << " threads" << LL_ENDL; -    // We don't want anyone, especially the main thread, to have to block -    // due to this ThreadPool being full. -    mGeneralThreadPool = new LL::ThreadPool("General", poolSize, 1024 * 1024); +    mGeneralThreadPool = new LL::ThreadPool("General", 3);      mGeneralThreadPool->start();  } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 0451bae3c9..604444b64a 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2113,10 +2113,10 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe  // Threads:  Tmain  void LLTextureFetchWorker::endWork(S32 param, bool aborted)  { -    LL_PROFILE_ZONE_SCOPED; +	LL_PROFILE_ZONE_SCOPED;  	if (mDecodeHandle != 0)  	{ -		mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false); +		// LL::ThreadPool has no operation to cancel a particular work item  		mDecodeHandle = 0;  	}  	mFormattedImage = NULL; @@ -3176,7 +3176,7 @@ void LLTextureFetch::shutDownImageDecodeThread()  {  	if(mImageDecodeThread)  	{ -		llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ; +		delete mImageDecodeThread;  		mImageDecodeThread = NULL ;  	}  } diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index a6e7b01df7..3584fffd44 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -803,14 +803,8 @@ void LLViewerTexture::addTextureStats(F32 virtual_size, BOOL needs_gltexture) co  	}  	virtual_size *= sTexelPixelRatio; -	/*if (!mMaxVirtualSizeResetCounter) -	{ -		//flag to reset the values because the old values are used. -		resetMaxVirtualSizeResetCounter(); -		mMaxVirtualSize = virtual_size; -		mNeedsGLTexture = needs_gltexture; -	} -	else*/ if (virtual_size > mMaxVirtualSize) + +	if (virtual_size > mMaxVirtualSize)  	{  		mMaxVirtualSize = virtual_size;  	} @@ -1801,6 +1795,12 @@ void LLViewerFetchedTexture::updateVirtualSize()          return;      } +    if (sDesiredDiscardBias > 0.f) +    { +        // running out of video memory, don't hold onto high res textures in the background +        mMaxVirtualSize = 0.f; +    } +  	for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)  	{				  		llassert(mNumFaces[ch] <= mFaceList[ch].size()); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index e27b5caab7..15f20d1d34 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -5476,7 +5476,6 @@ void LLViewerWindow::stopGL(BOOL save_state)  		// Pause texture decode threads (will get unpaused during main loop)  		LLAppViewer::getTextureCache()->pause(); -		LLAppViewer::getImageDecodeThread()->pause();  		LLAppViewer::getTextureFetch()->pause();  		gSky.destroyGL(); diff --git a/indra/newview/tests/llviewercontrollistener_test.cpp b/indra/newview/tests/llviewercontrollistener_test.cpp new file mode 100644 index 0000000000..6d100ef984 --- /dev/null +++ b/indra/newview/tests/llviewercontrollistener_test.cpp @@ -0,0 +1,174 @@ +/** + * @file   llviewercontrollistener_test.cpp + * @author Nat Goodspeed + * @date   2022-06-09 + * @brief  Test for llviewercontrollistener. + *  + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "llviewercontrollistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "../test/catch_and_store_what_in.h" // catch_what() +#include "commoncontrol.h" +#include "llcontrol.h"              // LLControlGroup +#include "llviewercontrollistener.h" + +/***************************************************************************** +*   TUT +*****************************************************************************/ +namespace tut +{ +    void ensure_contains(const std::string& msg, const std::string& substr) +    { +        ensure_contains("Exception does not contain " + substr, msg, substr); +    } + +    struct llviewercontrollistener_data +    { +        LLControlGroup Global{"FakeGlobal"}; + +        llviewercontrollistener_data() +        { +            Global.declareString("strvar", "woof", "string variable"); +            // together we will stroll the boolvar, ma cherie +            Global.declareBOOL("boolvar",  TRUE, "bool variable"); +        } +    }; +    typedef test_group<llviewercontrollistener_data> llviewercontrollistener_group; +    typedef llviewercontrollistener_group::object object; +    llviewercontrollistener_group llviewercontrollistenergrp("llviewercontrollistener"); + +    template<> template<> +    void object::test<1>() +    { +        set_test_name("CommonControl no listener"); +        // Not implemented: the linker drags in LLViewerControlListener when +        // we bring in LLViewerControl. +    } + +    template<> template<> +    void object::test<2>() +    { +        set_test_name("CommonControl bad group"); +        std::string threw{ catch_what<LL::CommonControl::ParamError>( +                [](){ LL::CommonControl::get("Nonexistent", "Variable"); }) }; +        ensure_contains(threw, "group"); +        ensure_contains(threw, "Nonexistent"); +    } + +    template<> template<> +    void object::test<3>() +    { +        set_test_name("CommonControl bad variable"); +        std::string threw{ catch_what<LL::CommonControl::ParamError>( +                [](){ LL::CommonControl::get("FakeGlobal", "Nonexistent"); }) }; +        ensure_contains(threw, "key"); +        ensure_contains(threw, "Nonexistent"); +    } + +    template<> template<> +    void object::test<4>() +    { +        set_test_name("CommonControl toggle string"); +        std::string threw{ catch_what<LL::CommonControl::ParamError>( +                [](){ LL::CommonControl::toggle("FakeGlobal", "strvar"); }) }; +        ensure_contains(threw, "non-boolean"); +        ensure_contains(threw, "strvar"); +    } + +    template<> template<> +    void object::test<5>() +    { +        set_test_name("CommonControl list bad group"); +        std::string threw{ catch_what<LL::CommonControl::ParamError>( +                [](){ LL::CommonControl::get_vars("Nonexistent"); }) }; +        ensure_contains(threw, "group"); +        ensure_contains(threw, "Nonexistent"); +    } + +    template<> template<> +    void object::test<6>() +    { +        set_test_name("CommonControl get"); +        auto strvar{ LL::CommonControl::get("FakeGlobal", "strvar") }; +        ensure_equals(strvar, "woof"); +        auto boolvar{ LL::CommonControl::get("FakeGlobal", "boolvar") }; +        ensure(boolvar); +    } + +    template<> template<> +    void object::test<7>() +    { +        set_test_name("CommonControl set, set_default, toggle"); + +        std::string newstr{ LL::CommonControl::set("FakeGlobal", "strvar", "mouse").asString() }; +        ensure_equals(newstr, "mouse"); +        ensure_equals(LL::CommonControl::get("FakeGlobal", "strvar").asString(), "mouse"); +        ensure_equals(LL::CommonControl::set_default("FakeGlobal", "strvar").asString(), "woof"); + +        bool newbool{ LL::CommonControl::set("FakeGlobal", "boolvar", false) }; +        ensure(! newbool); +        ensure(! LL::CommonControl::get("FakeGlobal", "boolvar").asBoolean()); +        ensure(LL::CommonControl::set_default("FakeGlobal", "boolvar").asBoolean()); +        ensure(! LL::CommonControl::toggle("FakeGlobal", "boolvar").asBoolean()); +    } + +    template<> template<> +    void object::test<8>() +    { +        set_test_name("CommonControl get_def"); +        LLSD def{ LL::CommonControl::get_def("FakeGlobal", "strvar") }; +        ensure_equals( +            def, +            llsd::map("name", "strvar", +                      "type", "String", +                      "value", "woof", +                      "comment", "string variable")); +    } + +    template<> template<> +    void object::test<9>() +    { +        set_test_name("CommonControl get_groups"); +        std::vector<std::string> groups{ LL::CommonControl::get_groups() }; +        ensure_equals(groups.size(), 1); +        ensure_equals(groups[0], "FakeGlobal"); +    } + +    template<> template<> +    void object::test<10>() +    { +        set_test_name("CommonControl get_vars"); +        LLSD vars{ LL::CommonControl::get_vars("FakeGlobal") }; +        // convert from array (unpredictable order) to map +        LLSD varsmap{ LLSD::emptyMap() }; +        for (auto& var : llsd::inArray(vars)) +        { +            varsmap[var["name"].asString()] = var; +        } +        // comparing maps is order-insensitive +        ensure_equals( +            varsmap, +            llsd::map( +                "strvar", +                llsd::map("name", "strvar", +                          "type", "String", +                          "value", "woof", +                          "comment", "string variable"), +                "boolvar", +                llsd::map("name", "boolvar", +                          "type", "Boolean", +                          "value", TRUE, +                          "comment", "bool variable"))); +    } +} // namespace tut diff --git a/indra/test/test.cpp b/indra/test/test.cpp index bb48216b2b..28f25087ac 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -401,7 +401,7 @@ public:  	{  		// Per http://confluence.jetbrains.net/display/TCD65/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ServiceMessages  		std::string result; -		BOOST_FOREACH(char c, str) +		for (char c : str)  		{  			switch (c)  			{ | 
