diff --git a/autobuild.xml b/autobuild.xml
old mode 100755
new mode 100644
index f5d46ce929..92a4ca5528
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -3329,14 +3329,6 @@
arguments
..\indra
- &&
- ..\indra\tools\vstool\VSTool.exe
- --solution
- SecondLife.sln
- --config
- RelWithDebInfo
- --startup
- secondlife-bin
options
@@ -3372,20 +3364,11 @@
arguments
..\indra
- &&
- ..\indra\tools\vstool\VSTool.exe
- --solution
- SecondLife.sln
- --config
- RelWithDebInfo
- --startup
- secondlife-bin
options
-G
"Visual Studio 12"
- -DUNATTENDED:BOOL=ON
-DINSTALL_PROPRIETARY=FALSE
-DUSE_KDU=FALSE
@@ -3414,14 +3397,6 @@
arguments
..\indra
- &&
- ..\indra\tools\vstool\VSTool.exe
- --solution
- SecondLife.sln
- --config
- Release
- --startup
- secondlife-bin
options
@@ -3453,20 +3428,11 @@
arguments
..\indra
- &&
- ..\indra\tools\vstool\VSTool.exe
- --solution
- SecondLife.sln
- --config
- Release
- --startup
- secondlife-bin
options
-G
"Visual Studio 12"
- -DUNATTENDED:BOOL=ON
-DINSTALL_PROPRIETARY=FALSE
-DUSE_KDU=FALSE
diff --git a/build.sh b/build.sh
index 185b7c9592..bed79302e4 100755
--- a/build.sh
+++ b/build.sh
@@ -97,6 +97,7 @@ pre_build()
"$autobuild" configure --quiet -c $variant -- \
-DPACKAGE:BOOL=ON \
+ -DUNATTENDED:BOOL=ON \
-DRELEASE_CRASH_REPORTING:BOOL=ON \
-DVIEWER_CHANNEL:STRING="\"$viewer_channel\"" \
-DGRID:STRING="\"$viewer_grid\"" \
diff --git a/doc/contributions.txt b/doc/contributions.txt
index cedc723395..6e7d734bd4 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -201,6 +201,7 @@ Ansariel Hiller
STORM-2133
MAINT-6511
MAINT-6612
+ MAINT-6637
Aralara Rajal
Arare Chantilly
CHUIBUG-191
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 809583557a..27d2f3a1d3 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -961,6 +961,12 @@ void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)
{
llassert( getComponents() <= 4 );
// This is fairly bogus, but it'll do for now.
+ if (isBufferInvalid())
+ {
+ LL_WARNS() << "Invalid image buffer" << LL_ENDL;
+ return;
+ }
+
U8 *pos = getData();
U32 x, y;
for (x = 0; x < getWidth(); x++)
@@ -1088,6 +1094,11 @@ void LLImageRaw::composite( LLImageRaw* src )
{
LLImageRaw* dst = this; // Just for clarity.
+ if (!validateSrcAndDst("LLImageRaw::composite", src, dst))
+ {
+ return;
+ }
+
llassert(3 == src->getComponents());
llassert(3 == dst->getComponents());
@@ -1155,7 +1166,6 @@ void LLImageRaw::compositeUnscaled4onto3( LLImageRaw* src )
llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
-
U8* src_data = src->getData();
U8* dst_data = dst->getData();
S32 pixels = getWidth() * getHeight();
@@ -1190,6 +1200,11 @@ void LLImageRaw::copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill)
{
LLImageRaw* dst = this; // Just for clarity.
+ if (!validateSrcAndDst("LLImageRaw::copyUnscaledAlphaMask", src, dst))
+ {
+ return;
+ }
+
llassert( 1 == src->getComponents() );
llassert( 4 == dst->getComponents() );
llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
@@ -1212,6 +1227,12 @@ void LLImageRaw::copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill)
// Fill the buffer with a constant color
void LLImageRaw::fill( const LLColor4U& color )
{
+ if (isBufferInvalid())
+ {
+ LL_WARNS() << "Invalid image buffer" << LL_ENDL;
+ return;
+ }
+
S32 pixels = getWidth() * getHeight();
if( 4 == getComponents() )
{
@@ -1250,14 +1271,13 @@ LLPointer LLImageRaw::duplicate()
// Src and dst can be any size. Src and dst can each have 3 or 4 components.
void LLImageRaw::copy(LLImageRaw* src)
{
- if (!src)
+ LLImageRaw* dst = this; // Just for clarity.
+
+ if (!validateSrcAndDst("LLImageRaw::copy", src, dst))
{
- LL_WARNS() << "LLImageRaw::copy called with a null src pointer" << LL_ENDL;
return;
}
- LLImageRaw* dst = this; // Just for clarity.
-
if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) )
{
// No scaling needed
@@ -1384,6 +1404,11 @@ void LLImageRaw::copyScaled( LLImageRaw* src )
{
LLImageRaw* dst = this; // Just for clarity.
+ if (!validateSrcAndDst("LLImageRaw::copyScaled", src, dst))
+ {
+ return;
+ }
+
llassert_always( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) );
llassert_always( src->getComponents() == dst->getComponents() );
@@ -1422,6 +1447,12 @@ bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data )
{
llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) );
+ if (isBufferInvalid())
+ {
+ LL_WARNS() << "Invalid image buffer" << LL_ENDL;
+ return false;
+ }
+
S32 old_width = getWidth();
S32 old_height = getHeight();
@@ -1721,6 +1752,25 @@ void LLImageRaw::compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S3
}
}
+bool LLImageRaw::validateSrcAndDst(std::string func, LLImageRaw* src, LLImageRaw* dst)
+{
+ if (!src || !dst || src->isBufferInvalid() || dst->isBufferInvalid())
+ {
+ LL_WARNS() << func << ": Source: ";
+ if (!src) LL_CONT << "Null pointer";
+ else if (src->isBufferInvalid()) LL_CONT << "Invalid buffer";
+ else LL_CONT << "OK";
+
+ LL_CONT << "; Destination: ";
+ if (!dst) LL_CONT << "Null pointer";
+ else if (dst->isBufferInvalid()) LL_CONT << "Invalid buffer";
+ else LL_CONT << "OK";
+ LL_CONT << "." << LL_ENDL;
+
+ return false;
+ }
+ return true;
+}
//----------------------------------------------------------------------------
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index f62aa69b2a..76d53b3f09 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -287,6 +287,9 @@ public:
// texture comment metadata reader
std::string mComment;
//
+
+private:
+ bool validateSrcAndDst(std::string func, LLImageRaw* src, LLImageRaw* dst);
};
// Compressed representation of image.
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index 4bffe586f6..7978ecc845 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -31,9 +31,31 @@
#include "llpointer.h"
#include "llmath.h"
#include "llkdumem.h"
+#include "stringize.h"
#include "kdu_block_coding.h"
+#include
+#include
+
+namespace {
+// exception used to keep KDU from terminating entire program -- see comments
+// in LLKDUMessageError::flush()
+struct KDUError: public std::runtime_error
+{
+ KDUError(const std::string& msg): std::runtime_error(msg) {}
+};
+} // anonymous namespace
+
+// stream kdu_dims to std::ostream
+// Turns out this must NOT be in the anonymous namespace!
+inline
+std::ostream& operator<<(std::ostream& out, const kdu_dims& dims)
+{
+ return out << "(" << dims.pos.x << "," << dims.pos.y << "),"
+ "[" << dims.size.x << "x" << dims.size.y << "]";
+}
+
class kdc_flow_control {
public:
@@ -172,9 +194,15 @@ struct LLKDUMessageError : public LLKDUMessage
// terminating handler→flush call."
// So throwing an exception here isn't arbitrary: we MUST throw an
// exception if we want to recover from a KDU error.
+ // Because this confused me: the above quote specifically refers to
+ // the kdu_error class, which is constructed internally within KDU at
+ // the point where a fatal error is discovered and reported. It is NOT
+ // talking about the kdu_message subclass passed to
+ // kdu_customize_errors(). Destroying this static object at program
+ // shutdown will NOT engage the behavior described above.
if (end_of_message)
{
- throw "KDU throwing an exception";
+ throw KDUError("LLKDUMessageError::flush()");
}
}
};
@@ -203,6 +231,10 @@ LLImageJ2CKDU::~LLImageJ2CKDU()
// Stuff for new simple decode
void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision);
+// This is called by the real (private) initDecode() (keep_codestream true)
+// and getMetadata() methods (keep_codestream false). As far as nat can tell,
+// mode is always MODE_FAST. It was called by findDiscardLevelsBoundaries()
+// as well, when that still existed, with keep_codestream true and MODE_FAST.
void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECodeStreamMode mode)
{
S32 data_size = base.getDataSize();
@@ -213,6 +245,12 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod
//
mCodeStreamp.reset();
+ // It's not clear to nat under what circumstances we would reuse a
+ // pre-existing LLKDUMemSource instance. As of 2016-08-05, it consists of
+ // two U32s and a pointer, so it's not as if it would be a huge overhead
+ // to allocate a new one every time.
+ // Also -- why is base.getData() tested specifically here? If that returns
+ // NULL, shouldn't we bail out of the whole method?
if (!mInputp && base.getData())
{
// The compressed data has been loaded
@@ -269,13 +307,22 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod
S32 components = mCodeStreamp->get_num_components();
- if (components >= 3)
- { // Check that components have consistent dimensions (for PPM file)
- kdu_dims dims1; mCodeStreamp->get_dims(1,dims1);
- kdu_dims dims2; mCodeStreamp->get_dims(2,dims2);
- if ((dims1 != dims) || (dims2 != dims))
+ // Check that components have consistent dimensions (for PPM file)
+ for (int idx = 1; idx < components; ++idx)
+ {
+ kdu_dims other_dims;
+ mCodeStreamp->get_dims(idx, other_dims);
+ if (other_dims != dims)
{
- LL_ERRS() << "Components don't have matching dimensions!" << LL_ENDL;
+ // This method is only called from methods that catch KDUError.
+ // We want to fail the image load, not crash the viewer.
+ // Can't use operator << with kdu_core::kdu_dims
+ //throw KDUError(STRINGIZE("Component " << idx << " dimensions "
+ // << other_dims
+ // << " do not match component 0 dimensions "
+ // << dims << "!"));
+ throw KDUError("Components don't have matching dimensions!");
+ //
}
}
@@ -302,6 +349,9 @@ void LLImageJ2CKDU::cleanupCodeStream()
mTileIndicesp.reset();
}
+// This is the protected virtual method called by LLImageJ2C::initDecode().
+// However, as far as nat can tell, LLImageJ2C::initDecode() is called only by
+// llimage_libtest.cpp's load_image() function. No detectable production use.
bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region)
{
return initDecode(base,raw_image,0.0f,MODE_FAST,0,4,discard_level,region);
@@ -334,6 +384,9 @@ bool LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int bloc
return true;
}
+// This is the real (private) initDecode() called both by the protected
+// initDecode() method and by decodeImpl(). As far as nat can tell, only the
+// decodeImpl() usage matters for production.
bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level, int* region)
{
base.resetLastError();
@@ -391,9 +444,9 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
mTPosp->x = 0;
}
}
- catch (const char* msg)
+ catch (const KDUError& msg)
{
- base.setLastError(ll_safe_string(msg));
+ base.setLastError(msg.what());
return false;
}
catch (...)
@@ -501,9 +554,9 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
return false;
}
}
- catch (const char* msg)
+ catch (const KDUError& msg)
{
- base.setLastError(ll_safe_string(msg));
+ base.setLastError(msg.what());
base.decodeFailed();
cleanupCodeStream();
return true; // done
@@ -695,9 +748,9 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
base.updateData(); // set width, height
delete[] output_buffer;
}
- catch(const char* msg)
+ catch(const KDUError& msg)
{
- base.setLastError(ll_safe_string(msg));
+ base.setLastError(msg.what());
return false;
}
catch( ... )
@@ -719,9 +772,9 @@ bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
setupCodeStream(base, false, MODE_FAST);
return true;
}
- catch (const char* msg)
+ catch (const KDUError& msg)
{
- base.setLastError(ll_safe_string(msg));
+ base.setLastError(msg.what());
return false;
}
catch (...)
diff --git a/indra/llwindow/llwindowcallbacks.cpp b/indra/llwindow/llwindowcallbacks.cpp
index d2afb3f91b..474953d3a4 100644
--- a/indra/llwindow/llwindowcallbacks.cpp
+++ b/indra/llwindow/llwindowcallbacks.cpp
@@ -175,6 +175,11 @@ BOOL LLWindowCallbacks::handleDeviceChange(LLWindow *window)
return FALSE;
}
+void LLWindowCallbacks::handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height)
+{
+
+}
+
void LLWindowCallbacks::handlePingWatchdog(LLWindow *window, const char * msg)
{
diff --git a/indra/llwindow/llwindowcallbacks.h b/indra/llwindow/llwindowcallbacks.h
index 6a7137e593..de789a71d9 100644
--- a/indra/llwindow/llwindowcallbacks.h
+++ b/indra/llwindow/llwindowcallbacks.h
@@ -65,6 +65,7 @@ public:
virtual void handleDataCopy(LLWindow *window, S32 data_type, void *data);
virtual BOOL handleTimerEvent(LLWindow *window);
virtual BOOL handleDeviceChange(LLWindow *window);
+ virtual void handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height);
enum DragNDropAction {
DNDA_START_TRACKING = 0,// Start tracking an incoming drag
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 1144b75257..0d326641fd 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -71,6 +71,11 @@ const S32 MAX_MESSAGE_PER_UPDATE = 20;
const S32 BITS_PER_PIXEL = 32;
const S32 MAX_NUM_RESOLUTIONS = 32;
const F32 ICON_FLASH_TIME = 0.5f;
+const F32 DEFAULT_DPI = 96.0f;
+
+#ifndef WM_DPICHANGED
+const S32 WM_DPICHANGED = 0x02E0;
+#endif
extern BOOL gDebugWindowProc;
@@ -97,6 +102,10 @@ typedef enum MONITOR_DPI_TYPE {
typedef HRESULT(STDAPICALLTYPE *SetProcessDpiAwarenessType)(_In_ PROCESS_DPI_AWARENESS value);
+typedef HRESULT(STDAPICALLTYPE *GetProcessDpiAwarenessType)(
+ _In_ HANDLE hprocess,
+ _Out_ PROCESS_DPI_AWARENESS *value);
+
typedef HRESULT(STDAPICALLTYPE *GetDpiForMonitorType)(
_In_ HMONITOR hmonitor,
_In_ MONITOR_DPI_TYPE dpiType,
@@ -2639,6 +2648,24 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
return 0;
}
+
+ case WM_DPICHANGED:
+ {
+ LPRECT lprc_new_scale;
+ F32 new_scale = LOWORD(w_param) / 96.0f;
+ lprc_new_scale = (LPRECT)l_param;
+ S32 new_width = lprc_new_scale->right - lprc_new_scale->left;
+ S32 new_height = lprc_new_scale->bottom - lprc_new_scale->top;
+ window_imp->mCallbacks->handleDPIChanged(window_imp, new_scale, new_width, new_height);
+ SetWindowPos(h_wnd,
+ HWND_TOP,
+ lprc_new_scale->left,
+ lprc_new_scale->top,
+ new_width,
+ new_height,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ return 0;
+ }
case WM_SETFOCUS:
window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SETFOCUS");
@@ -3966,6 +3993,31 @@ BOOL LLWindowWin32::handleImeRequests(WPARAM request, LPARAM param, LRESULT *res
return FALSE;
}
+//static
+void LLWindowWin32::setDPIAwareness()
+{
+ HMODULE hShcore = LoadLibrary(L"shcore.dll");
+ if (hShcore != NULL)
+ {
+ SetProcessDpiAwarenessType pSPDA;
+ pSPDA = (SetProcessDpiAwarenessType)GetProcAddress(hShcore, "SetProcessDpiAwareness");
+ if (pSPDA)
+ {
+
+ HRESULT hr = pSPDA(PROCESS_PER_MONITOR_DPI_AWARE);
+ if (hr != S_OK)
+ {
+ LL_WARNS() << "SetProcessDpiAwareness() function returned an error. Will use legacy DPI awareness API of Win XP/7" << LL_ENDL;
+ }
+ }
+ FreeLibrary(hShcore);
+ }
+ else
+ {
+ LL_WARNS() << "Could not load shcore.dll library (included by from Win 8.1 SDK. Will use legacy DPI awareness API of Win XP/7" << LL_ENDL;
+ }
+}
+
F32 LLWindowWin32::getSystemUISize()
{
// Type fix
@@ -3974,39 +4026,55 @@ F32 LLWindowWin32::getSystemUISize()
HWND hWnd = (HWND)getPlatformWindow();
HDC hdc = GetDC(hWnd);
HMONITOR hMonitor;
+ HANDLE hProcess = GetCurrentProcess();
+ PROCESS_DPI_AWARENESS dpi_awareness;
HMODULE hShcore = LoadLibrary(L"shcore.dll");
if (hShcore != NULL)
{
- // Set DPI awareness via manifest as recommended
- //SetProcessDpiAwarenessType pSPDA;
- //pSPDA = (SetProcessDpiAwarenessType)GetProcAddress(hShcore, "SetProcessDpiAwareness");
- //
+ GetProcessDpiAwarenessType pGPDA;
+ pGPDA = (GetProcessDpiAwarenessType)GetProcAddress(hShcore, "GetProcessDpiAwareness");
GetDpiForMonitorType pGDFM;
pGDFM = (GetDpiForMonitorType)GetProcAddress(hShcore, "GetDpiForMonitor");
- if (/*pSPDA != NULL &&*/ pGDFM != NULL) // Set DPI awareness via manifest as recommended
+ if (pGPDA != NULL && pGDFM != NULL)
{
- //pSPDA(PROCESS_PER_MONITOR_DPI_AWARE); // Set DPI awareness via manifest as recommended
- POINT pt;
- UINT dpix = 0, dpiy = 0;
- HRESULT hr = E_FAIL;
+ pGPDA(hProcess, &dpi_awareness);
+ if (dpi_awareness == PROCESS_PER_MONITOR_DPI_AWARE)
+ {
+ POINT pt;
+ UINT dpix = 0, dpiy = 0;
+ HRESULT hr = E_FAIL;
+ RECT rect;
- // Get the DPI for the main monitor, and set the scaling factor
- pt.x = 1;
- pt.y = 1;
- // Get scaling for primary display, assuming that's where we open the viewer
- //hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
- hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
- //
- hr = pGDFM(hMonitor, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
- scale_value = dpix / 96.0f;
+ GetWindowRect(hWnd, &rect);
+ // Get the DPI for the monitor, on which the center of window is displayed and set the scaling factor
+ pt.x = (rect.left + rect.right) / 2;
+ pt.y = (rect.top + rect.bottom) / 2;
+ hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
+ hr = pGDFM(hMonitor, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
+ if (hr == S_OK)
+ {
+ scale_value = dpix / DEFAULT_DPI;
+ }
+ else
+ {
+ LL_WARNS() << "Could not determine DPI for monitor. Setting scale to default 100 %" << LL_ENDL;
+ scale_value = 1.0f;
+ }
+ }
+ else
+ {
+ LL_WARNS() << "Process is not per-monitor DPI-aware. Setting scale to default 100 %" << LL_ENDL;
+ scale_value = 1.0f;
+ }
}
+ FreeLibrary(hShcore);
}
else
{
LL_WARNS() << "Could not load shcore.dll library (included by from Win 8.1 SDK). Using legacy DPI awareness API of Win XP/7" << LL_ENDL;
- scale_value = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f;
+ scale_value = GetDeviceCaps(hdc, LOGPIXELSX) / DEFAULT_DPI;
}
ReleaseDC(hWnd, hdc);
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index 15c968e82f..a94f93a776 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -120,7 +120,7 @@ public:
LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url );
static std::vector getDynamicFallbackFontList();
-
+ static void setDPIAwareness();
protected:
LLWindowWin32(LLWindowCallbacks* callbacks,
const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags,
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 3a5d694b02..125c453c66 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10358,6 +10358,17 @@ Change of this parameter will affect the layout of buttons in notification toast
Backup
0
+ PrimTextMaxDrawDistance
+
ProbeHardwareOnStartup
+ ComplexityChangesPopUpDelay
+
RenderAvatarMaxComplexity
+ RenderHUDObjectsWarning
+
+ RenderHUDTexturesWarning
+
+ RenderHUDOversizedTexturesWarning
+
+ RenderHUDTexturesVirtualMemoryWarning
+
RenderAutoMuteSurfaceAreaLimit