Merge branch 'DRTVWR-563' of https://bitbucket.org/lindenlab/viewer
commit
8ac67e5476
|
|
@ -29,6 +29,7 @@ include_directories( SYSTEM
|
|||
# ${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
|
||||
|
|
|
|||
|
|
@ -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"];
|
||||
}
|
||||
|
|
@ -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) */
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
# include <sys/types.h>
|
||||
# include <mach/task.h>
|
||||
# include <mach/mach_init.h>
|
||||
#include <mach/mach_host.h>
|
||||
#elif LL_LINUX
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
|
@ -109,6 +110,50 @@ void LLMemory::updateMemoryInfo()
|
|||
{
|
||||
sAvailPhysicalMemInKB = U32Kilobytes(0);
|
||||
}
|
||||
|
||||
#elif defined(LL_DARWIN)
|
||||
task_vm_info info;
|
||||
mach_msg_type_number_t infoCount = TASK_VM_INFO_COUNT;
|
||||
// MACH_TASK_BASIC_INFO reports the same resident_size, but does not tell us the reusable bytes or phys_footprint.
|
||||
if (task_info(mach_task_self(), TASK_VM_INFO, reinterpret_cast<task_info_t>(&info), &infoCount) == KERN_SUCCESS)
|
||||
{
|
||||
// Our Windows definition of PagefileUsage is documented by Microsoft as "the total amount of
|
||||
// memory that the memory manager has committed for a running process", which is rss.
|
||||
sAllocatedPageSizeInKB = U32Bytes(info.resident_size);
|
||||
|
||||
// Activity Monitor => Inspect Process => Real Memory Size appears to report resident_size
|
||||
// Activity monitor => main window memory column appears to report phys_footprint, which spot checks as at least 30% less.
|
||||
// I think that is because of compression, which isn't going to give us a consistent measurement. We want uncompressed totals.
|
||||
//
|
||||
// In between is resident_size - reusable. This is what Chrome source code uses, with source comments saying it is 'the "Real Memory" value
|
||||
// reported for the app by the Memory Monitor in Instruments.' It is still about 8% bigger than phys_footprint.
|
||||
//
|
||||
// (On Windows, we use WorkingSetSize.)
|
||||
sAllocatedMemInKB = U32Bytes(info.resident_size - info.reusable);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "task_info failed" << LL_ENDL;
|
||||
}
|
||||
|
||||
// Total installed and available physical memory are properties of the host, not just our process.
|
||||
vm_statistics64_data_t vmstat;
|
||||
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
|
||||
mach_port_t host = mach_host_self();
|
||||
vm_size_t page_size;
|
||||
host_page_size(host, &page_size);
|
||||
kern_return_t result = host_statistics64(host, HOST_VM_INFO64, reinterpret_cast<host_info_t>(&vmstat), &count);
|
||||
if (result == KERN_SUCCESS) {
|
||||
// This is what Chrome reports as 'the "Physical Memory Free" value reported by the Memory Monitor in Instruments.'
|
||||
// Note though that inactive pages are not included here and not yet free, but could become so under memory pressure.
|
||||
sAvailPhysicalMemInKB = U32Bytes(vmstat.free_count * page_size);
|
||||
sMaxPhysicalMemInKB = LLMemoryInfo::getHardwareMemSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "task_info failed" << LL_ENDL;
|
||||
}
|
||||
|
||||
#else
|
||||
//not valid for other systems for now.
|
||||
sAllocatedMemInKB = U64Bytes(LLMemory::getCurrentRSS());
|
||||
|
|
|
|||
|
|
@ -695,20 +695,28 @@ static U32Kilobytes LLMemoryAdjustKBResult(U32Kilobytes inKB)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN
|
||||
// static
|
||||
U32Kilobytes LLMemoryInfo::getHardwareMemSize()
|
||||
{
|
||||
// This might work on Linux as well. Someone check...
|
||||
uint64_t phys = 0;
|
||||
int mib[2] = { CTL_HW, HW_MEMSIZE };
|
||||
|
||||
size_t len = sizeof(phys);
|
||||
sysctl(mib, 2, &phys, &len, NULL, 0);
|
||||
|
||||
return U64Bytes(phys);
|
||||
}
|
||||
#endif
|
||||
|
||||
U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
return LLMemoryAdjustKBResult(U32Kilobytes(mStatsMap["Total Physical KB"].asInteger()));
|
||||
|
||||
#elif LL_DARWIN
|
||||
// This might work on Linux as well. Someone check...
|
||||
uint64_t phys = 0;
|
||||
int mib[2] = { CTL_HW, HW_MEMSIZE };
|
||||
|
||||
size_t len = sizeof(phys);
|
||||
sysctl(mib, 2, &phys, &len, NULL, 0);
|
||||
|
||||
return U64Bytes(phys);
|
||||
return getHardwareMemSize();
|
||||
|
||||
#elif LL_LINUX
|
||||
U64 phys = 0;
|
||||
|
|
|
|||
|
|
@ -113,7 +113,10 @@ public:
|
|||
LLMemoryInfo(); ///< Default constructor
|
||||
void stream(std::ostream& s) const; ///< output text info to s
|
||||
|
||||
U32Kilobytes getPhysicalMemoryKB() const;
|
||||
U32Kilobytes getPhysicalMemoryKB() const;
|
||||
#if LL_DARWIN
|
||||
static U32Kilobytes getHardwareMemSize(); // Because some Mac linkers won't let us reference extern gSysMemory from a different lib.
|
||||
#endif
|
||||
|
||||
//get the available memory infomation in KiloBytes.
|
||||
static void getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
return mThreadPool->getQueue().size();
|
||||
}
|
||||
|
||||
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;
|
||||
handle_t handle = generateHandle();
|
||||
|
||||
ImageRequest* req = new ImageRequest(handle, image,
|
||||
discard, needs_aux,
|
||||
responder);
|
||||
// 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);
|
||||
});
|
||||
|
||||
addRequest(req);
|
||||
// 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
|
||||
|
|
@ -127,9 +170,9 @@ bool LLImageDecodeThread::ImageRequest::processRequest()
|
|||
}
|
||||
|
||||
// <FS:ND> Probably out of memory crash
|
||||
// done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms
|
||||
// done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice);
|
||||
if( mDecodedImageRaw->getData() )
|
||||
done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms
|
||||
done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice);
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "No memory for LLImageRaw of size " << (U32)mFormattedImage->getWidth() << "x" << (U32)mFormattedImage->getHeight() << "x"
|
||||
|
|
@ -150,14 +193,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())
|
||||
|
|
@ -167,10 +210,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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2447,10 +2447,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;
|
||||
|
|
|
|||
|
|
@ -141,7 +141,12 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
CGLError the_err = CGLQueryRendererInfo (CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers);
|
||||
if(0 == the_err)
|
||||
{
|
||||
CGLDescribeRenderer (info, 0, kCGLRPTextureMemoryMegabytes, &vram_megabytes);
|
||||
// The name, uses, and other platform definitions of gGLManager.mVRAM suggest that this is supposed to be total vram in MB,
|
||||
// rather than, say, just the texture memory. The two exceptions are:
|
||||
// 1. LLAppViewer::getViewerInfo() puts the value in a field labeled "TEXTURE_MEMORY"
|
||||
// 2. For years, this present function used kCGLRPTextureMemoryMegabytes
|
||||
// Now we use kCGLRPVideoMemoryMegabytes to bring it in line with everything else (except thatone label).
|
||||
CGLDescribeRenderer (info, 0, kCGLRPVideoMemoryMegabytes, &vram_megabytes);
|
||||
CGLDestroyRendererInfo (info);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1232,6 +1232,16 @@ F32 LLWindowMacOSX::getPixelAspectRatio()
|
|||
return 1.f;
|
||||
}
|
||||
|
||||
U32 LLWindowMacOSX::getAvailableVRAMMegabytes() {
|
||||
// MTL (and MoltenVK) has some additional gpu data, such as recommendedMaxWorkingSetSize and currentAllocatedSize.
|
||||
// But these are not available for OpenGL and/or our current mimimum OS version.
|
||||
// So we will estimate.
|
||||
static const U32 mb = 1024*1024;
|
||||
// We're asked for total available gpu memory, but we only have allocation info on texture usage. So estimate by doubling that.
|
||||
static const U32 total_factor = 2; // estimated total/textures
|
||||
return gGLManager.mVRAM - (LLImageGL::getTextureBytesAllocated() * total_factor/mb);
|
||||
}
|
||||
|
||||
//static SInt32 oldWindowLevel;
|
||||
|
||||
// MBW -- XXX -- There's got to be a better way than this. Find it, please...
|
||||
|
|
|
|||
|
|
@ -101,8 +101,7 @@ public:
|
|||
void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; }
|
||||
|
||||
// query VRAM usage
|
||||
// FIXME FIXME
|
||||
virtual U32 getAvailableVRAMMegabytes() override { return 0; }
|
||||
/*virtual*/ U32 getAvailableVRAMMegabytes() override;
|
||||
|
||||
void beforeDialog() override;
|
||||
void afterDialog() override;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@
|
|||
#include <d3d9.h>
|
||||
#include <dxgi1_4.h>
|
||||
|
||||
|
||||
// Require DirectInput version 8
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
|
||||
|
|
@ -4763,23 +4762,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
|
||||
|
|
|
|||
|
|
@ -3035,6 +3035,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}"
|
||||
|
|
|
|||
|
|
@ -15883,7 +15883,9 @@ Change of this parameter will affect the layout of buttons in notification toast
|
|||
<key>Value</key>
|
||||
<map>
|
||||
<key>General</key>
|
||||
<integer>4</integer>
|
||||
<integer>1</integer>
|
||||
<key>ImageDecode</key>
|
||||
<integer>9</integer>
|
||||
</map>
|
||||
</map>
|
||||
<key>ThrottleBandwidthKBPS</key>
|
||||
|
|
|
|||
|
|
@ -1821,7 +1821,6 @@ bool LLAppViewer::doFrame()
|
|||
{
|
||||
S32 non_interactive_ms_sleep_time = 100;
|
||||
LLAppViewer::getTextureCache()->pause();
|
||||
LLAppViewer::getImageDecodeThread()->pause();
|
||||
ms_sleep(non_interactive_ms_sleep_time);
|
||||
}
|
||||
|
||||
|
|
@ -1842,7 +1841,6 @@ bool LLAppViewer::doFrame()
|
|||
ms_sleep(milliseconds_to_sleep);
|
||||
// also pause worker threads during this wait period
|
||||
LLAppViewer::getTextureCache()->pause();
|
||||
LLAppViewer::getImageDecodeThread()->pause();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1896,7 +1894,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.
|
||||
|
|
@ -2202,8 +2199,6 @@ bool LLAppViewer::cleanup()
|
|||
|
||||
LL_INFOS() << "Cache files removed" << LL_ENDL;
|
||||
|
||||
// Wait for any pending LFS IO
|
||||
flushLFSIO();
|
||||
LL_INFOS() << "Shutting down Views" << LL_ENDL;
|
||||
|
||||
// Destroy the UI
|
||||
|
|
@ -2441,13 +2436,14 @@ bool LLAppViewer::cleanup()
|
|||
sTextureCache->shutdown();
|
||||
sImageDecodeThread->shutdown();
|
||||
sPurgeDiskCacheThread->shutdown();
|
||||
if (mGeneralThreadPool)
|
||||
{
|
||||
mGeneralThreadPool->close();
|
||||
}
|
||||
if (mGeneralThreadPool)
|
||||
{
|
||||
mGeneralThreadPool->close();
|
||||
}
|
||||
|
||||
sTextureFetch->shutDownTextureCacheThread() ;
|
||||
sTextureFetch->shutDownImageDecodeThread() ;
|
||||
LLLFSThread::sLocal->shutdown();
|
||||
|
||||
LL_INFOS() << "Shutting down message system" << LL_ENDL;
|
||||
end_messaging_system();
|
||||
|
|
@ -2564,14 +2560,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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1073,7 +1073,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size)
|
|||
// Locks: Mw
|
||||
void LLTextureFetchWorker::setImagePriority(F32 priority)
|
||||
{
|
||||
mImagePriority = priority; //should map to max virtual size
|
||||
mImagePriority = priority; //should map to max virtual size, abort if zero
|
||||
}
|
||||
|
||||
// Locks: Mw
|
||||
|
|
@ -1369,7 +1369,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
|
|||
{
|
||||
if (mFTType != FTT_DEFAULT)
|
||||
{
|
||||
LL_WARNS(LOG_TXT) << "trying to seek a non-default texture on the sim. Bad!" << LL_ENDL;
|
||||
LL_WARNS(LOG_TXT) << "Trying to fetch a texture of non-default type by UUID. This probably won't work!" << LL_ENDL;
|
||||
}
|
||||
setUrl(http_url + "/?texture_id=" + mID.asString().c_str());
|
||||
LL_DEBUGS(LOG_TXT) << "Texture URL: " << mUrl << LL_ENDL;
|
||||
|
|
@ -1418,7 +1418,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
|
|||
else
|
||||
{
|
||||
// Shouldn't need to do anything here
|
||||
llassert(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
|
||||
//llassert(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1692,7 +1692,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
|
|||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
if (mFTType != FTT_SERVER_BAKE)
|
||||
if (mFTType != FTT_SERVER_BAKE && mFTType != FTT_MAP_TILE)
|
||||
{
|
||||
mUrl.clear();
|
||||
}
|
||||
|
|
@ -2134,10 +2134,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;
|
||||
|
|
@ -2716,6 +2716,11 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const
|
|||
return false; // need to wait for previous aborted request to complete
|
||||
}
|
||||
worker->lockWorkMutex(); // +Mw
|
||||
if (worker->mState == LLTextureFetchWorker::DONE && worker->mDesiredSize == llmax(desired_size, TEXTURE_CACHE_ENTRY_SIZE) && worker->mDesiredDiscard == desired_discard) {
|
||||
worker->unlockWorkMutex(); // -Mw
|
||||
|
||||
return false; // similar request has failed or is in a transitional state
|
||||
}
|
||||
worker->mActiveCount++;
|
||||
worker->mNeedsAux = needs_aux;
|
||||
worker->setImagePriority(priority);
|
||||
|
|
@ -3032,16 +3037,18 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
|
|||
bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED;
|
||||
bool res = false;
|
||||
LLTextureFetchWorker* worker = getWorker(id);
|
||||
if (worker)
|
||||
{
|
||||
worker->lockWorkMutex(); // +Mw
|
||||
worker->setImagePriority(priority);
|
||||
worker->unlockWorkMutex(); // -Mw
|
||||
res = true;
|
||||
}
|
||||
return res;
|
||||
mRequestQueue.tryPost([=]()
|
||||
{
|
||||
LLTextureFetchWorker* worker = getWorker(id);
|
||||
if (worker)
|
||||
{
|
||||
worker->lockWorkMutex(); // +Mw
|
||||
worker->setImagePriority(priority);
|
||||
worker->unlockWorkMutex(); // -Mw
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replicates and expands upon the base class's
|
||||
|
|
@ -3190,7 +3197,7 @@ void LLTextureFetch::shutDownImageDecodeThread()
|
|||
{
|
||||
if(mImageDecodeThread)
|
||||
{
|
||||
llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ;
|
||||
delete mImageDecodeThread;
|
||||
mImageDecodeThread = NULL ;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -553,7 +553,7 @@ void LLViewerTexture::getGPUMemoryForTextures(S32Megabytes &gpu, S32Megabytes &p
|
|||
{
|
||||
gpu_res = (S32Megabytes)gViewerWindow->getWindow()->getAvailableVRAMMegabytes();
|
||||
|
||||
//check main memory, only works for windows.
|
||||
//check main memory, only works for windows and macos.
|
||||
LLMemory::updateMemoryInfo();
|
||||
physical_res = LLMemory::getAvailableMemKB();
|
||||
|
||||
|
|
@ -685,6 +685,11 @@ void LLViewerTexture::cleanup()
|
|||
{
|
||||
notifyAboutMissingAsset();
|
||||
|
||||
if (LLAppViewer::getTextureFetch())
|
||||
{
|
||||
LLAppViewer::getTextureFetch()->updateRequestPriority(mID, 0.f);
|
||||
}
|
||||
|
||||
mFaceList[LLRender::DIFFUSE_MAP].clear();
|
||||
mFaceList[LLRender::NORMAL_MAP].clear();
|
||||
mFaceList[LLRender::SPECULAR_MAP].clear();
|
||||
|
|
@ -830,14 +835,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;
|
||||
}
|
||||
|
|
@ -1881,6 +1880,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());
|
||||
|
|
@ -1969,6 +1974,16 @@ bool LLViewerFetchedTexture::isActiveFetching()
|
|||
return mFetchState > 7 && mFetchState < 10 && monitor_enabled; //in state of WAIT_HTTP_REQ or DECODE_IMAGE.
|
||||
}
|
||||
|
||||
void LLViewerFetchedTexture::setBoostLevel(S32 level)
|
||||
{
|
||||
LLViewerTexture::setBoostLevel(level);
|
||||
|
||||
if (level >= LLViewerTexture::BOOST_HIGH)
|
||||
{
|
||||
mDesiredDiscardLevel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool LLViewerFetchedTexture::updateFetch()
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
|
||||
|
|
@ -2016,6 +2031,11 @@ bool LLViewerFetchedTexture::updateFetch()
|
|||
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - in fast cache");
|
||||
return false;
|
||||
}
|
||||
if (mGLTexturep.isNull())
|
||||
{ // fix for crash inside getCurrentDiscardLevelForFetching (shouldn't happen but appears to be happening)
|
||||
llassert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
S32 current_discard = getCurrentDiscardLevelForFetching();
|
||||
S32 desired_discard = getDesiredDiscardLevel();
|
||||
|
|
@ -2284,8 +2304,11 @@ bool LLViewerFetchedTexture::updateFetch()
|
|||
mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP);
|
||||
}
|
||||
|
||||
// if createRequest() failed, we're finishing up a request for this UUID,
|
||||
// wait for it to complete
|
||||
// If createRequest() failed, that means one of two things:
|
||||
// 1. We're finishing up a request for this UUID, so we
|
||||
// should wait for it to complete
|
||||
// 2. We've failed a request for this UUID, so there is
|
||||
// no need to create another request
|
||||
}
|
||||
else if (mHasFetcher && !mIsFetching)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ public:
|
|||
/*virtual*/ bool isActiveFetching();
|
||||
|
||||
/*virtual*/ const LLUUID& getID() const { return mID; }
|
||||
void setBoostLevel(S32 level);
|
||||
virtual void setBoostLevel(S32 level);
|
||||
S32 getBoostLevel() { return mBoostLevel; }
|
||||
void setTextureListType(S32 tex_type) { mTextureListType = tex_type; }
|
||||
S32 getTextureListType() { return mTextureListType; }
|
||||
|
|
@ -334,10 +334,10 @@ public:
|
|||
};
|
||||
|
||||
public:
|
||||
/*virtual*/ S8 getType() const ;
|
||||
/*virtual*/ S8 getType() const override;
|
||||
FTType getFTType() const;
|
||||
/*virtual*/ void forceImmediateUpdate() ;
|
||||
/*virtual*/ void dump() ;
|
||||
/*virtual*/ void forceImmediateUpdate() override;
|
||||
/*virtual*/ void dump() override;
|
||||
|
||||
// Set callbacks to get called when the image gets updated with higher
|
||||
// resolution versions.
|
||||
|
|
@ -375,6 +375,7 @@ public:
|
|||
S32 getDesiredDiscardLevel() { return mDesiredDiscardLevel; }
|
||||
void setMinDiscardLevel(S32 discard) { mMinDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel,(S8)discard); }
|
||||
|
||||
void setBoostLevel(S32 level) override;
|
||||
bool updateFetch();
|
||||
bool setDebugFetching(S32 debug_level);
|
||||
bool isInDebug() const { return mInDebug; }
|
||||
|
|
@ -387,14 +388,14 @@ public:
|
|||
// Override the computation of discard levels if we know the exact output
|
||||
// size of the image. Used for UI textures to not decode, even if we have
|
||||
// more data.
|
||||
/*virtual*/ void setKnownDrawSize(S32 width, S32 height);
|
||||
/*virtual*/ void setKnownDrawSize(S32 width, S32 height) override;
|
||||
|
||||
// Set the debug text of all Viewer Objects associated with this texture
|
||||
// to the specified text
|
||||
void setDebugText(const std::string& text);
|
||||
|
||||
void setIsMissingAsset(BOOL is_missing = true);
|
||||
/*virtual*/ BOOL isMissingAsset() const { return mIsMissingAsset; }
|
||||
/*virtual*/ BOOL isMissingAsset() const override { return mIsMissingAsset; }
|
||||
|
||||
// returns dimensions of original image for local files (before power of two scaling)
|
||||
// and returns 0 for all asset system images
|
||||
|
|
@ -437,7 +438,7 @@ public:
|
|||
BOOL isRawImageValid()const { return mIsRawImageValid ; }
|
||||
void forceToSaveRawImage(S32 desired_discard = 0, F32 kept_time = 0.f) ;
|
||||
void forceToRefetchTexture(S32 desired_discard = 0, F32 kept_time = 60.f);
|
||||
/*virtual*/ void setCachedRawImage(S32 discard_level, LLImageRaw* imageraw) ;
|
||||
/*virtual*/ void setCachedRawImage(S32 discard_level, LLImageRaw* imageraw) override;
|
||||
void destroySavedRawImage() ;
|
||||
LLImageRaw* getSavedRawImage() ;
|
||||
BOOL hasSavedRawImage() const ;
|
||||
|
|
@ -453,14 +454,14 @@ public:
|
|||
void setInFastCacheList(bool in_list) { mInFastCacheList = in_list; }
|
||||
bool isInFastCacheList() { return mInFastCacheList; }
|
||||
|
||||
/*virtual*/bool isActiveFetching(); //is actively in fetching by the fetching pipeline.
|
||||
/*virtual*/bool isActiveFetching() override; //is actively in fetching by the fetching pipeline.
|
||||
|
||||
// <FS:Techwolf Lupindo> texture comment decoder
|
||||
std::map<std::string,std::string> mComment;
|
||||
// </FS:Techwolf Lupindo>
|
||||
|
||||
protected:
|
||||
/*virtual*/ void switchToCachedImage();
|
||||
/*virtual*/ void switchToCachedImage() override;
|
||||
S32 getCurrentDiscardLevelForFetching() ;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -1037,7 +1037,7 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
|
|||
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
|
||||
LLTimer image_op_timer;
|
||||
|
||||
typedef std::vector<LLViewerFetchedTexture*> entries_list_t;
|
||||
typedef std::vector<LLPointer<LLViewerFetchedTexture> > entries_list_t;
|
||||
entries_list_t entries;
|
||||
|
||||
// update N textures at beginning of mImageList
|
||||
|
|
@ -1069,10 +1069,13 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
|
|||
}
|
||||
}
|
||||
|
||||
for (auto* imagep : entries)
|
||||
for (auto& imagep : entries)
|
||||
{
|
||||
updateImageDecodePriority(imagep);
|
||||
imagep->updateFetch();
|
||||
if (imagep->getNumRefs() > 1) // make sure this image hasn't been deleted before attempting to update (may happen as a side effect of some other image updating)
|
||||
{
|
||||
updateImageDecodePriority(imagep);
|
||||
imagep->updateFetch();
|
||||
}
|
||||
}
|
||||
|
||||
if (entries.size() > 0)
|
||||
|
|
|
|||
|
|
@ -6716,7 +6716,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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -406,7 +406,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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue