summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/app_settings/settings.xml22
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZEALL.tifbin0 -> 624 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMOUT.tifbin0 -> 576 bytes
-rw-r--r--indra/newview/llappviewer.cpp4
-rw-r--r--indra/newview/llface.cpp31
-rw-r--r--indra/newview/llfloatertexturefetchdebugger.cpp8
-rw-r--r--indra/newview/llfloatertexturefetchdebugger.h1
-rw-r--r--indra/newview/llimview.cpp2
-rw-r--r--indra/newview/lltexturefetch.cpp185
-rw-r--r--indra/newview/lltexturefetch.h26
-rw-r--r--indra/newview/llviewermedia.cpp14
-rw-r--r--indra/newview/llviewerstats.cpp23
-rw-r--r--indra/newview/llviewerstats.h1
-rw-r--r--indra/newview/llviewertexture.h1
-rw-r--r--indra/newview/llviewertexturelist.cpp30
-rw-r--r--indra/newview/llviewertexturelist.h2
-rw-r--r--indra/newview/llviewerwindow.cpp7
-rw-r--r--indra/newview/res-sdl/lltoolzoomout.BMPbin2102 -> 630 bytes
-rw-r--r--indra/newview/res-sdl/sizeall.BMPbin0 -> 630 bytes
-rw-r--r--indra/newview/res/lltoolzoomout.curbin326 -> 326 bytes
-rw-r--r--indra/newview/skins/default/xui/en/floater_scene_load_stats.xml145
-rw-r--r--indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml12
22 files changed, 361 insertions, 153 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 77f0fd99bc..49c2d7b4e4 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -12509,6 +12509,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>TextureFetchMinTimeToLog</key>
+ <map>
+ <key>Comment</key>
+ <string>If texture fetching time exceeds this value, texture fetch tester will log info</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>2.0</real>
+ </map>
<key>TextureFetchFakeFailureRate</key>
<map>
<key>Comment</key>
@@ -12597,6 +12608,17 @@
<key>Value</key>
<integer>32</integer>
</map>
+ <key>TextureListFetchingThreshold</key>
+ <map>
+ <key>Comment</key>
+ <string>If the ratio between fetched and all textures in the list is greater than this threshold, which we assume that almost all textures are fetched</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.97</real>
+ </map>
<key>TextureLoadFullRes</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZEALL.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZEALL.tif
new file mode 100644
index 0000000000..85fec76fca
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZEALL.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMOUT.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMOUT.tif
new file mode 100644
index 0000000000..d64a7f2b68
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMOUT.tif
Binary files differ
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 0b2cdff36c..01de232dcc 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -4876,6 +4876,10 @@ void LLAppViewer::idle()
//
// Special case idle if still starting up
//
+ if (LLStartUp::getStartupState() >= STATE_WORLD_INIT)
+ {
+ update_texture_time();
+ }
if (LLStartUp::getStartupState() < STATE_STARTED)
{
// Skip rest if idle startup returns false (essentially, no world yet)
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 01d8b6775b..67ad7967a6 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -1074,6 +1074,13 @@ bool LLFace::calcAlignedPlanarTE(const LLFace* align_to, LLVector2* res_st_offs
F32 map_rot = 0.f, map_scaleS = 0.f, map_scaleT = 0.f, map_offsS = 0.f, map_offsT = 0.f;
+ LLMaterial* mat = orig_tep->getMaterialParams();
+ if (!mat && map != LLRender::DIFFUSE_MAP)
+ {
+ LL_WARNS_ONCE("llface") << "Face is set to use specular or normal map but has no material, defaulting to diffuse" << LL_ENDL;
+ map = LLRender::DIFFUSE_MAP;
+ }
+
switch (map)
{
case LLRender::DIFFUSE_MAP:
@@ -1084,26 +1091,26 @@ bool LLFace::calcAlignedPlanarTE(const LLFace* align_to, LLVector2* res_st_offs
map_offsT = orig_tep->mOffsetT;
break;
case LLRender::NORMAL_MAP:
- if (orig_tep->getMaterialParams()->getNormalID().isNull())
+ if (mat->getNormalID().isNull())
{
return false;
}
- map_rot = orig_tep->getMaterialParams()->getNormalRotation();
- map_scaleS = orig_tep->getMaterialParams()->getNormalRepeatX();
- map_scaleT = orig_tep->getMaterialParams()->getNormalRepeatY();
- map_offsS = orig_tep->getMaterialParams()->getNormalOffsetX();
- map_offsT = orig_tep->getMaterialParams()->getNormalOffsetY();
+ map_rot = mat->getNormalRotation();
+ map_scaleS = mat->getNormalRepeatX();
+ map_scaleT = mat->getNormalRepeatY();
+ map_offsS = mat->getNormalOffsetX();
+ map_offsT = mat->getNormalOffsetY();
break;
case LLRender::SPECULAR_MAP:
- if (orig_tep->getMaterialParams()->getSpecularID().isNull())
+ if (mat->getSpecularID().isNull())
{
return false;
}
- map_rot = orig_tep->getMaterialParams()->getSpecularRotation();
- map_scaleS = orig_tep->getMaterialParams()->getSpecularRepeatX();
- map_scaleT = orig_tep->getMaterialParams()->getSpecularRepeatY();
- map_offsS = orig_tep->getMaterialParams()->getSpecularOffsetX();
- map_offsT = orig_tep->getMaterialParams()->getSpecularOffsetY();
+ map_rot = mat->getSpecularRotation();
+ map_scaleS = mat->getSpecularRepeatX();
+ map_scaleT = mat->getSpecularRepeatY();
+ map_offsS = mat->getSpecularOffsetX();
+ map_offsT = mat->getSpecularOffsetY();
break;
default: /*make compiler happy*/
break;
diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp
index 9a23d99802..cda4dc8bcc 100644
--- a/indra/newview/llfloatertexturefetchdebugger.cpp
+++ b/indra/newview/llfloatertexturefetchdebugger.cpp
@@ -38,6 +38,7 @@
#include "llappviewer.h"
#include "lltexturefetch.h"
#include "llviewercontrol.h"
+#include "llviewerassetstats.h" //gTextureTimer
LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key)
: LLFloater(key),
@@ -50,6 +51,7 @@ LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key)
mCommitCallbackRegistrar.add("TexFetchDebugger.Start", boost::bind(&LLFloaterTextureFetchDebugger::onClickStart, this));
mCommitCallbackRegistrar.add("TexFetchDebugger.Clear", boost::bind(&LLFloaterTextureFetchDebugger::onClickClear, this));
mCommitCallbackRegistrar.add("TexFetchDebugger.Close", boost::bind(&LLFloaterTextureFetchDebugger::onClickClose, this));
+ mCommitCallbackRegistrar.add("TexFetchDebugger.ResetFetchTime", boost::bind(&LLFloaterTextureFetchDebugger::onClickResetFetchTime, this));
mCommitCallbackRegistrar.add("TexFetchDebugger.CacheRead", boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheRead, this));
mCommitCallbackRegistrar.add("TexFetchDebugger.CacheWrite", boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheWrite, this));
@@ -228,6 +230,12 @@ void LLFloaterTextureFetchDebugger::onClickClose()
delete this;
}
+void LLFloaterTextureFetchDebugger::onClickResetFetchTime()
+{
+ gTextureTimer.start();
+ gTextureTimer.pause();
+}
+
void LLFloaterTextureFetchDebugger::onClickClear()
{
mButtonStateMap["start_btn"] = true;
diff --git a/indra/newview/llfloatertexturefetchdebugger.h b/indra/newview/llfloatertexturefetchdebugger.h
index 096ad88e07..637f3b03e5 100644
--- a/indra/newview/llfloatertexturefetchdebugger.h
+++ b/indra/newview/llfloatertexturefetchdebugger.h
@@ -44,6 +44,7 @@ public:
void onClickStart();
void onClickClear();
void onClickClose();
+ void onClickResetFetchTime();
void onClickCacheRead();
void onClickCacheWrite();
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 1059324a16..1509e5ddab 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -1701,7 +1701,7 @@ LLUUID LLIMMgr::computeSessionID(
}
}
- if (gAgent.isInGroup(session_id) && (session_id != other_participant_id))
+ if (gAgent.isInGroup(session_id, TRUE) && (session_id != other_participant_id))
{
LL_WARNS() << "Group session id different from group id: IM type = " << dialog << ", session id = " << session_id << ", group id = " << other_participant_id << LL_ENDL;
}
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index f64db7beb5..e7b756bf4a 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -70,8 +70,12 @@ LLTrace::EventStatHandle<LLUnit<F32, LLUnits::Percent> > LLTextureFetch::sCacheH
LLTrace::SampleStatHandle<F32Seconds> LLTextureFetch::sCacheReadLatency("texture_cache_read_latency");
LLTrace::SampleStatHandle<F32Seconds> LLTextureFetch::sTexDecodeLatency("texture_decode_latency");
+LLTrace::SampleStatHandle<F32Seconds> LLTextureFetch::sCacheWriteLatency("texture_write_latency");
LLTrace::SampleStatHandle<F32Seconds> LLTextureFetch::sTexFetchLatency("texture_fetch_latency");
+LLTextureFetchTester* LLTextureFetch::sTesterp = NULL ;
+const std::string sTesterName("TextureFetchTester");
+
//////////////////////////////////////////////////////////////////////////////
//
// Introduction
@@ -438,6 +442,29 @@ public:
// Threads: Ttf
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
+ enum e_state // mState
+ {
+ // *NOTE: Do not change the order/value of state variables, some code
+ // depends upon specific ordering/adjacency.
+
+ // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
+ INVALID = 0,
+ INIT,
+ LOAD_FROM_TEXTURE_CACHE,
+ CACHE_POST,
+ LOAD_FROM_NETWORK,
+ LOAD_FROM_SIMULATOR,
+ WAIT_HTTP_RESOURCE, // Waiting for HTTP resources
+ WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources
+ SEND_HTTP_REQ, // Commit to sending as HTTP
+ WAIT_HTTP_REQ, // Request sent, wait for completion
+ DECODE_IMAGE,
+ DECODE_IMAGE_UPDATE,
+ WRITE_TO_CACHE,
+ WAIT_ON_WRITE,
+ DONE
+ };
+
protected:
LLTextureFetchWorker(LLTextureFetch* fetcher, FTType f_type,
const std::string& url, const LLUUID& id, const LLHost& host,
@@ -517,28 +544,6 @@ private:
}
private:
- enum e_state // mState
- {
- // *NOTE: Do not change the order/value of state variables, some code
- // depends upon specific ordering/adjacency.
-
- // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
- INVALID = 0,
- INIT,
- LOAD_FROM_TEXTURE_CACHE,
- CACHE_POST,
- LOAD_FROM_NETWORK,
- LOAD_FROM_SIMULATOR,
- WAIT_HTTP_RESOURCE, // Waiting for HTTP resources
- WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources
- SEND_HTTP_REQ, // Commit to sending as HTTP
- WAIT_HTTP_REQ, // Request sent, wait for completion
- DECODE_IMAGE,
- DECODE_IMAGE_UPDATE,
- WRITE_TO_CACHE,
- WAIT_ON_WRITE,
- DONE
- };
enum e_request_state // mSentRequest
{
UNSENT = 0,
@@ -551,7 +556,7 @@ private:
CAN_WRITE = 1,
SHOULD_WRITE = 2
};
- static const char* sStateDescs[];
+
e_state mState;
void setState(e_state new_state);
@@ -579,10 +584,15 @@ private:
LLFrameTimer mFetchDeltaTimer;
LLTimer mCacheReadTimer;
LLTimer mDecodeTimer;
+ LLTimer mCacheWriteTimer;
LLTimer mFetchTimer;
+ LLTimer mStateTimer;
F32 mCacheReadTime; // time for cache read only
F32 mDecodeTime; // time for decode only
+ F32 mCacheWriteTime;
F32 mFetchTime; // total time from req to finished fetch
+ std::map<S32, F32> mStateTimersMap;
+ F32 mSkippedStatesTime;
LLTextureCache::handle_t mCacheReadHandle,
mCacheWriteHandle;
S32 mRequestedSize,
@@ -866,8 +876,7 @@ bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
//////////////////////////////////////////////////////////////////////////////
-//static
-const char* LLTextureFetchWorker::sStateDescs[] = {
+const char* sStateDescs[] = {
"INVALID",
"INIT",
"LOAD_FROM_TEXTURE_CACHE",
@@ -885,6 +894,9 @@ const char* LLTextureFetchWorker::sStateDescs[] = {
"DONE"
};
+const std::set<S32> LOGGED_STATES = { LLTextureFetchWorker::LOAD_FROM_TEXTURE_CACHE, LLTextureFetchWorker::LOAD_FROM_NETWORK, LLTextureFetchWorker::LOAD_FROM_SIMULATOR,
+ LLTextureFetchWorker::WAIT_HTTP_REQ, LLTextureFetchWorker::DECODE_IMAGE_UPDATE, LLTextureFetchWorker::WAIT_ON_WRITE };
+
// static
volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break
@@ -916,6 +928,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mLoadedDiscard(-1),
mDecodedDiscard(-1),
mCacheReadTime(0.f),
+ mCacheWriteTime(0.f),
mDecodeTime(0.f),
mFetchTime(0.f),
mCacheReadHandle(LLTextureCache::nullHandle()),
@@ -924,6 +937,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mRequestedOffset(0),
mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE),
mFileSize(0),
+ mSkippedStatesTime(0),
mCachedSize(0),
mLoaded(FALSE),
mSentRequest(UNSENT),
@@ -1184,6 +1198,13 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == INIT)
{
+ mStateTimer.reset();
+ mFetchTimer.reset();
+ for(auto i : LOGGED_STATES)
+ {
+ mStateTimersMap[i] = 0;
+ }
+ mSkippedStatesTime = 0;
mRawImage = NULL ;
mRequestedDiscard = -1;
mLoadedDiscard = -1;
@@ -1241,9 +1262,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
++mCacheReadCount;
std::string filename = mUrl.substr(7, std::string::npos);
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
+ mCacheReadTimer.reset();
mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
offset, size, responder);
- mCacheReadTimer.reset();
+
}
else if ((mUrl.empty() || mFTType==FTT_SERVER_BAKE) && mFetcher->canLoadFromCache())
{
@@ -1251,9 +1273,9 @@ bool LLTextureFetchWorker::doWork(S32 param)
++mCacheReadCount;
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
- mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
- offset, size, responder);
mCacheReadTimer.reset();
+ mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
+ offset, size, responder);;
}
else if(!mUrl.empty() && mCanUseHTTP)
{
@@ -1275,6 +1297,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
mCacheReadHandle = LLTextureCache::nullHandle();
setState(CACHE_POST);
add(LLTextureFetch::sCacheHit, 1.0);
+ mCacheReadTime = mCacheReadTimer.getElapsedTimeF32();
// fall through
}
else
@@ -1888,7 +1911,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: mLoadedDiscard < 0" << LL_ENDL;
return true;
}
-
+ mDecodeTimer.reset();
mRawImage = NULL;
mAuxImage = NULL;
llassert_always(mFormattedImage.notNull());
@@ -1982,6 +2005,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
// be protected by work mutex and won't be safe to use here nor in cache worker.
// So make sure users of getRequestFinished() does not attempt to modify image while
// fetcher is working
+ mCacheWriteTimer.reset();
mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
mFormattedImage->getData(), datasize,
mFileSize, mRawImage, mDecodedDiscard, responder);
@@ -1992,6 +2016,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
if (writeToCacheComplete())
{
+ mCacheWriteTime = mCacheWriteTimer.getElapsedTimeF32();
setState(DONE);
// fall through
}
@@ -2500,7 +2525,6 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag
mDecoded = TRUE;
// LL_INFOS(LOG_TXT) << mID << " : DECODE COMPLETE " << LL_ENDL;
setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- mCacheReadTime = mCacheReadTimer.getElapsedTimeF32();
} // -Mw
//////////////////////////////////////////////////////////////////////////////
@@ -2625,6 +2649,17 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
}
mOriginFetchSource = mFetchSource;
}
+
+ // If that test log has ben requested but not yet created, create it
+ if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName))
+ {
+ sTesterp = new LLTextureFetchTester() ;
+ if (!sTesterp->isValid())
+ {
+ delete sTesterp;
+ sTesterp = NULL;
+ }
+ }
}
LLTextureFetch::~LLTextureFetch()
@@ -2966,20 +3001,51 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
}
else if (worker->checkWork())
{
+ F32 decode_time;
+ F32 fetch_time;
+ F32 cache_read_time;
+ F32 cache_write_time;
+ S32 file_size;
+ std::map<S32, F32> logged_state_timers;
+ F32 skipped_states_time;
worker->lockWorkMutex(); // +Mw
last_http_get_status = worker->mGetStatus;
discard_level = worker->mDecodedDiscard;
raw = worker->mRawImage;
aux = worker->mAuxImage;
- sample(sTexDecodeLatency, worker->mDecodeTime);
- sample(sTexFetchLatency, worker->mFetchTime);
- sample(sCacheReadLatency, worker->mCacheReadTime);
+
+ decode_time = worker->mDecodeTime;
+ fetch_time = worker->mFetchTime;
+ cache_read_time = worker->mCacheReadTime;
+ cache_write_time = worker->mCacheWriteTime;
+ file_size = worker->mFileSize;
worker->mCacheReadTimer.reset();
worker->mDecodeTimer.reset();
+ worker->mCacheWriteTimer.reset();
worker->mFetchTimer.reset();
+ logged_state_timers = worker->mStateTimersMap;
+ skipped_states_time = worker->mSkippedStatesTime;
+ worker->mStateTimer.reset();
res = true;
LL_DEBUGS(LOG_TXT) << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
worker->unlockWorkMutex(); // -Mw
+
+ sample(sTexDecodeLatency, decode_time);
+ sample(sTexFetchLatency, fetch_time);
+ sample(sCacheReadLatency, cache_read_time);
+ sample(sCacheWriteLatency, cache_write_time);
+
+ static LLCachedControl<F32> min_time_to_log(gSavedSettings, "TextureFetchMinTimeToLog", 2.f);
+ if (fetch_time > min_time_to_log)
+ {
+ //LL_INFOS() << "fetch_time: " << fetch_time << " cache_read_time: " << cache_read_time << " decode_time: " << decode_time << " cache_write_time: " << cache_write_time << LL_ENDL;
+
+ LLTextureFetchTester* tester = (LLTextureFetchTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
+ if (tester)
+ {
+ tester->updateStats(logged_state_timers, fetch_time, skipped_states_time, file_size) ;
+ }
+ }
}
else
{
@@ -3464,6 +3530,21 @@ void LLTextureFetchWorker::setState(e_state new_state)
// LL_INFOS(LOG_TXT) << "id: " << mID << " FTType: " << mFTType << " disc: " << mDesiredDiscard << " sz: " << mDesiredSize << " state: " << e_state_name[mState] << " => " << e_state_name[new_state] << LL_ENDL;
}
+
+ F32 d_time = mStateTimer.getElapsedTimeF32();
+ if (d_time >= 0.0001F)
+ {
+ if (LOGGED_STATES.count(mState))
+ {
+ mStateTimersMap[mState] = d_time;
+ }
+ else
+ {
+ mSkippedStatesTime += d_time;
+ }
+ }
+
+ mStateTimer.reset();
mState = new_state;
}
@@ -3679,7 +3760,7 @@ void LLTextureFetch::dump()
LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass();
LL_INFOS(LOG_TXT) << " ID: " << worker->mID
<< " PRI: " << llformat("0x%08x",wreq->getPriority())
- << " STATE: " << worker->sStateDescs[worker->mState]
+ << " STATE: " << sStateDescs[worker->mState]
<< LL_ENDL;
}
@@ -5121,4 +5202,40 @@ void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpRespon
//End LLTextureFetchDebugger
///////////////////////////////////////////////////////////////////////////////////////////
+LLTextureFetchTester::LLTextureFetchTester() : LLMetricPerformanceTesterBasic(sTesterName)
+{
+ mTextureFetchTime = 0;
+ mSkippedStatesTime = 0;
+ mFileSize = 0;
+}
+
+LLTextureFetchTester::~LLTextureFetchTester()
+{
+ outputTestResults();
+ LLTextureFetch::sTesterp = NULL;
+}
+
+//virtual
+void LLTextureFetchTester::outputTestRecord(LLSD *sd)
+{
+ std::string currentLabel = getCurrentLabelName();
+
+ (*sd)[currentLabel]["Texture Fetch Time"] = (LLSD::Real)mTextureFetchTime;
+ (*sd)[currentLabel]["File Size"] = (LLSD::Integer)mFileSize;
+ (*sd)[currentLabel]["Skipped States Time"] = (LLSD::String)llformat("%.6f", mSkippedStatesTime);
+
+ for(auto i : LOGGED_STATES)
+ {
+ (*sd)[currentLabel][sStateDescs[i]] = mStateTimersMap[i];
+ }
+}
+
+void LLTextureFetchTester::updateStats(const std::map<S32, F32> state_timers, const F32 fetch_time, const F32 skipped_states_time, const S32 file_size)
+{
+ mTextureFetchTime = fetch_time;
+ mStateTimersMap = state_timers;
+ mFileSize = file_size;
+ mSkippedStatesTime = skipped_states_time;
+ outputTestResults();
+}
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 2aa194e141..bf6732963f 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -50,6 +50,7 @@ class LLHost;
class LLViewerAssetStats;
class LLTextureFetchDebugger;
class LLTextureCache;
+class LLTextureFetchTester;
// Interface class
@@ -312,6 +313,7 @@ public:
static LLTrace::CountStatHandle<F64> sCacheAttempt;
static LLTrace::SampleStatHandle<F32Seconds> sCacheReadLatency;
static LLTrace::SampleStatHandle<F32Seconds> sTexDecodeLatency;
+ static LLTrace::SampleStatHandle<F32Seconds> sCacheWriteLatency;
static LLTrace::SampleStatHandle<F32Seconds> sTexFetchLatency;
static LLTrace::EventStatHandle<LLUnit<F32, LLUnits::Percent> > sCacheHitRate;
@@ -403,6 +405,9 @@ public:
FROM_HTTP_ONLY,
INVALID_SOURCE
};
+
+ static LLTextureFetchTester* sTesterp;
+
private:
//debug use
LLTextureFetchDebugger* mFetchDebugger;
@@ -635,5 +640,26 @@ private:
public:
static bool isEnabled() {return sDebuggerEnabled;}
};
+
+
+class LLTextureFetchTester : public LLMetricPerformanceTesterBasic
+{
+public:
+ LLTextureFetchTester();
+ ~LLTextureFetchTester();
+
+ void updateStats(const std::map<S32, F32> states_timers, const F32 fetch_time, const F32 other_states_time, const S32 file_size);
+
+protected:
+ /*virtual*/ void outputTestRecord(LLSD* sd);
+
+private:
+
+ F32 mTextureFetchTime;
+ F32 mSkippedStatesTime;
+ S32 mFileSize;
+
+ std::map<S32, F32> mStateTimersMap;
+};
#endif // LL_LLTEXTUREFETCH_H
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 9ed2df2759..661a520394 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -3193,19 +3193,7 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla
LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << plugin->getCursorName() << LL_ENDL;
std::string cursor = plugin->getCursorName();
-
- if(cursor == "arrow")
- mLastSetCursor = UI_CURSOR_ARROW;
- else if(cursor == "ibeam")
- mLastSetCursor = UI_CURSOR_IBEAM;
- else if(cursor == "splith")
- mLastSetCursor = UI_CURSOR_SIZEWE;
- else if(cursor == "splitv")
- mLastSetCursor = UI_CURSOR_SIZENS;
- else if(cursor == "hand")
- mLastSetCursor = UI_CURSOR_HAND;
- else // for anything else, default to the arrow
- mLastSetCursor = UI_CURSOR_ARROW;
+ mLastSetCursor = getCursorFromString(cursor);
}
break;
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 05f88b0a75..0380bb47fc 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -206,6 +206,7 @@ LLTrace::EventStatHandle<F64Seconds > AVATAR_EDIT_TIME("avataredittime", "Second
LLTrace::EventStatHandle<LLUnit<F32, LLUnits::Percent> > OBJECT_CACHE_HIT_RATE("object_cache_hits");
+LLTrace::EventStatHandle<F64Seconds > TEXTURE_FETCH_TIME("texture_fetch_time");
}
LLViewerStats::LLViewerStats()
@@ -387,15 +388,6 @@ void update_statistics()
add(LLStatViewer::ASSET_UDP_DATA_RECEIVED, F64Bits(gTransferManager.getTransferBitsIn(LLTCT_ASSET)));
gTransferManager.resetTransferBitsIn(LLTCT_ASSET);
- if (LLAppViewer::getTextureFetch()->getNumRequests() == 0)
- {
- gTextureTimer.pause();
- }
- else
- {
- gTextureTimer.unpause();
- }
-
sample(LLStatViewer::VISIBLE_AVATARS, LLVOAvatar::sNumVisibleAvatars);
LLWorld::getInstance()->updateNetStats();
LLWorld::getInstance()->requestCacheMisses();
@@ -417,6 +409,19 @@ void update_statistics()
}
}
+void update_texture_time()
+{
+ if (gTextureList.isPrioRequestsFetched())
+ {
+ gTextureTimer.pause();
+ }
+ else
+ {
+ gTextureTimer.unpause();
+ }
+
+ record(LLStatViewer::TEXTURE_FETCH_TIME, gTextureTimer.getElapsedTimeF32());
+}
/*
* The sim-side LLSD is in newsim/llagentinfo.cpp:forwardViewerStats.
*
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 04870e0c26..64b4628daa 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -295,6 +295,7 @@ static const F32 SEND_STATS_PERIOD = 300.0f;
// The following are from (older?) statistics code found in appviewer.
void update_statistics();
void send_viewer_stats(bool include_preferences);
+void update_texture_time();
extern LLFrameTimer gTextureTimer;
extern U32Bytes gTotalTextureData;
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index 69568cc825..7b4b3d940f 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -415,6 +415,7 @@ public:
BOOL isFullyLoaded() const;
BOOL hasFetcher() const { return mHasFetcher;}
+ bool isFetching() const { return mIsFetching;}
void setCanUseHTTP(bool can_use_http) {mCanUseHTTP = can_use_http;}
void forceToDeleteRequest();
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 561319ca5d..82973d3278 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -205,6 +205,9 @@ static std::string get_texture_list_name()
void LLViewerTextureList::doPrefetchImages()
{
+ gTextureTimer.start();
+ gTextureTimer.pause();
+
if (LLAppViewer::instance()->getPurgeCache())
{
// cache was purged, no point
@@ -1402,6 +1405,33 @@ S32Megabytes LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended, fl
return max_texmem;
}
+bool LLViewerTextureList::isPrioRequestsFetched()
+{
+ static LLCachedControl<F32> prio_threshold(gSavedSettings, "TextureFetchUpdatePriorityThreshold", 0.0f);
+ static LLCachedControl<F32> fetching_textures_threshold(gSavedSettings, "TextureListFetchingThreshold", 0.97f);
+ S32 fetching_tex_count = 0;
+ S32 tex_count_threshold = gTextureList.mImageList.size() * (1 - fetching_textures_threshold);
+
+ for (LLViewerTextureList::image_priority_list_t::iterator iter = gTextureList.mImageList.begin();
+ iter != gTextureList.mImageList.end(); )
+ {
+ LLPointer<LLViewerFetchedTexture> imagep = *iter++;
+ if (imagep->getDecodePriority() > prio_threshold)
+ {
+ if (imagep->hasFetcher() || imagep->isFetching())
+ {
+ fetching_tex_count++;
+ if (fetching_tex_count >= tex_count_threshold)
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
const S32Megabytes VIDEO_CARD_FRAMEBUFFER_MEM(12);
const S32Megabytes MIN_MEM_FOR_NON_TEXTURE(512);
void LLViewerTextureList::updateMaxResidentTexMem(S32Megabytes mem)
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 281d23c671..fead2e52b2 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -138,6 +138,8 @@ public:
static S32Megabytes getMinVideoRamSetting();
static S32Megabytes getMaxVideoRamSetting(bool get_recommended, float mem_multiplier);
+
+ static bool isPrioRequestsFetched();
private:
void updateImagesDecodePriorities();
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 0f96a0b06c..cf4b3f74d1 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -216,6 +216,7 @@
#if LL_WINDOWS
#include <tchar.h> // For Unicode conversion methods
+#include "llwindowwin32.h" // For AltGr handling
#endif
//
@@ -2770,9 +2771,11 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
// of character handling.
// Alt Gr can be additionally modified by Shift
const MASK alt_gr = MASK_CONTROL | MASK_ALT;
+ LLWindowWin32 *window = static_cast<LLWindowWin32*>(mWindow);
+ U32 raw_key = window->getRawWParam();
if ((mask & alt_gr) != 0
- && key >= 0x30
- && key <= 0x5A
+ && ((raw_key >= 0x30 && raw_key <= 0x5A) //0-9, plus normal chartacters
+ || (raw_key >= 0xBA && raw_key <= 0xE4)) // Misc/OEM characters that can be covered by AltGr, ex: -, =, ~
&& (GetKeyState(VK_RMENU) & 0x8000) != 0
&& (GetKeyState(VK_RCONTROL) & 0x8000) == 0) // ensure right control is not pressed, only left one
{
diff --git a/indra/newview/res-sdl/lltoolzoomout.BMP b/indra/newview/res-sdl/lltoolzoomout.BMP
index 7f958383ab..5bdf96f80d 100644
--- a/indra/newview/res-sdl/lltoolzoomout.BMP
+++ b/indra/newview/res-sdl/lltoolzoomout.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizeall.BMP b/indra/newview/res-sdl/sizeall.BMP
new file mode 100644
index 0000000000..03d9bf4654
--- /dev/null
+++ b/indra/newview/res-sdl/sizeall.BMP
Binary files differ
diff --git a/indra/newview/res/lltoolzoomout.cur b/indra/newview/res/lltoolzoomout.cur
index b33e68d1a6..21e0ee9702 100644
--- a/indra/newview/res/lltoolzoomout.cur
+++ b/indra/newview/res/lltoolzoomout.cur
Binary files differ
diff --git a/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml
index 62cce3a1e3..3f493192ff 100644
--- a/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml
+++ b/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml
@@ -36,16 +36,14 @@
stat="FramePixelDifference"
bar_max="100"
tick_spacing="10"
- unit_scale="100"
- precision="0"/>
+ unit_scale="100"/>
<stat_bar name="bandwidth"
label="UDP Data Received"
orientation="horizontal"
unit_label="kbps"
stat="activemessagedatareceived"
bar_max="5000"
- tick_spacing="500"
- precision="0"/>
+ tick_spacing="500"/>
<stat_bar name="packet_loss"
label="Packet Loss"
orientation="horizontal"
@@ -53,9 +51,7 @@
stat="packetslostpercentstat"
bar_max="5"
tick_spacing="0.5"
- precision="3"
- show_bar="false"
- show_mean="true"/>
+ show_bar="false"/>
</stat_view>
<!--Advanced Section-->
<stat_view name="advanced"
@@ -73,7 +69,6 @@
stat="numobjectsstat"
bar_max="50000"
tick_spacing="5000"
- precision="0"
show_bar="false"/>
<stat_bar name="newobjs"
label="New Objects"
@@ -109,12 +104,48 @@
<stat_bar name="texture_cache_read_latency"
label="Cache Read Latency"
orientation="horizontal"
- unit_label="msec"
+ unit_label="sec"
stat="texture_cache_read_latency"
bar_max="1000.f"
tick_spacing="100"
show_history="true"
show_bar="false"/>
+ <stat_bar name="texture_decode_latency"
+ label="Cache Decode Latency"
+ orientation="horizontal"
+ unit_label="sec"
+ stat="texture_decode_latency"
+ bar_max="1000.f"
+ tick_spacing="100"
+ show_history="true"
+ show_bar="false"/>
+ <stat_bar name="texture_decode_latency"
+ label="Cache Write Latency"
+ orientation="horizontal"
+ unit_label="sec"
+ stat="texture_write_latency"
+ bar_max="1000.f"
+ tick_spacing="100"
+ show_history="true"
+ show_bar="false"/>
+ <stat_bar name="texture_fetch_latency"
+ label="Cache Fetch Latency"
+ orientation="horizontal"
+ unit_label="sec"
+ stat="texture_fetch_latency"
+ bar_max="1000.f"
+ tick_spacing="100"
+ show_history="true"
+ show_bar="false"/>
+ <stat_bar name="texture_fetch_time"
+ label="Cache Fetch Time"
+ orientation="horizontal"
+ unit_label="sec"
+ stat="texture_fetch_time"
+ bar_max="1000.f"
+ tick_spacing="100"
+ show_history="true"
+ show_bar="false"/>
<stat_bar name="numimagesstat"
label="Count"
orientation="horizontal"
@@ -142,7 +173,6 @@
unit_label="/sec"
bar_max="1024.f"
tick_spacing="128.f"
- precision="1"
show_bar="false"/>
<stat_bar name="packetsoutstat"
label="Packets Out"
@@ -151,7 +181,6 @@
unit_label="/sec"
bar_max="1024.f"
tick_spacing="128.f"
- precision="1"
show_bar="false"/>
<stat_bar name="objectdatareceived"
label="Objects"
@@ -160,7 +189,6 @@
unit_label="kbps"
bar_max="1024.f"
tick_spacing="128.f"
- precision="1"
show_bar="false"/>
<stat_bar name="texturedatareceived"
label="Texture"
@@ -169,7 +197,6 @@
unit_label="kbps"
bar_max="1024.f"
tick_spacing="128.f"
- precision="1"
show_bar="false"/>
<stat_bar name="assetudpdatareceived"
label="Asset"
@@ -178,7 +205,6 @@
unit_label="kbps"
bar_max="1024.f"
tick_spacing="128.f"
- precision="1"
show_bar="false"/>
<stat_bar name="layersdatareceived"
label="Layers"
@@ -187,7 +213,6 @@
unit_label="kbps"
bar_max="1024.f"
tick_spacing="128.f"
- precision="1"
show_bar="false"/>
<stat_bar name="messagedatain"
label="Actual In"
@@ -196,7 +221,6 @@
unit_label="kbps"
bar_max="1024.f"
tick_spacing="128.f"
- precision="1"
show_bar="false"/>
<stat_bar name="messagedataout"
label="Actual Out"
@@ -205,7 +229,6 @@
unit_label="kbps"
bar_max="1024.f"
tick_spacing="128.f"
- precision="1"
show_bar="false"/>
<stat_bar name="vfspendingoperations"
label="VFS Pending Operations"
@@ -224,77 +247,61 @@
label="Objects"
orientation="horizontal"
stat="simobjects"
- precision="0"
bar_max="30000.f"
tick_spacing="5000.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simactiveobjects"
label="Active Objects"
orientation="horizontal"
stat="simactiveobjects"
- precision="0"
bar_max="5000.f"
tick_spacing="750.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simactivescripts"
label="Active Scripts"
orientation="horizontal"
stat="simactivescripts"
- precision="0"
bar_max="15000.f"
tick_spacing="1875.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="siminpps"
label="Packets In"
orientation="horizontal"
stat="siminpps"
unit_label="pps"
- precision="0"
bar_max="2000.f"
tick_spacing="250.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simoutpps"
label="Packets Out"
orientation="horizontal"
stat="simoutpps"
unit_label="pps"
- precision="0"
bar_max="2000.f"
tick_spacing="250.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simpendingdownloads"
label="Pending Downloads"
orientation="horizontal"
stat="simpendingdownloads"
- precision="0"
bar_max="800.f"
tick_spacing="100.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simpendinguploads"
label="Pending Uploads"
orientation="horizontal"
stat="simpendinguploads"
- precision="0"
bar_max="100.f"
tick_spacing="25.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simtotalunackedbytes"
label="Total Unacked Bytes"
orientation="horizontal"
stat="simtotalunackedbytes"
unit_label="kb"
- precision="1"
bar_max="100000.f"
tick_spacing="25000.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_view name="simperf"
label="Time (ms)"
show_label="true">
@@ -303,81 +310,65 @@
orientation="horizontal"
stat="simframemsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simnetmsec"
label="Net Time"
orientation="horizontal"
stat="simnetmsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simsimphysicsmsec"
label="Physics Time"
orientation="horizontal"
stat="simsimphysicsmsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simsimothermsec"
label="Simulation Time"
orientation="horizontal"
stat="simsimothermsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simagentmsec"
label="Agent Time"
orientation="horizontal"
stat="simagentmsec"
unit_label="ms"
- precision="3"
- bar_max="40.f"
+ bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simimagesmsec"
label="Images Time"
orientation="horizontal"
stat="simimagesmsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simscriptmsec"
label="Script Time"
orientation="horizontal"
stat="simscriptmsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simsparemsec"
label="Spare Time"
orientation="horizontal"
stat="simsparemsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<!--2nd level time blocks under 'Details' second-->
<stat_view name="timedetails"
label="Time Details (ms)"
@@ -387,51 +378,41 @@
orientation="horizontal"
stat="simsimphysicsstepmsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simsimphysicsshapeupdatemsec"
label=" Update Phys Shapes"
orientation="horizontal"
stat="simsimphysicsshapeupdatemsec"
unit_label="ms"
- precision="3"
- bar_max="40.f"
+ bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simsimphysicsothermsec"
label=" Physics Other"
orientation="horizontal"
stat="simsimphysicsothermsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simsleepmsec"
label=" Sleep Time"
orientation="horizontal"
stat="simsleepmsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
<stat_bar name="simpumpiomsec"
label=" Pump IO"
orientation="horizontal"
stat="simpumpiomsec"
unit_label="ms"
- precision="3"
bar_max="40.f"
tick_spacing="10.f"
- show_bar="false"
- show_mean="false"/>
+ show_bar="false"/>
</stat_view>
</stat_view>
</stat_view>
diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
index 1ea256b8b3..9278a1a598 100644
--- a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
+++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
@@ -319,6 +319,18 @@
</button>
<button
follows="left|top"
+ height="22"
+ label="Reset Fetching Time"
+ layout="topleft"
+ left_pad="175"
+ name="reset_time_btn"
+ top_delta="0"
+ width="120">
+ <button.commit_callback
+ function="TexFetchDebugger.ResetFetchTime" />
+ </button>
+ <button
+ follows="left|top"
height="20"
label="Cache Read"
layout="topleft"