summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/commoncontrol.cpp106
-rw-r--r--indra/llcommon/commoncontrol.h75
-rw-r--r--indra/llcommon/threadpool.cpp60
-rw-r--r--indra/llcommon/threadpool.h34
-rw-r--r--indra/llimage/llimageworker.cpp104
-rw-r--r--indra/llimage/llimageworker.h61
-rw-r--r--indra/llimage/tests/llimageworker_test.cpp66
-rw-r--r--indra/llrender/llimagegl.cpp6
-rw-r--r--indra/llwindow/llwindowwin32.cpp22
-rw-r--r--indra/newview/CMakeLists.txt13
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/settings.xml4
-rw-r--r--indra/newview/llappviewer.cpp20
-rw-r--r--indra/newview/lltexturefetch.cpp6
-rw-r--r--indra/newview/llviewertexture.cpp16
-rw-r--r--indra/newview/llviewerwindow.cpp1
-rw-r--r--indra/newview/tests/llviewercontrollistener_test.cpp174
-rw-r--r--indra/test/test.cpp2
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)
{