From e6247fbe78c4d3dacb74f3b2359aba2b6538133b Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Tue, 9 Feb 2010 21:11:42 -0600 Subject: Ported many texture engine changes from Snowglobe. --- ChangeLog.txt | 95 ++ linden/indra/llimage/llimage.cpp | 38 +- linden/indra/llimage/llimage.h | 8 +- linden/indra/llimage/llimagedxt.cpp | 19 +- linden/indra/llimage/llimagej2c.cpp | 5 +- linden/indra/llimage/llimagejpeg.cpp | 9 +- linden/indra/llimage/llimageworker.cpp | 202 ++-- linden/indra/llimage/llimageworker.h | 97 +- linden/indra/llrender/llcubemap.cpp | 4 +- linden/indra/llrender/llimagegl.cpp | 273 ++++- linden/indra/llrender/llimagegl.h | 82 +- linden/indra/llrender/llrender.cpp | 20 +- linden/indra/llrender/llrender.h | 2 +- linden/indra/llrender/llrendertarget.cpp | 4 +- linden/indra/newview/CMakeLists.txt | 8 + linden/indra/newview/app_settings/settings.xml | 44 + linden/indra/newview/llappviewer.cpp | 17 +- linden/indra/newview/llappviewer.h | 6 +- linden/indra/newview/llassetuploadresponders.cpp | 8 + linden/indra/newview/llassetuploadresponders.h | 1 + linden/indra/newview/lldrawable.cpp | 8 +- linden/indra/newview/lldrawable.h | 4 +- linden/indra/newview/lldrawpool.cpp | 5 +- linden/indra/newview/lldrawpoolalpha.cpp | 4 +- linden/indra/newview/lldrawpoolbump.cpp | 14 +- linden/indra/newview/lldrawpoolterrain.cpp | 4 +- linden/indra/newview/lldrawpooltree.cpp | 2 +- linden/indra/newview/lldynamictexture.cpp | 2 +- linden/indra/newview/llface.cpp | 193 +++- linden/indra/newview/llface.h | 21 +- linden/indra/newview/llfloaterassetbrowser.cpp | 2 +- linden/indra/newview/llfloaterreporter.cpp | 4 +- linden/indra/newview/llpreviewtexture.cpp | 8 +- linden/indra/newview/llstartup.cpp | 3 +- linden/indra/newview/lltexlayer.cpp | 29 +- linden/indra/newview/lltexturecache.cpp | 1268 +++++++++++----------- linden/indra/newview/lltexturecache.h | 68 +- linden/indra/newview/lltexturectrl.cpp | 8 +- linden/indra/newview/lltexturefetch.cpp | 1058 ++++++++++-------- linden/indra/newview/lltexturefetch.h | 37 +- linden/indra/newview/lltextureinfo.cpp | 290 +++++ linden/indra/newview/lltextureinfo.h | 80 ++ linden/indra/newview/lltextureinfodetails.cpp | 40 + linden/indra/newview/lltextureinfodetails.h | 58 + linden/indra/newview/lltexturestats.cpp | 61 ++ linden/indra/newview/lltexturestats.h | 41 + linden/indra/newview/lltexturestatsuploader.cpp | 59 + linden/indra/newview/lltexturestatsuploader.h | 48 + linden/indra/newview/lltextureview.cpp | 11 +- linden/indra/newview/llviewercamera.cpp | 20 +- linden/indra/newview/llviewercamera.h | 14 +- linden/indra/newview/llviewercontrol.cpp | 9 + linden/indra/newview/llviewerimage.cpp | 700 +++++++++--- linden/indra/newview/llviewerimage.h | 130 ++- linden/indra/newview/llviewerimagelist.cpp | 143 ++- linden/indra/newview/llviewerimagelist.h | 12 +- linden/indra/newview/llviewermessage.cpp | 1 + linden/indra/newview/llviewerobject.cpp | 6 +- linden/indra/newview/llviewerobject.h | 2 +- linden/indra/newview/llviewerobjectlist.cpp | 2 +- linden/indra/newview/llviewerparceloverlay.cpp | 2 +- linden/indra/newview/llviewerregion.cpp | 4 +- linden/indra/newview/llviewerwindow.cpp | 2 + linden/indra/newview/llvlcomposition.cpp | 12 +- linden/indra/newview/llvoavatar.cpp | 79 +- linden/indra/newview/llvoavatar.h | 2 +- linden/indra/newview/llvoclouds.cpp | 4 +- linden/indra/newview/llvoclouds.h | 2 +- linden/indra/newview/llvograss.cpp | 2 +- linden/indra/newview/llvograss.h | 2 +- linden/indra/newview/llvoground.cpp | 2 +- linden/indra/newview/llvoground.h | 2 +- linden/indra/newview/llvopartgroup.cpp | 2 +- linden/indra/newview/llvopartgroup.h | 2 +- linden/indra/newview/llvosky.cpp | 4 +- linden/indra/newview/llvosky.h | 2 +- linden/indra/newview/llvosurfacepatch.cpp | 2 +- linden/indra/newview/llvosurfacepatch.h | 2 +- linden/indra/newview/llvotextbubble.cpp | 2 +- linden/indra/newview/llvotextbubble.h | 2 +- linden/indra/newview/llvotree.cpp | 2 +- linden/indra/newview/llvotree.h | 2 +- linden/indra/newview/llvotreenew.h | 2 +- linden/indra/newview/llvovolume.cpp | 199 ++-- linden/indra/newview/llvovolume.h | 6 +- linden/indra/newview/llvowater.cpp | 2 +- linden/indra/newview/llvowater.h | 2 +- linden/indra/newview/llworldmapview.cpp | 6 +- linden/indra/newview/primbackup.cpp | 4 +- 89 files changed, 3931 insertions(+), 1838 deletions(-) create mode 100644 linden/indra/newview/lltextureinfo.cpp create mode 100644 linden/indra/newview/lltextureinfo.h create mode 100644 linden/indra/newview/lltextureinfodetails.cpp create mode 100644 linden/indra/newview/lltextureinfodetails.h create mode 100644 linden/indra/newview/lltexturestats.cpp create mode 100644 linden/indra/newview/lltexturestats.h create mode 100644 linden/indra/newview/lltexturestatsuploader.cpp create mode 100644 linden/indra/newview/lltexturestatsuploader.h diff --git a/ChangeLog.txt b/ChangeLog.txt index cf6968f..c8d5ade 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,98 @@ +2010-02-09 Jacek Antonelli + + * Ported many texture engine changes from Snowglobe. + + modified: linden/indra/llimage/llimage.cpp + modified: linden/indra/llimage/llimage.h + modified: linden/indra/llimage/llimagedxt.cpp + modified: linden/indra/llimage/llimagej2c.cpp + modified: linden/indra/llimage/llimagejpeg.cpp + modified: linden/indra/llimage/llimageworker.cpp + modified: linden/indra/llimage/llimageworker.h + modified: linden/indra/llrender/llcubemap.cpp + modified: linden/indra/llrender/llimagegl.cpp + modified: linden/indra/llrender/llimagegl.h + modified: linden/indra/llrender/llrender.cpp + modified: linden/indra/llrender/llrender.h + modified: linden/indra/llrender/llrendertarget.cpp + modified: linden/indra/newview/CMakeLists.txt + modified: linden/indra/newview/app_settings/settings.xml + modified: linden/indra/newview/llappviewer.cpp + modified: linden/indra/newview/llappviewer.h + modified: linden/indra/newview/llassetuploadresponders.cpp + modified: linden/indra/newview/llassetuploadresponders.h + modified: linden/indra/newview/lldrawable.cpp + modified: linden/indra/newview/lldrawable.h + modified: linden/indra/newview/lldrawpool.cpp + modified: linden/indra/newview/lldrawpoolalpha.cpp + modified: linden/indra/newview/lldrawpoolbump.cpp + modified: linden/indra/newview/lldrawpoolterrain.cpp + modified: linden/indra/newview/lldrawpooltree.cpp + modified: linden/indra/newview/lldynamictexture.cpp + modified: linden/indra/newview/llface.cpp + modified: linden/indra/newview/llface.h + modified: linden/indra/newview/llfasttimerview.cpp + modified: linden/indra/newview/llfloaterassetbrowser.cpp + modified: linden/indra/newview/llfloaterreporter.cpp + modified: linden/indra/newview/llpreviewtexture.cpp + modified: linden/indra/newview/llstartup.cpp + modified: linden/indra/newview/lltexlayer.cpp + modified: linden/indra/newview/lltexturecache.cpp + modified: linden/indra/newview/lltexturecache.h + modified: linden/indra/newview/lltexturectrl.cpp + modified: linden/indra/newview/lltexturefetch.cpp + modified: linden/indra/newview/lltexturefetch.h + new file: linden/indra/newview/lltextureinfo.cpp + new file: linden/indra/newview/lltextureinfo.h + new file: linden/indra/newview/lltextureinfodetails.cpp + new file: linden/indra/newview/lltextureinfodetails.h + new file: linden/indra/newview/lltexturestats.cpp + new file: linden/indra/newview/lltexturestats.h + new file: linden/indra/newview/lltexturestatsuploader.cpp + new file: linden/indra/newview/lltexturestatsuploader.h + modified: linden/indra/newview/lltextureview.cpp + modified: linden/indra/newview/llviewercamera.cpp + modified: linden/indra/newview/llviewercamera.h + modified: linden/indra/newview/llviewercontrol.cpp + modified: linden/indra/newview/llviewerimage.cpp + modified: linden/indra/newview/llviewerimage.h + modified: linden/indra/newview/llviewerimagelist.cpp + modified: linden/indra/newview/llviewerimagelist.h + modified: linden/indra/newview/llviewermessage.cpp + modified: linden/indra/newview/llviewerobject.cpp + modified: linden/indra/newview/llviewerobject.h + modified: linden/indra/newview/llviewerobjectlist.cpp + modified: linden/indra/newview/llviewerparceloverlay.cpp + modified: linden/indra/newview/llviewerregion.cpp + modified: linden/indra/newview/llviewerwindow.cpp + modified: linden/indra/newview/llvlcomposition.cpp + modified: linden/indra/newview/llvoavatar.cpp + modified: linden/indra/newview/llvoavatar.h + modified: linden/indra/newview/llvoclouds.cpp + modified: linden/indra/newview/llvoclouds.h + modified: linden/indra/newview/llvograss.cpp + modified: linden/indra/newview/llvograss.h + modified: linden/indra/newview/llvoground.cpp + modified: linden/indra/newview/llvoground.h + modified: linden/indra/newview/llvopartgroup.cpp + modified: linden/indra/newview/llvopartgroup.h + modified: linden/indra/newview/llvosky.cpp + modified: linden/indra/newview/llvosky.h + modified: linden/indra/newview/llvosurfacepatch.cpp + modified: linden/indra/newview/llvosurfacepatch.h + modified: linden/indra/newview/llvotextbubble.cpp + modified: linden/indra/newview/llvotextbubble.h + modified: linden/indra/newview/llvotree.cpp + modified: linden/indra/newview/llvotree.h + modified: linden/indra/newview/llvotreenew.h + modified: linden/indra/newview/llvovolume.cpp + modified: linden/indra/newview/llvovolume.h + modified: linden/indra/newview/llvowater.cpp + modified: linden/indra/newview/llvowater.h + modified: linden/indra/newview/llworldmapview.cpp + modified: linden/indra/newview/primbackup.cpp + + 2010-02-08 Jacek Antonelli * Ported some cURL and HTTP-related changes from Snowglobe. diff --git a/linden/indra/llimage/llimage.cpp b/linden/indra/llimage/llimage.cpp index 4b0076e..32891e7 100644 --- a/linden/indra/llimage/llimage.cpp +++ b/linden/indra/llimage/llimage.cpp @@ -55,13 +55,9 @@ std::string LLImage::sLastErrorMessage; LLMutex* LLImage::sMutex = NULL; //static -void LLImage::initClass(LLWorkerThread* workerthread) +void LLImage::initClass() { sMutex = new LLMutex(NULL); - if (workerthread) - { - LLImageWorker::initImageWorker(workerthread); - } LLImageJ2C::openDSO(); } @@ -69,7 +65,6 @@ void LLImage::initClass(LLWorkerThread* workerthread) void LLImage::cleanupClass() { LLImageJ2C::closeDSO(); - LLImageWorker::cleanupImageWorker(); delete sMutex; sMutex = NULL; } @@ -1242,25 +1237,28 @@ bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip ifs.read ((char*)buffer, length); /* Flawfinder: ignore */ ifs.close(); - image->updateData(); - - if (j2c_lowest_mip_only && codec == IMG_CODEC_J2C) + BOOL success; + + success = image->updateData(); + if (success) { - S32 width = image->getWidth(); - S32 height = image->getHeight(); - S32 discard_level = 0; - while (width > 1 && height > 1 && discard_level < MAX_DISCARD_LEVEL) + if (j2c_lowest_mip_only && codec == IMG_CODEC_J2C) { - width >>= 1; - height >>= 1; - discard_level++; + S32 width = image->getWidth(); + S32 height = image->getHeight(); + S32 discard_level = 0; + while (width > 1 && height > 1 && discard_level < MAX_DISCARD_LEVEL) + { + width >>= 1; + height >>= 1; + discard_level++; + } + ((LLImageJ2C *)((LLImageFormatted*)image))->setDiscardLevel(discard_level); } - ((LLImageJ2C *)((LLImageFormatted*)image))->setDiscardLevel(discard_level); + success = image->decode(this, 100000.0f); } - - BOOL success = image->decode(this, 100000.0f); - image = NULL; // deletes image + image = NULL; // deletes image if (!success) { deleteData(); diff --git a/linden/indra/llimage/llimage.h b/linden/indra/llimage/llimage.h index bd609b6..be2eb08 100644 --- a/linden/indra/llimage/llimage.h +++ b/linden/indra/llimage/llimage.h @@ -49,7 +49,8 @@ const S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE; const S32 MAX_IMAGE_COMPONENTS = 8; const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; -// Note! These CANNOT be changed without invalidating the viewer VFS files, I think? +// Note! These CANNOT be changed without modifying simulator code +// *TODO: change both to 1024 when SIM texture fetching is deprecated const S32 FIRST_PACKET_SIZE = 600; const S32 MAX_IMG_PACKET_SIZE = 1000; @@ -60,7 +61,6 @@ const S32 MAX_IMG_PACKET_SIZE = 1000; class LLImageFormatted; class LLImageRaw; class LLColor4U; -class LLWorkerThread; typedef enum e_image_codec { @@ -81,7 +81,7 @@ typedef enum e_image_codec class LLImage { public: - static void initClass(LLWorkerThread* workerthread); + static void initClass(); static void cleanupClass(); static const std::string& getLastError(); @@ -309,7 +309,7 @@ protected: protected: S8 mCodec; S8 mDecoding; - S8 mDecoded; + S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC S8 mDiscardLevel; public: diff --git a/linden/indra/llimage/llimagedxt.cpp b/linden/indra/llimage/llimagedxt.cpp index 1ce4517..0aa6840 100644 --- a/linden/indra/llimage/llimagedxt.cpp +++ b/linden/indra/llimage/llimagedxt.cpp @@ -264,6 +264,8 @@ void LLImageDXT::setFormat() // virtual BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time) { + // *TODO: Test! This has been tweaked since its intial inception, + // but we don't use it any more! llassert_always(raw_image); if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5) @@ -274,8 +276,17 @@ BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time) S32 width = getWidth(), height = getHeight(); S32 ncomponents = getComponents(); + U8* data = NULL; + if (mDiscardLevel >= 0) + { + data = getData() + getMipOffset(mDiscardLevel); + calcDiscardWidthHeight(mDiscardLevel, mFileFormat, width, height); + } + else + { + data = getData() + getMipOffset(0); + } S32 image_size = formatBytes(mFileFormat, width, height); - U8* data = getData() + getMipOffset(0); if ((!getData()) || (data + image_size > getData() + getDataSize())) { @@ -300,10 +311,8 @@ BOOL LLImageDXT::getMipData(LLPointer& raw, S32 discard) llerrs << "Request for invalid discard level" << llendl; } U8* data = getData() + getMipOffset(discard); - // I'm not sure these are the correct initial values for height and width, - // but previously they were being used uninitialized. JC - S32 width = raw->getWidth(); - S32 height = raw->getHeight(); + S32 width = 0; + S32 height = 0; calcDiscardWidthHeight(discard, mFileFormat, width, height); raw = new LLImageRaw(data, width, height, getComponents()); return TRUE; diff --git a/linden/indra/llimage/llimagej2c.cpp b/linden/indra/llimage/llimagej2c.cpp index ed58f85..1aae83e 100644 --- a/linden/indra/llimage/llimagej2c.cpp +++ b/linden/indra/llimage/llimagej2c.cpp @@ -277,6 +277,7 @@ BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time) } +// Returns TRUE to mean done, whether successful or not. BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count ) { LLMemType mt1((LLMemType::EMemType)mMemType); @@ -289,7 +290,7 @@ BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 fir if (!getData() || (getDataSize() < 16)) { setLastError("LLImageJ2C uninitialized"); - res = FALSE; + res = TRUE; // done } else { @@ -342,7 +343,7 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, //static S32 LLImageJ2C::calcHeaderSizeJ2C() { - return 600; //2048; // ??? hack... just needs to be >= actual header size... + return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size... } //static diff --git a/linden/indra/llimage/llimagejpeg.cpp b/linden/indra/llimage/llimagejpeg.cpp index fa0dd3f..79ea79c 100644 --- a/linden/indra/llimage/llimagejpeg.cpp +++ b/linden/indra/llimage/llimagejpeg.cpp @@ -188,6 +188,7 @@ void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo) } +// Returns true when done, whether or not decode was successful. BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) { llassert_always(raw_image); @@ -198,7 +199,7 @@ BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) if (!getData() || (0 == getDataSize())) { setLastError("LLImageJPEG trying to decode an image with no data!"); - return FALSE; + return TRUE; // done } S32 row_stride = 0; @@ -226,7 +227,7 @@ BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) if(setjmp(sSetjmpBuffer)) { jpeg_destroy_decompress(&cinfo); - return FALSE; + return TRUE; // done } try { @@ -320,7 +321,7 @@ BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) catch (int) { jpeg_destroy_decompress(&cinfo); - return FALSE; + return TRUE; // done } // Check to see whether any corrupt-data warnings occurred @@ -328,7 +329,7 @@ BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time) { // TODO: extract the warning to find out what went wrong. setLastError( "Unable to decode JPEG image."); - return FALSE; + return TRUE; // done } return TRUE; diff --git a/linden/indra/llimage/llimageworker.cpp b/linden/indra/llimage/llimageworker.cpp index 532e996..86d4151 100644 --- a/linden/indra/llimage/llimageworker.cpp +++ b/linden/indra/llimage/llimageworker.cpp @@ -37,152 +37,138 @@ //---------------------------------------------------------------------------- -//static -LLWorkerThread* LLImageWorker::sWorkerThread = NULL; -S32 LLImageWorker::sCount = 0; +// MAIN THREAD +LLImageDecodeThread::LLImageDecodeThread(bool threaded) + : LLQueuedThread("imagedecode", threaded) +{ + mCreationMutex = new LLMutex(getAPRPool()); +} -//static -void LLImageWorker::initImageWorker(LLWorkerThread* workerthread) +// MAIN THREAD +// virtual +S32 LLImageDecodeThread::update(U32 max_time_ms) { - sWorkerThread = workerthread; + LLMutexLock lock(mCreationMutex); + for (creation_list_t::iterator iter = mCreationList.begin(); + iter != mCreationList.end(); ++iter) + { + creation_info& info = *iter; + ImageRequest* req = new ImageRequest(info.handle, info.image, + info.priority, info.discard, info.needs_aux, + info.responder); + addRequest(req); + } + mCreationList.clear(); + S32 res = LLQueuedThread::update(max_time_ms); + return res; } -//static -void LLImageWorker::cleanupImageWorker() +LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image, + U32 priority, S32 discard, BOOL needs_aux, Responder* responder) +{ + LLMutexLock lock(mCreationMutex); + handle_t handle = generateHandle(); + mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder)); + return handle; +} + +// Used by unit test only +// Returns the size of the mutex guarded list as an indication of sanity +S32 LLImageDecodeThread::tut_size() +{ + LLMutexLock lock(mCreationMutex); + S32 res = mCreationList.size(); + return res; +} + +LLImageDecodeThread::Responder::~Responder() { } //---------------------------------------------------------------------------- -LLImageWorker::LLImageWorker(LLImageFormatted* image, U32 priority, - S32 discard, - LLPointer responder) - : LLWorkerClass(sWorkerThread, "Image"), +LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatted* image, + U32 priority, S32 discard, BOOL needs_aux, + LLImageDecodeThread::Responder* responder) + : LLQueuedThread::QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE), mFormattedImage(image), - mDecodedType(-1), mDiscardLevel(discard), - mPriority(priority), + mNeedsAux(needs_aux), + mDecodedRaw(FALSE), + mDecodedAux(FALSE), mResponder(responder) { - ++sCount; } -LLImageWorker::~LLImageWorker() +LLImageDecodeThread::ImageRequest::~ImageRequest() { - mDecodedImage = NULL; + mDecodedImageRaw = NULL; + mDecodedImageAux = NULL; mFormattedImage = NULL; - --sCount; } //---------------------------------------------------------------------------- -//virtual, main thread -void LLImageWorker::startWork(S32 param) -{ - llassert_always(mDecodedImage.isNull()); - mDecodedType = -1; -} -bool LLImageWorker::doWork(S32 param) +// Returns true when done, whether or not decode was successful. +bool LLImageDecodeThread::ImageRequest::processRequest() { - bool decoded = false; - if(mDecodedImage.isNull()) + const F32 decode_time_slice = .1f; + bool done = true; + if (!mDecodedRaw && mFormattedImage.notNull()) { - if (!mFormattedImage->updateData()) - { - mDecodedType = -2; // failed - return true; - } - if (mDiscardLevel >= 0) + // Decode primary channels + if (mDecodedImageRaw.isNull()) { - mFormattedImage->setDiscardLevel(mDiscardLevel); - } - if (!(mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents())) - { - decoded = true; // failed - } - else - { - mDecodedImage = new LLImageRaw(); // allow possibly smaller size set during decoding + // parse formatted header + if (!mFormattedImage->updateData()) + { + return true; // done (failed) + } + if (!(mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents())) + { + return true; // done (failed) + } + if (mDiscardLevel >= 0) + { + mFormattedImage->setDiscardLevel(mDiscardLevel); + } + mDecodedImageRaw = new LLImageRaw(mFormattedImage->getWidth(), + mFormattedImage->getHeight(), + mFormattedImage->getComponents()); } + done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms + mDecodedRaw = done; } - if (!decoded) + if (done && mNeedsAux && !mDecodedAux && mFormattedImage.notNull()) { - if (param == 0) - { - // Decode primary channels - decoded = mFormattedImage->decode(mDecodedImage, .1f); // 1ms - } - else + // Decode aux channel + if (!mDecodedImageAux) { - // Decode aux channel - decoded = mFormattedImage->decodeChannels(mDecodedImage, .1f, param, param); // 1ms + mDecodedImageAux = new LLImageRaw(mFormattedImage->getWidth(), + mFormattedImage->getHeight(), + 1); } + done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms + mDecodedAux = done; } - if (decoded) - { - // Call the callback immediately; endWork doesn't get called until ckeckWork - if (mResponder.notNull()) - { - bool success = (!wasAborted() && mDecodedImage.notNull() && mDecodedImage->getDataSize() != 0); - mResponder->completed(success); - } - } - return decoded; -} -void LLImageWorker::endWork(S32 param, bool aborted) -{ - if (mDecodedType != -2) - { - mDecodedType = aborted ? -2 : param; - } + return done; } -//---------------------------------------------------------------------------- - - -BOOL LLImageWorker::requestDecodedAuxData(LLPointer& raw, S32 channel, S32 discard) +void LLImageDecodeThread::ImageRequest::finishRequest(bool completed) { - // For most codecs, only mDiscardLevel data is available. - // (see LLImageDXT for exception) - if (discard >= 0 && discard != mFormattedImage->getDiscardLevel()) - { - llerrs << "Request for invalid discard level" << llendl; - } - checkWork(); - if (mDecodedType == -2) + if (mResponder.notNull()) { - return TRUE; // aborted, done - } - if (mDecodedType != channel) - { - if (!haveWork()) - { - addWork(channel, mPriority); - } - return FALSE; - } - else - { - llassert_always(!haveWork()); - llassert_always(mDecodedType == channel); - raw = mDecodedImage; // smart pointer acquires ownership of data - mDecodedImage = NULL; - return TRUE; + bool success = completed && mDecodedRaw && (!mNeedsAux || mDecodedAux); + mResponder->completed(success, mDecodedImageRaw, mDecodedImageAux); } + // Will automatically be deleted } -BOOL LLImageWorker::requestDecodedData(LLPointer& raw, S32 discard) +// 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() { - if (mFormattedImage->getCodec() == IMG_CODEC_DXT) - { - // special case - LLImageDXT* imagedxt = (LLImageDXT*)((LLImageFormatted*)mFormattedImage); - return imagedxt->getMipData(raw, discard); - } - else - { - return requestDecodedAuxData(raw, 0, discard); - } + return mResponder.notNull(); } diff --git a/linden/indra/llimage/llimageworker.h b/linden/indra/llimage/llimageworker.h index 879fcf5..fcd3039 100644 --- a/linden/indra/llimage/llimageworker.h +++ b/linden/indra/llimage/llimageworker.h @@ -34,51 +34,74 @@ #define LL_LLIMAGEWORKER_H #include "llimage.h" -#include "llworkerthread.h" +#include "llqueuedthread.h" -class LLImageWorker : public LLWorkerClass +class LLImageDecodeThread : public LLQueuedThread { public: - static void initImageWorker(LLWorkerThread* workerthread); - static void cleanupImageWorker(); - -public: - static LLWorkerThread* getWorkerThread() { return sWorkerThread; } - - // LLWorkerThread -public: - LLImageWorker(LLImageFormatted* image, U32 priority, S32 discard, - LLPointer responder); - ~LLImageWorker(); - - // called from WORKER THREAD, returns TRUE if done - /*virtual*/ bool doWork(S32 param); - - BOOL requestDecodedData(LLPointer& raw, S32 discard = -1); - BOOL requestDecodedAuxData(LLPointer& raw, S32 channel, S32 discard = -1); - void releaseDecodedData(); - void cancelDecode(); + class Responder : public LLThreadSafeRefCount + { + protected: + virtual ~Responder(); + public: + virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) = 0; + }; -private: - // called from MAIN THREAD - /*virtual*/ void startWork(S32 param); // called from addWork() - /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() + class ImageRequest : public LLQueuedThread::QueuedRequest + { + protected: + virtual ~ImageRequest(); // use deleteRequest() + + public: + ImageRequest(handle_t handle, LLImageFormatted* image, + U32 priority, S32 discard, BOOL needs_aux, + LLImageDecodeThread::Responder* responder); -protected: - LLPointer mFormattedImage; - LLPointer mDecodedImage; - S32 mDecodedType; - S32 mDiscardLevel; + /*virtual*/ bool processRequest(); + /*virtual*/ void finishRequest(bool completed); -private: - U32 mPriority; - LLPointer mResponder; + // Used by unit tests to check the consitency of the request instance + bool tut_isOK(); + + private: + // input + LLPointer mFormattedImage; + S32 mDiscardLevel; + BOOL mNeedsAux; + // output + LLPointer mDecodedImageRaw; + LLPointer mDecodedImageAux; + BOOL mDecodedRaw; + BOOL mDecodedAux; + LLPointer mResponder; + }; -protected: - static LLWorkerThread* sWorkerThread; - public: - static S32 sCount; + LLImageDecodeThread(bool threaded = true); + handle_t decodeImage(LLImageFormatted* image, + U32 priority, S32 discard, BOOL needs_aux, + Responder* responder); + S32 update(U32 max_time_ms); + + // Used by unit tests to check the consistency of the thread instance + S32 tut_size(); + +private: + struct creation_info + { + handle_t handle; + LLImageFormatted* image; + U32 priority; + S32 discard; + BOOL needs_aux; + LLPointer responder; + creation_info(handle_t h, LLImageFormatted* i, U32 p, S32 d, BOOL aux, Responder* r) + : handle(h), image(i), priority(p), discard(d), needs_aux(aux), responder(r) + {} + }; + typedef std::list creation_list_t; + creation_list_t mCreationList; + LLMutex* mCreationMutex; }; #endif diff --git a/linden/indra/llrender/llcubemap.cpp b/linden/indra/llrender/llcubemap.cpp index 754d90c..a5c677d 100644 --- a/linden/indra/llrender/llcubemap.cpp +++ b/linden/indra/llrender/llcubemap.cpp @@ -94,7 +94,7 @@ void LLCubeMap::initGL() mImages[i] = new LLImageGL(64, 64, 4, (use_cube_mipmaps? TRUE : FALSE)); mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP); mRawImages[i] = new LLImageRaw(64, 64, 4); - mImages[i]->createGLTexture(0, mRawImages[i], texname); + mImages[i]->createGLTexture(0, mRawImages[i], texname, TRUE); gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname); mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP); @@ -106,7 +106,7 @@ void LLCubeMap::initGL() } else { - llwarns << "Using cube map without extension!" << llendl + llwarns << "Using cube map without extension!" << llendl; } } diff --git a/linden/indra/llrender/llimagegl.cpp b/linden/indra/llrender/llimagegl.cpp index d1efb11..27b1d55 100644 --- a/linden/indra/llrender/llimagegl.cpp +++ b/linden/indra/llrender/llimagegl.cpp @@ -43,8 +43,6 @@ #include "llmath.h" #include "llgl.h" #include "llrender.h" - - //---------------------------------------------------------------------------- const F32 MIN_TEXTURE_LIFETIME = 10.f; @@ -61,8 +59,33 @@ S32 LLImageGL::sCount = 0; BOOL LLImageGL::sGlobalUseAnisotropic = FALSE; F32 LLImageGL::sLastFrameTime = 0.f; +BOOL LLImageGL::sAllowReadBackRaw = FALSE ; std::set LLImageGL::sImageList; + +//**************************************************************************************************** +//The below for texture auditing use only +//**************************************************************************************************** +//----------------------- +//debug use +BOOL gAuditTexture = FALSE ; +#define MAX_TEXTURE_LOG_SIZE 22 //2048 * 2048 +std::vector LLImageGL::sTextureLoadedCounter(MAX_TEXTURE_LOG_SIZE + 1) ; +std::vector LLImageGL::sTextureBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ; +std::vector LLImageGL::sTextureCurBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ; +S32 LLImageGL::sCurTexSizeBar = -1 ; +S32 LLImageGL::sCurTexPickSize = -1 ; +LLPointer LLImageGL::sDefaultTexturep = NULL; +S32 LLImageGL::sMaxCatagories = 1 ; + +std::vector LLImageGL::sTextureMemByCategory; +std::vector LLImageGL::sTextureMemByCategoryBound ; +std::vector LLImageGL::sTextureCurMemByCategoryBound ; +//------------------------ +//**************************************************************************************************** +//End for texture auditing use only +//**************************************************************************************************** + //************************************************************************************** //below are functions for debug use //do not delete them even though they are not currently being used. @@ -110,6 +133,20 @@ void LLImageGL::checkTexSize() const //************************************************************************************** //---------------------------------------------------------------------------- +//static +void LLImageGL::initClass(S32 num_catagories) +{ + sMaxCatagories = num_catagories ; + + sTextureMemByCategory.resize(sMaxCatagories); + sTextureMemByCategoryBound.resize(sMaxCatagories) ; + sTextureCurMemByCategoryBound.resize(sMaxCatagories) ; +} + +//static +void LLImageGL::cleanupClass() +{ +} //static S32 LLImageGL::dataFormatBits(S32 dataformat) @@ -176,12 +213,44 @@ void LLImageGL::updateStats(F32 current_time) sLastFrameTime = current_time; sBoundTextureMemoryInBytes = sCurBoundTextureMemory; sCurBoundTextureMemory = 0; + + if(gAuditTexture) + { + for(U32 i = 0 ; i < sTextureCurBoundCounter.size() ; i++) + { + sTextureBoundCounter[i] = sTextureCurBoundCounter[i] ; + sTextureCurBoundCounter[i] = 0 ; + } + for(U32 i = 0 ; i < sTextureCurMemByCategoryBound.size() ; i++) + { + sTextureMemByCategoryBound[i] = sTextureCurMemByCategoryBound[i] ; + sTextureCurMemByCategoryBound[i] = 0 ; + } + } } //static -S32 LLImageGL::updateBoundTexMem(const S32 delta) +S32 LLImageGL::updateBoundTexMemStatic(const S32 delta, const S32 size, S32 category) +{ + if(gAuditTexture) + { + sTextureCurBoundCounter[getTextureCounterIndex(size)]++ ; + sTextureCurMemByCategoryBound[category] += delta ; + } + + LLImageGL::sCurBoundTextureMemory += delta ; + return LLImageGL::sCurBoundTextureMemory; +} + +S32 LLImageGL::updateBoundTexMem()const { - LLImageGL::sCurBoundTextureMemory += delta; + if(gAuditTexture) + { + sTextureCurBoundCounter[getTextureCounterIndex(mTextureMemory / mComponents)]++ ; + sTextureCurMemByCategoryBound[mCategory] += mTextureMemory ; + } + + LLImageGL::sCurBoundTextureMemory += mTextureMemory ; return LLImageGL::sCurBoundTextureMemory; } @@ -195,6 +264,7 @@ void LLImageGL::destroyGL(BOOL save_state) gGL.getTexUnit(stage)->unbind(LLTexUnit::TT_TEXTURE); } + sAllowReadBackRaw = true ; for (std::set::iterator iter = sImageList.begin(); iter != sImageList.end(); iter++) { @@ -204,7 +274,7 @@ void LLImageGL::destroyGL(BOOL save_state) if (save_state && glimage->isGLTextureCreated() && glimage->mComponents) { glimage->mSaveData = new LLImageRaw; - if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) + if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it. { glimage->mSaveData = NULL ; } @@ -214,6 +284,7 @@ void LLImageGL::destroyGL(BOOL save_state) stop_glerror(); } } + sAllowReadBackRaw = false ; } //static @@ -231,7 +302,7 @@ void LLImageGL::restoreGL() { if (glimage->getComponents() && glimage->mSaveData->getComponents()) { - glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData); + glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, TRUE, glimage->getCategory()); stop_glerror(); } glimage->mSaveData = NULL; // deletes data @@ -311,7 +382,7 @@ void LLImageGL::init(BOOL usemipmaps) mTextureState = NO_DELETE ; mTextureMemory = 0; mLastBindTime = 0.f; - + mTarget = GL_TEXTURE_2D; mBindTarget = LLTexUnit::TT_TEXTURE; mUseMipMaps = usemipmaps; @@ -338,7 +409,9 @@ void LLImageGL::init(BOOL usemipmaps) mHasExplicitFormat = FALSE; mGLTextureCreated = FALSE ; + mIsMask = FALSE; + mCategory = -1 ; } void LLImageGL::cleanup() @@ -438,6 +511,10 @@ void LLImageGL::dump() } //---------------------------------------------------------------------------- +void LLImageGL::forceUpdateBindStats(void) const +{ + mLastBindTime = sLastFrameTime; +} void LLImageGL::updateBindStats(void) const { @@ -451,7 +528,8 @@ void LLImageGL::updateBindStats(void) const { // we haven't accounted for this texture yet this frame sUniqueCount++; - updateBoundTexMem(mTextureMemory); + + updateBoundTexMem(); mLastBindTime = sLastFrameTime; } } @@ -464,7 +542,7 @@ bool LLImageGL::bindError(const S32 stage) const } //virtual -bool LLImageGL::bindDefaultImage(const S32 stage) const +bool LLImageGL::bindDefaultImage(const S32 stage) { return false; } @@ -503,7 +581,6 @@ void LLImageGL::setImage(const LLImageRaw* imageraw) void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) { // LLFastTimer t1(LLFastTimer::FTM_TEMP1); - llpushcallstacks ; bool is_compressed = false; if (mFormatPrimary >= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && mFormatPrimary <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) { @@ -716,12 +793,10 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } stop_glerror(); mGLTextureCreated = true; - llpushcallstacks ; } -BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height) +BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update) { - llpushcallstacks ; if (!width || !height) { return TRUE; @@ -737,7 +812,8 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 return FALSE; } - if (x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height) + // HACK: allow the caller to explicitly force the fast path (i.e. using glTexSubImage2D here instead of calling setImage) even when updating the full texture. + if (!force_fast_update && x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height) { setImage(datap, FALSE); } @@ -810,20 +886,20 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 stop_glerror(); mGLTextureCreated = true; } - llpushcallstacks ; return TRUE; } -BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height) +BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update) { - return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height); + return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update); } // Copy sub image from frame buffer BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height) { - if (gGL.getTexUnit(0)->bind(this, true)) + if (gGL.getTexUnit(0)->bind(this, false, true)) { + //checkTexSize() ; glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height); mGLTextureCreated = true; stop_glerror(); @@ -883,14 +959,14 @@ BOOL LLImageGL::createGLTexture() return TRUE ; } -BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/) +BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, BOOL to_create, S32 category) { - llpushcallstacks ; if (gGLManager.mIsDisabled) { llwarns << "Trying to create a texture while GL is disabled!" << llendl; return FALSE; } + mGLTextureCreated = false ; llassert(gGLManager.mInited); stop_glerror(); @@ -903,8 +979,10 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel); // Actual image width/height = raw image width/height * 2^discard_level - S32 w = imageraw->getWidth() << discard_level; - S32 h = imageraw->getHeight() << discard_level; + S32 raw_w = imageraw->getWidth() ; + S32 raw_h = imageraw->getHeight() ; + S32 w = raw_w << discard_level; + S32 h = raw_h << discard_level; // setSize may call destroyGLTexture if the size does not match setSize(w, h, imageraw->getComponents()); @@ -940,13 +1018,21 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S } } + if(!to_create) //not create a gl texture + { + destroyGLTexture(); + mCurrentDiscardLevel = discard_level; + mLastBindTime = sLastFrameTime; + return TRUE ; + } + + mCategory = category ; const U8* rawdata = imageraw->getData(); return createGLTexture(discard_level, rawdata, FALSE, usename); } BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename) { - llpushcallstacks ; llassert(data_in); if (discard_level < 0) @@ -1014,7 +1100,14 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ if (old_name != 0) { sGlobalTextureMemoryInBytes -= mTextureMemory; + + if(gAuditTexture) + { + decTextureCounter() ; + } + LLImageGL::deleteTextures(1, &old_name); + stop_glerror(); } @@ -1022,13 +1115,15 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ sGlobalTextureMemoryInBytes += mTextureMemory; setActive() ; + if(gAuditTexture) + { + incTextureCounter() ; + } // mark this as bound at this point, so we don't throw it out immediately mLastBindTime = sLastFrameTime; - - llpushcallstacks ; return TRUE; } - +#if 0 BOOL LLImageGL::setDiscardLevel(S32 discard_level) { llassert(discard_level >= 0); @@ -1080,25 +1175,14 @@ BOOL LLImageGL::setDiscardLevel(S32 discard_level) return FALSE; } } - -BOOL LLImageGL::isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) -{ - assert_glerror(); - S32 gl_discard = discard_level - mCurrentDiscardLevel; - LLGLint glwidth = 0; - glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_WIDTH, (GLint*)&glwidth); - LLGLint glheight = 0; - glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_HEIGHT, (GLint*)&glheight); - LLGLint glcomponents = 0 ; - glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&glcomponents); - assert_glerror(); - - return glwidth >= image_width && glheight >= image_height && (GL_RGB8 == glcomponents || GL_RGBA8 == glcomponents) ; -} +#endif BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) { - llpushcallstacks ; + // VWR-13505 : Merov : Allow gl texture read back so save texture works again (temporary) + //llassert_always(sAllowReadBackRaw) ; + //llerrs << "should not call this function!" << llendl ; + if (discard_level < 0) { discard_level = mCurrentDiscardLevel; @@ -1201,7 +1285,7 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre return FALSE ; } //----------------------------------------------------------------------------------------------- - llpushcallstacks ; + return TRUE ; } @@ -1219,12 +1303,19 @@ void LLImageGL::destroyGLTexture() stop_glerror(); } } - - sGlobalTextureMemoryInBytes -= mTextureMemory; - mTextureMemory = 0; - + + if(mTextureMemory) + { + if(gAuditTexture) + { + decTextureCounter() ; + } + sGlobalTextureMemoryInBytes -= mTextureMemory; + mTextureMemory = 0; + } + LLImageGL::deleteTextures(1, &mTexName); - mTextureState = DELETED ; + mTextureState = DELETED ; mTexName = 0; mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel. mGLTextureCreated = FALSE ; @@ -1338,6 +1429,11 @@ S32 LLImageGL::getMipBytes(S32 discard_level) const return res; } +BOOL LLImageGL::isJustBound() const +{ + return (BOOL)(sLastFrameTime - mLastBindTime < 0.5f); +} + BOOL LLImageGL::getBoundRecently() const { return (BOOL)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME); @@ -1539,6 +1635,87 @@ BOOL LLImageGL::getMask(const LLVector2 &tc) return res; } +void LLImageGL::setCategory(S32 category) +{ + if(!gAuditTexture) + { + return ; + } + if(mCategory != category) + { + if(mCategory > -1) + { + sTextureMemByCategory[mCategory] -= mTextureMemory ; + } + sTextureMemByCategory[category] += mTextureMemory ; + + mCategory = category; + } +} + +//for debug use +//val is a "power of two" number +S32 LLImageGL::getTextureCounterIndex(U32 val) +{ + //index range is [0, MAX_TEXTURE_LOG_SIZE]. + if(val < 2) + { + return 0 ; + } + else if(val >= (1 << MAX_TEXTURE_LOG_SIZE)) + { + return MAX_TEXTURE_LOG_SIZE ; + } + else + { + S32 ret = 0 ; + while(val >>= 1) + { + ++ret; + } + return ret ; + } +} +void LLImageGL::incTextureCounterStatic(U32 val, S32 ncomponents, S32 category) +{ + sTextureLoadedCounter[getTextureCounterIndex(val)]++ ; + sTextureMemByCategory[category] += (S32)val * ncomponents ; +} +void LLImageGL::decTextureCounterStatic(U32 val, S32 ncomponents, S32 category) +{ + sTextureLoadedCounter[getTextureCounterIndex(val)]-- ; + sTextureMemByCategory[category] += (S32)val * ncomponents ; +} +void LLImageGL::incTextureCounter() +{ + sTextureLoadedCounter[getTextureCounterIndex(mTextureMemory / mComponents)]++ ; + sTextureMemByCategory[mCategory] += mTextureMemory ; +} +void LLImageGL::decTextureCounter() +{ + sTextureLoadedCounter[getTextureCounterIndex(mTextureMemory / mComponents)]-- ; + sTextureMemByCategory[mCategory] -= mTextureMemory ; +} +void LLImageGL::setCurTexSizebar(S32 index, BOOL set_pick_size) +{ + sCurTexSizeBar = index ; + + if(set_pick_size) + { + sCurTexPickSize = (1 << index) ; + } + else + { + sCurTexPickSize = -1 ; + } +} +void LLImageGL::resetCurTexSizebar() +{ + sCurTexSizeBar = -1 ; + sCurTexPickSize = -1 ; +} +//---------------------------------------------------------------------------- + //---------------------------------------------------------------------------- diff --git a/linden/indra/llrender/llimagegl.h b/linden/indra/llrender/llimagegl.h index 2f08a5a..c8df101 100644 --- a/linden/indra/llrender/llimagegl.h +++ b/linden/indra/llrender/llimagegl.h @@ -46,7 +46,6 @@ #define MEGA_BYTES_TO_BYTES(x) ((x) << 20) //============================================================================ - class LLImageGL : public LLRefCount { friend class LLTexUnit; @@ -57,6 +56,7 @@ public: static S32 dataFormatComponents(S32 dataformat); void updateBindStats(void) const; + void forceUpdateBindStats(void) const; // needs to be called every frame static void updateStats(F32 current_time); @@ -65,9 +65,10 @@ public: static void destroyGL(BOOL save_state = TRUE); static void restoreGL(); - // Sometimes called externally for textures not using LLImageGL (should go away...) - static S32 updateBoundTexMem(const S32 delta); - + // Sometimes called externally for textures not using LLImageGL (should go away...) + static S32 updateBoundTexMemStatic(const S32 delta, const S32 size, S32 category) ; + S32 updateBoundTexMem()const; + static bool checkSize(S32 width, S32 height); // Not currently necessary for LLImageGL, but required in some derived classes, @@ -89,7 +90,7 @@ protected: public: virtual void dump(); // debugging info to llinfos virtual bool bindError(const S32 stage = 0) const; - virtual bool bindDefaultImage(const S32 stage = 0) const; + virtual bool bindDefaultImage(const S32 stage = 0) ; virtual void forceImmediateUpdate() ; void setSize(S32 width, S32 height, S32 ncomponents); @@ -101,20 +102,22 @@ public: static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels); BOOL createGLTexture() ; - BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0); + BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, + S32 category = sMaxCatagories - 1); BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0); void setImage(const LLImageRaw* imageraw); void setImage(const U8* data_in, BOOL data_hasmips = FALSE); - BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height); - BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height); + BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE); + BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE); BOOL setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height); - BOOL setDiscardLevel(S32 discard_level); + // Read back a raw image for this discard level, if it exists BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok); void destroyGLTexture(); void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, BOOL swap_bytes = FALSE); void dontDiscard() { mDontDiscard = 1; mTextureState = NO_DELETE; } + void setComponents(S8 ncomponents) { mComponents = ncomponents; } S32 getDiscardLevel() const { return mCurrentDiscardLevel; } S32 getMaxDiscardLevel() const { return mMaxDiscardLevel; } @@ -127,6 +130,7 @@ public: S32 getBytes(S32 discard_level = -1) const; S32 getMipBytes(S32 discard_level = -1) const; BOOL getBoundRecently() const; + BOOL isJustBound() const; LLGLenum getPrimaryFormat() const { return mFormatPrimary; } BOOL getHasGLTexture() const { return mTexName != 0; } @@ -147,8 +151,6 @@ public: BOOL getUseDiscard() const { return mUseMipMaps && !mDontDiscard; } BOOL getDontDiscard() const { return mDontDiscard; } - BOOL isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) ; - void updatePickMask(S32 width, S32 height, const U8* data_in); BOOL getMask(const LLVector2 &tc); @@ -175,6 +177,7 @@ public: void forceActive() ; void setNoDelete() ; + void setTextureSize(S32 size) {mTextureMemory = size;} protected: void init(BOOL usemipmaps); virtual void cleanup(); // Clean up the LLImageGL so it can be reinitialized. Be careful when using this in derived class destructors @@ -183,7 +186,7 @@ public: // Various GL/Rendering options S32 mTextureMemory; mutable F32 mLastBindTime; // last time this was bound, by discard level - + private: LLPointer mSaveData; // used for destroyGL/restoreGL U8* mPickMask; //downsampled bitmap approximation of alpha channel. NULL if no alpha channel @@ -198,7 +201,7 @@ private: U16 mWidth; U16 mHeight; S8 mCurrentDiscardLevel; - + protected: LLGLenum mTarget; // Normally GL_TEXTURE2D, sometimes something else (ex. cube maps) LLTexUnit::eTextureType mBindTarget; // Normally TT_TEXTURE, sometimes something else (ex. cube maps) @@ -237,7 +240,7 @@ public: static S32 sCount; static F32 sLastFrameTime; - + static LLGLuint sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS]; // Currently bound texture ID // Global memory statistics @@ -253,6 +256,57 @@ public: #else BOOL getMissed() const { return FALSE; }; #endif + +public: + static void initClass(S32 num_catagories) ; + static void cleanupClass() ; +private: + static S32 sMaxCatagories ; + + //the flag to allow to call readBackRaw(...). + //can be removed if we do not use that function at all. + static BOOL sAllowReadBackRaw ; +// +//**************************************************************************************************** +//The below for texture auditing use only +//**************************************************************************************************** +private: + S32 mCategory ; +public: + void setCategory(S32 category) ; + S32 getCategory()const {return mCategory ;} + + //for debug use: show texture size distribution + //---------------------------------------- + static LLPointer sDefaultTexturep; //default texture to replace normal textures + static std::vector sTextureLoadedCounter ; + static std::vector sTextureBoundCounter ; + static std::vector sTextureCurBoundCounter ; + static S32 sCurTexSizeBar ; + static S32 sCurTexPickSize ; + + static S32 getTextureCounterIndex(U32 val) ; + static void incTextureCounterStatic(U32 val, S32 ncomponents, S32 category) ; + static void decTextureCounterStatic(U32 val, S32 ncomponents, S32 category) ; + static void setCurTexSizebar(S32 index, BOOL set_pick_size = TRUE) ; + static void resetCurTexSizebar(); + + void incTextureCounter() ; + void decTextureCounter() ; + //---------------------------------------- + + //for debug use: show texture category distribution + //---------------------------------------- + + static std::vector sTextureMemByCategory; + static std::vector sTextureMemByCategoryBound ; + static std::vector sTextureCurMemByCategoryBound ; + //---------------------------------------- +//**************************************************************************************************** +//End of definitions for texture auditing use only +//**************************************************************************************************** + }; +extern BOOL gAuditTexture; #endif // LL_LLIMAGEGL_H diff --git a/linden/indra/llrender/llrender.cpp b/linden/indra/llrender/llrender.cpp index ba95a19..93ff822 100644 --- a/linden/indra/llrender/llrender.cpp +++ b/linden/indra/llrender/llrender.cpp @@ -177,7 +177,7 @@ void LLTexUnit::disable(void) } } -bool LLTexUnit::bind(LLImageGL* texture, bool forceBind) +bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind) { stop_glerror(); if (mIndex < 0) return false; @@ -192,12 +192,25 @@ bool LLTexUnit::bind(LLImageGL* texture, bool forceBind) if (!texture->getTexName()) //if texture does not exist { - //if deleted, will re-generate it immediately - texture->forceImmediateUpdate() ; + if (texture->isDeleted()) + { + // This will re-generate the texture immediately. + texture->forceImmediateUpdate() ; + } + texture->forceUpdateBindStats() ; return texture->bindDefaultImage(mIndex); } + if(gAuditTexture && for_rendering && LLImageGL::sCurTexPickSize > 0) + { + if(texture->getWidth() * texture->getHeight() == LLImageGL::sCurTexPickSize) + { + texture->updateBindStats(); + return bind(LLImageGL::sDefaultTexturep.get()); + } + } + if ((mCurrTexture != texture->getTexName()) || forceBind) { activate(); @@ -214,6 +227,7 @@ bool LLTexUnit::bind(LLImageGL* texture, bool forceBind) setTextureFilteringOption(texture->mFilterOption); } } + return true; } diff --git a/linden/indra/llrender/llrender.h b/linden/indra/llrender/llrender.h index 437c715..ce84e74 100644 --- a/linden/indra/llrender/llrender.h +++ b/linden/indra/llrender/llrender.h @@ -148,7 +148,7 @@ public: // Binds the LLImageGL to this texture unit // (automatically enables the unit for the LLImageGL's texture type) - bool bind(LLImageGL* texture, bool forceBind = false); + bool bind(LLImageGL* texture, bool for_rendering = false, bool forceBind = false); // Binds a cubemap to this texture unit // (automatically enables the texture unit for cubemaps) diff --git a/linden/indra/llrender/llrendertarget.cpp b/linden/indra/llrender/llrendertarget.cpp index b7f3177..4cf8451 100644 --- a/linden/indra/llrender/llrendertarget.cpp +++ b/linden/indra/llrender/llrendertarget.cpp @@ -139,7 +139,7 @@ void LLRenderTarget::addColorAttachment(U32 color_fmt) U32 offset = mTex.size(); if (offset >= 4 || - offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers)) + (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers))) { llerrs << "Too many color attachments!" << llendl; } @@ -608,7 +608,7 @@ void LLMultisampleBuffer::addColorAttachment(U32 color_fmt) U32 offset = mTex.size(); if (offset >= 4 || - offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers)) + (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers))) { llerrs << "Too many color attachments!" << llendl; } diff --git a/linden/indra/newview/CMakeLists.txt b/linden/indra/newview/CMakeLists.txt index e451465..d5b50cf 100644 --- a/linden/indra/newview/CMakeLists.txt +++ b/linden/indra/newview/CMakeLists.txt @@ -340,6 +340,10 @@ set(viewer_SOURCE_FILES lltexturecache.cpp lltexturectrl.cpp lltexturefetch.cpp + lltextureinfo.cpp + lltextureinfodetails.cpp + lltexturestats.cpp + lltexturestatsuploader.cpp lltextureview.cpp lltoolbar.cpp lltoolbrush.cpp @@ -760,6 +764,10 @@ set(viewer_HEADER_FILES lltexturecache.h lltexturectrl.h lltexturefetch.h + lltextureinfo.h + lltextureinfodetails.h + lltexturestats.h + lltexturestatsuploader.h lltextureview.h lltool.h lltoolbar.h diff --git a/linden/indra/newview/app_settings/settings.xml b/linden/indra/newview/app_settings/settings.xml index f8594ad..9fea0d8 100644 --- a/linden/indra/newview/app_settings/settings.xml +++ b/linden/indra/newview/app_settings/settings.xml @@ -523,6 +523,17 @@ Value 1 + AuditTexture + + Comment + Enable texture auditting. + Persist + 1 + Type + Boolean + Value + 0 + AutoAcceptNewInventory Comment @@ -5811,6 +5822,28 @@ Value http://imprudenceviewer.org/splash/ + LogTextureDownloadsToViewerLog + + Comment + Send texture download details to the viewer log + Persist + 1 + Type + Boolean + Value + 0 + + LogTextureDownloadsToSimulator + + Comment + Send a digest of texture info to the sim + Persist + 1 + Type + Boolean + Value + 0 + LosslessJ2CUpload Comment @@ -9776,6 +9809,17 @@ Value 20.0 + TextureLoggingThreshold + + Comment + Specifies the byte threshold at which texture download data should be sent to the sim. + Persist + 1 + Type + U32 + Value + 1 + TextureMemory Comment diff --git a/linden/indra/newview/llappviewer.cpp b/linden/indra/newview/llappviewer.cpp index cdecaf4..a630beb 100644 --- a/linden/indra/newview/llappviewer.cpp +++ b/linden/indra/newview/llappviewer.cpp @@ -57,9 +57,12 @@ #include "llares.h" #include "llcurl.h" #include "llfloatersnapshot.h" +#include "lltexturestats.h" #include "llviewerwindow.h" #include "llviewerdisplay.h" #include "llviewermedia.h" + + #include "llviewermessage.h" #include "llviewerobjectlist.h" #include "llworldmap.h" @@ -435,7 +438,7 @@ static void settings_modify() LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4] gDebugGL = gSavedSettings.getBOOL("RenderDebugGL"); gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline"); - + gAuditTexture = gSavedSettings.getBOOL("AuditTexture"); #if LL_VECTORIZE if (gSysCPU.hasAltivec()) { @@ -527,7 +530,7 @@ const std::string LLAppViewer::sPerAccountSettingsName = "PerAccount"; const std::string LLAppViewer::sCrashSettingsName = "CrashSettings"; LLTextureCache* LLAppViewer::sTextureCache = NULL; -LLWorkerThread* LLAppViewer::sImageDecodeThread = NULL; +LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; LLTextureFetch* LLAppViewer::sTextureFetch = NULL; LLAppViewer::LLAppViewer() : @@ -1512,14 +1515,14 @@ bool LLAppViewer::initThreads() LLWatchdog::getInstance()->init(watchdog_killer_callback); } - LLVFSThread::initClass(enable_threads && true); - LLLFSThread::initClass(enable_threads && true); + LLVFSThread::initClass(enable_threads && false); + LLLFSThread::initClass(enable_threads && false); // Image decoding - LLAppViewer::sImageDecodeThread = new LLWorkerThread("ImageDecode", enable_threads && true); + LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true); LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true); - LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), enable_threads && false); - LLImage::initClass(LLAppViewer::getImageDecodeThread()); + LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread, enable_threads && true); + LLImage::initClass(); // *FIX: no error handling here! return true; diff --git a/linden/indra/newview/llappviewer.h b/linden/indra/newview/llappviewer.h index ada5557..9d72457 100644 --- a/linden/indra/newview/llappviewer.h +++ b/linden/indra/newview/llappviewer.h @@ -34,7 +34,7 @@ #define LL_LLAPPVIEWER_H class LLTextureCache; -class LLWorkerThread; +class LLImageDecodeThread; class LLTextureFetch; class LLWatchdogTimeout; class LLCommandLineParser; @@ -91,7 +91,7 @@ public: // Thread accessors static LLTextureCache* getTextureCache() { return sTextureCache; } - static LLWorkerThread* getImageDecodeThread() { return sImageDecodeThread; } + static LLImageDecodeThread* getImageDecodeThread() { return sImageDecodeThread; } static LLTextureFetch* getTextureFetch() { return sTextureFetch; } const std::string& getSerialNumber() { return mSerialNumber; } @@ -213,7 +213,7 @@ private: // Thread objects. static LLTextureCache* sTextureCache; - static LLWorkerThread* sImageDecodeThread; + static LLImageDecodeThread* sImageDecodeThread; static LLTextureFetch* sTextureFetch; S32 mNumSessions; diff --git a/linden/indra/newview/llassetuploadresponders.cpp b/linden/indra/newview/llassetuploadresponders.cpp index dfd7403..700edb5 100644 --- a/linden/indra/newview/llassetuploadresponders.cpp +++ b/linden/indra/newview/llassetuploadresponders.cpp @@ -378,6 +378,14 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content) } } +void LLSendTexLayerResponder::error(U32 statusNum, const std::string& reason) +{ + llinfos << "status: " << statusNum << " reason: " << reason << llendl; + + // Invoke the original callback with an error result + LLTexLayerSetBuffer::onTextureUploadComplete(LLUUID(), (void*) mBakedUploadData, -1, LL_EXSTAT_NONE); + mBakedUploadData = NULL; // deleted in onTextureUploadComplete() +} LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(const LLSD& post_data, const LLUUID& vfile_id, diff --git a/linden/indra/newview/llassetuploadresponders.h b/linden/indra/newview/llassetuploadresponders.h index 9ab571a..c08f299 100644 --- a/linden/indra/newview/llassetuploadresponders.h +++ b/linden/indra/newview/llassetuploadresponders.h @@ -83,6 +83,7 @@ public: ~LLSendTexLayerResponder(); virtual void uploadComplete(const LLSD& content); + virtual void error(U32 statusNum, const std::string& reason); LLBakedUploadData * mBakedUploadData; }; diff --git a/linden/indra/newview/lldrawable.cpp b/linden/indra/newview/lldrawable.cpp index 44a5de9..14aa38a 100644 --- a/linden/indra/newview/lldrawable.cpp +++ b/linden/indra/newview/lldrawable.cpp @@ -102,7 +102,7 @@ void LLDrawable::init() mVObjp = NULL; // mFaces mSpatialGroupp = NULL; - mVisible = 0; + mVisible = sCurVisible - 2;//invisible for the current frame and the last frame. mRadius = 0.f; mGeneration = -1; @@ -949,7 +949,11 @@ LLSpatialPartition* LLDrawable::getSpatialPartition() return retval; } - +BOOL LLDrawable::isRecentlyVisible() const +{ + //currently visible or visible in the previous frame. + return isVisible() || (mVisible == sCurVisible - 1) ; +} BOOL LLDrawable::isVisible() const { if (mVisible == sCurVisible) diff --git a/linden/indra/newview/lldrawable.h b/linden/indra/newview/lldrawable.h index 21cc0c5..71b75dc 100644 --- a/linden/indra/newview/lldrawable.h +++ b/linden/indra/newview/lldrawable.h @@ -50,7 +50,6 @@ #include "llviewerobject.h" #include "llrect.h" #include "llappviewer.h" // for gFrameTimeSeconds -#include "llimagej2c.h" class LLCamera; class LLDrawPool; @@ -81,7 +80,8 @@ public: BOOL isLight() const; - BOOL isVisible() const; + BOOL isVisible() const; + BOOL isRecentlyVisible() const; virtual void setVisible(LLCamera& camera_in, std::vector* results = NULL, BOOL for_select = FALSE); diff --git a/linden/indra/newview/lldrawpool.cpp b/linden/indra/newview/lldrawpool.cpp index 9f05ce3..e1bf1ed 100644 --- a/linden/indra/newview/lldrawpool.cpp +++ b/linden/indra/newview/lldrawpool.cpp @@ -279,7 +279,7 @@ S32 LLFacePool::drawLoopSetTex(face_array_t& face_list, S32 stage) iter != face_list.end(); iter++) { LLFace *facep = *iter; - gGL.getTexUnit(stage)->bind(facep->getTexture()); + gGL.getTexUnit(stage)->bind(facep->getTexture(), TRUE); gGL.getTexUnit(0)->activate(); res += facep->renderIndexed(); } @@ -481,14 +481,13 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture) { if (params.mTexture.notNull()) { - gGL.getTexUnit(0)->bind(params.mTexture.get()); + gGL.getTexUnit(0)->bind(params.mTexture.get(), TRUE); if (params.mTextureMatrix) { glMatrixMode(GL_TEXTURE); glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix); gPipeline.mTextureMatrixOps++; } - params.mTexture->addTextureStats(params.mVSize); } else { diff --git a/linden/indra/newview/lldrawpoolalpha.cpp b/linden/indra/newview/lldrawpoolalpha.cpp index 7e470bd..4b552ac 100644 --- a/linden/indra/newview/lldrawpoolalpha.cpp +++ b/linden/indra/newview/lldrawpoolalpha.cpp @@ -219,7 +219,7 @@ void LLDrawPoolAlpha::render(S32 pass) gPipeline.enableLightsFullbright(LLColor4(1,1,1,1)); glColor4f(1,0,0,1); LLViewerImage::sSmokeImagep->addTextureStats(1024.f*1024.f); - gGL.getTexUnit(0)->bind(LLViewerImage::sSmokeImagep.get()); + gGL.getTexUnit(0)->bind(LLViewerImage::sSmokeImagep.get(), TRUE); renderAlphaHighlight(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0); } @@ -295,7 +295,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask) { gGL.getTexUnit(0)->activate(); gGL.getTexUnit(0)->bind(params.mTexture.get()); - params.mTexture->addTextureStats(params.mVSize); + if (params.mTextureMatrix) { glMatrixMode(GL_TEXTURE); diff --git a/linden/indra/newview/lldrawpoolbump.cpp b/linden/indra/newview/lldrawpoolbump.cpp index 8f5858f..ff464f7 100644 --- a/linden/indra/newview/lldrawpoolbump.cpp +++ b/linden/indra/newview/lldrawpoolbump.cpp @@ -309,8 +309,8 @@ void LLDrawPoolBump::endRenderPass(S32 pass) void LLDrawPoolBump::beginShiny(bool invisible) { LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); - if (!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY)|| - invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)) + if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| + (invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY))) { return; } @@ -384,8 +384,8 @@ void LLDrawPoolBump::beginShiny(bool invisible) void LLDrawPoolBump::renderShiny(bool invisible) { LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); - if (!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY)|| - invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)) + if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| + (invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY))) { return; } @@ -411,8 +411,8 @@ void LLDrawPoolBump::renderShiny(bool invisible) void LLDrawPoolBump::endShiny(bool invisible) { LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); - if (!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY)|| - invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)) + if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| + (invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY))) { return; } @@ -1221,7 +1221,7 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture) if (params.mTexture.notNull()) { gGL.getTexUnit(diffuse_channel)->bind(params.mTexture.get()); - params.mTexture->addTextureStats(params.mVSize); + //params.mTexture->addTextureStats(params.mVSize); } else { diff --git a/linden/indra/newview/lldrawpoolterrain.cpp b/linden/indra/newview/lldrawpoolterrain.cpp index 2c644b0..cac5162 100644 --- a/linden/indra/newview/lldrawpoolterrain.cpp +++ b/linden/indra/newview/lldrawpoolterrain.cpp @@ -83,7 +83,7 @@ LLDrawPoolTerrain::LLDrawPoolTerrain(LLViewerImage *texturep) : gGL.getTexUnit(0)->bind(m2DAlphaRampImagep.get()); m2DAlphaRampImagep->setAddressMode(LLTexUnit::TAM_CLAMP); - mTexturep->setBoostLevel(LLViewerImage::BOOST_TERRAIN); + mTexturep->setBoostLevel(LLViewerImageBoostLevel::BOOST_TERRAIN); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } @@ -170,7 +170,7 @@ void LLDrawPoolTerrain::render(S32 pass) LLVLComposition *compp = regionp->getComposition(); for (S32 i = 0; i < 4; i++) { - compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); + compp->mDetailTextures[i]->setBoostLevel(LLViewerImageBoostLevel::BOOST_TERRAIN); compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area } diff --git a/linden/indra/newview/lldrawpooltree.cpp b/linden/indra/newview/lldrawpooltree.cpp index 2f2b072..46cd2d5 100644 --- a/linden/indra/newview/lldrawpooltree.cpp +++ b/linden/indra/newview/lldrawpooltree.cpp @@ -247,7 +247,7 @@ void LLDrawPoolTree::renderTree(BOOL selecting) LLGLState normalize(GL_NORMALIZE, TRUE); // Bind the texture for this tree. - gGL.getTexUnit(sDiffTex)->bind(mTexturep.get()); + gGL.getTexUnit(sDiffTex)->bind(mTexturep.get(), TRUE); U32 indices_drawn = 0; diff --git a/linden/indra/newview/lldynamictexture.cpp b/linden/indra/newview/lldynamictexture.cpp index 62fcf60..61f5a89 100644 --- a/linden/indra/newview/lldynamictexture.cpp +++ b/linden/indra/newview/lldynamictexture.cpp @@ -111,7 +111,7 @@ void LLDynamicTexture::generateGLTexture(LLGLint internal_format, LLGLenum prima mTexture->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes); } // llinfos << "ALLOCATING " << (mWidth*mHeight*mComponents)/1024 << "K" << llendl; - mTexture->createGLTexture(0, raw_image); + mTexture->createGLTexture(0, raw_image, 0, TRUE, LLViewerImageBoostLevel::DYNAMIC_TEX); mTexture->setAddressMode((mClamp) ? LLTexUnit::TAM_CLAMP : LLTexUnit::TAM_WRAP); mTexture->setGLTextureCreated(false); } diff --git a/linden/indra/newview/llface.cpp b/linden/indra/newview/llface.cpp index 69edcca..dab1dac 100644 --- a/linden/indra/newview/llface.cpp +++ b/linden/indra/newview/llface.cpp @@ -52,6 +52,7 @@ #include "llvovolume.h" #include "pipeline.h" #include "llviewerregion.h" +#include "llviewerwindow.h" #define LL_MAX_INDICES_COUNT 1000000 @@ -174,11 +175,19 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp) mLastGeomIndex = mGeomIndex; mLastIndicesCount = mIndicesCount; mLastIndicesIndex = mIndicesIndex; + + mImportanceToCamera = 0.f ; + mBoundingSphereRadius = 0.0f ; } void LLFace::destroy() { + if(mTexture.notNull()) + { + mTexture->removeFace(this) ; + } + if (mDrawPoolp) { mDrawPoolp->removeFace(this); @@ -199,7 +208,7 @@ void LLFace::destroy() } } } - + setDrawInfo(NULL); mDrawablep = NULL; @@ -247,9 +256,29 @@ void LLFace::setPool(LLFacePool* new_pool, LLViewerImage *texturep) } mDrawPoolp = new_pool; } - mTexture = texturep; + + setTexture(texturep) ; } +void LLFace::setTexture(LLViewerImage* tex) +{ + if(mTexture == tex) + { + return ; + } + + if(mTexture.notNull()) + { + mTexture->removeFace(this) ; + } + + mTexture = tex ; + + if(mTexture.notNull()) + { + mTexture->addFace(this) ; + } +} void LLFace::setTEOffset(const S32 te_offset) { @@ -698,7 +727,9 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f, } mCenterLocal = (newMin+newMax)*0.5f; - + LLVector3 tmp = (newMin - newMax) ; + mBoundingSphereRadius = tmp.length() * 0.5f ; + updateCenterAgent(); } @@ -865,6 +896,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, { mVertexBuffer->getBinormalStrider(binormals, mGeomIndex); } + if (rebuild_tcoord) { mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex); @@ -959,7 +991,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, 0.75f }; - if (getPoolType() != LLDrawPool::POOL_ALPHA && (LLPipeline::sRenderDeferred || LLPipeline::sRenderBump && tep->getShiny())) + if (getPoolType() != LLDrawPool::POOL_ALPHA && (LLPipeline::sRenderDeferred || (LLPipeline::sRenderBump && tep->getShiny()))) { color.mV[3] = U8 (alpha[tep->getShiny()] * 255); } @@ -1147,6 +1179,159 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, return TRUE; } +const F32 LEAST_IMPORTANCE = 0.05f ; +const F32 LEAST_IMPORTANCE_FOR_LARGE_IMAGE = 0.3f ; + +F32 LLFace::getTextureVirtualSize() +{ + F32 radius; + F32 cos_angle_to_view_dir; + mPixelArea = calcPixelArea(cos_angle_to_view_dir, radius); + + if (mPixelArea <= 0) + { + return 0.f; + } + + //get area of circle in texture space + LLVector2 tdim = mTexExtents[1] - mTexExtents[0]; + F32 texel_area = (tdim * 0.5f).lengthSquared()*3.14159f; + if (texel_area <= 0) + { + // Probably animated, use default + texel_area = 1.f; + } + + F32 face_area; + if (mVObjp->isSculpted() && texel_area > 1.f) + { + //sculpts can break assumptions about texel area + face_area = mPixelArea; + } + else + { + //apply texel area to face area to get accurate ratio + //face_area /= llclamp(texel_area, 1.f/64.f, 16.f); + face_area = mPixelArea / llclamp(texel_area, 0.015625f, 1024.f); + } + + if(face_area > LLViewerImage::sMaxSmallImageSize) + { + if(mImportanceToCamera < LEAST_IMPORTANCE) //if the face is not important, do not load hi-res. + { + face_area = LLViewerImage::sMaxSmallImageSize ; + } + else if(face_area > LLViewerImage::sMinLargeImageSize) //if is large image, shrink face_area by considering the partial overlapping. + { + if(mImportanceToCamera < LEAST_IMPORTANCE_FOR_LARGE_IMAGE)//if the face is not important, do not load hi-res. + { + face_area = LLViewerImage::sMinLargeImageSize ; + } + else if(mTexture.notNull() && mTexture->isLargeImage()) + { + face_area *= adjustPartialOverlapPixelArea(cos_angle_to_view_dir, radius ); + } + } + } + + return face_area; +} + +F32 LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) +{ + //get area of circle around face + LLVector3 center = getPositionAgent(); + LLVector3 size = (mExtents[1] - mExtents[0]) * 0.5f; + + LLVector3 lookAt = center - LLViewerCamera::getInstance()->getOrigin(); + F32 dist = lookAt.normVec() ; + + //get area of circle around node + F32 app_angle = atanf(size.length()/dist); + radius = app_angle*LLDrawable::sCurPixelAngle; + F32 face_area = radius*radius * 3.14159f; + + if(dist < mBoundingSphereRadius) //camera is very close + { + cos_angle_to_view_dir = 1.0f ; + mImportanceToCamera = 1.0f ; + } + else + { + cos_angle_to_view_dir = lookAt * LLViewerCamera::getInstance()->getXAxis() ; + mImportanceToCamera = LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist) ; + } + + return face_area ; +} + +//the projection of the face partially overlaps with the screen +F32 LLFace::adjustPartialOverlapPixelArea(F32 cos_angle_to_view_dir, F32 radius ) +{ + F32 screen_radius = (F32)llmax(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight()) ; + F32 center_angle = acosf(cos_angle_to_view_dir) ; + F32 d = center_angle * LLDrawable::sCurPixelAngle ; + + if(d + radius > screen_radius + 5.f) + { + //---------------------------------------------- + //calculate the intersection area of two circles + //F32 radius_square = radius * radius ; + //F32 d_square = d * d ; + //F32 screen_radius_square = screen_radius * screen_radius ; + //face_area = + // radius_square * acosf((d_square + radius_square - screen_radius_square)/(2 * d * radius)) + + // screen_radius_square * acosf((d_square + screen_radius_square - radius_square)/(2 * d * screen_radius)) - + // 0.5f * sqrtf((-d + radius + screen_radius) * (d + radius - screen_radius) * (d - radius + screen_radius) * (d + radius + screen_radius)) ; + //---------------------------------------------- + + //the above calculation is too expensive + //the below is a good estimation: bounding box of the bounding sphere: + F32 alpha = 0.5f * (radius + screen_radius - d) / radius ; + alpha = llclamp(alpha, 0.f, 1.f) ; + return alpha * alpha ; + } + return 1.0f ; +} + +const S8 FACE_IMPORTANCE_LEVEL = 4 ; +const F32 FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[FACE_IMPORTANCE_LEVEL][2] = //{distance, importance_weight} + {{16.1f, 1.0f}, {32.1f, 0.5f}, {48.1f, 0.2f}, {96.1f, 0.05f} } ; +const F32 FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[FACE_IMPORTANCE_LEVEL][2] = //{cos(angle), importance_weight} + {{0.985f /*cos(10 degrees)*/, 1.0f}, {0.94f /*cos(20 degrees)*/, 0.8f}, {0.866f /*cos(30 degrees)*/, 0.64f}, {0.0f, 0.36f}} ; + +//static +F32 LLFace::calcImportanceToCamera(F32 cos_angle_to_view_dir, F32 dist) +{ + F32 importance = 0.f ; + + if(cos_angle_to_view_dir > LLViewerCamera::getInstance()->getCosHalfFov() && + dist < FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[FACE_IMPORTANCE_LEVEL - 1][0]) + { + F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ; + F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed(); + + if(camera_moving_speed > 10.0f || camera_angular_speed > 1.0f) + { + //if camera moves or rotates too fast, ignore the importance factor + return 0.f ; + } + + //F32 camera_relative_speed = camera_moving_speed * (lookAt * LLViewerCamera::getInstance()->getVelocityDir()) ; + + S32 i = 0 ; + for(i = 0; i < FACE_IMPORTANCE_LEVEL && dist > FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[i][0]; ++i); + i = llmin(i, FACE_IMPORTANCE_LEVEL - 1) ; + F32 dist_factor = FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[i][1] ; + + for(i = 0; i < FACE_IMPORTANCE_LEVEL && cos_angle_to_view_dir < FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[i][0] ; ++i) ; + i = llmin(i, FACE_IMPORTANCE_LEVEL - 1) ; + importance = dist_factor * FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[i][1] ; + } + + return importance ; +} + BOOL LLFace::verify(const U32* indices_array) const { BOOL ok = TRUE; diff --git a/linden/indra/newview/llface.h b/linden/indra/newview/llface.h index 4a551ff..5e422b4 100644 --- a/linden/indra/newview/llface.h +++ b/linden/indra/newview/llface.h @@ -88,7 +88,7 @@ public: U16 getGeomIndex() const { return mGeomIndex; } // index into draw pool U16 getGeomStart() const { return mGeomIndex; } // index into draw pool LLViewerImage* getTexture() const { return mTexture; } - void setTexture(LLViewerImage* tex) { mTexture = tex; } + void setTexture(LLViewerImage* tex) ; LLXformMatrix* getXform() const { return mXform; } BOOL hasGeometry() const { return mGeomCount > 0; } LLVector3 getPositionAgent() const; @@ -186,7 +186,14 @@ public: void setIndicesIndex(S32 idx) { mIndicesIndex = idx; } void setDrawInfo(LLDrawInfo* draw_info); -protected: + F32 getTextureVirtualSize() ; + F32 getImportanceToCamera()const {return mImportanceToCamera ;} + +private: + F32 adjustPartialOverlapPixelArea(F32 cos_angle_to_view_dir, F32 radius ); + F32 calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) ; +public: + static F32 calcImportanceToCamera(F32 to_view_dir, F32 dist); public: @@ -202,7 +209,7 @@ public: LLMatrix4* mTextureMatrix; LLDrawInfo* mDrawInfo; -protected: +private: friend class LLGeometryManager; friend class LLVolumeGeometryManager; @@ -231,7 +238,13 @@ protected: S32 mReferenceIndex; F32 mVSize; F32 mPixelArea; - + + //importance factor, in the range [0, 1.0]. + //1.0: the most important. + //based on the distance from the face to the view point and the angle from the face center to the view direction. + F32 mImportanceToCamera ; + F32 mBoundingSphereRadius ; + protected: static BOOL sSafeRenderSelect; diff --git a/linden/indra/newview/llfloaterassetbrowser.cpp b/linden/indra/newview/llfloaterassetbrowser.cpp index cb2412d..af81c4a 100644 --- a/linden/indra/newview/llfloaterassetbrowser.cpp +++ b/linden/indra/newview/llfloaterassetbrowser.cpp @@ -130,7 +130,7 @@ void LLFloaterAssetBrowser::createThumbnails() for(S32 i = 0; i < items.count(); i++) { mTextureAssets[i].mTexturep = gImageList.getImage(mTextureAssets[i].mAssetUUID, MIPMAP_YES, IMMEDIATE_NO); - mTextureAssets[i].mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW); + mTextureAssets[i].mTexturep->setBoostLevel(LLViewerImageBoostLevel::BOOST_PREVIEW); //mTextureAssets[i].mTexturep->processTextureStats(); } diff --git a/linden/indra/newview/llfloaterreporter.cpp b/linden/indra/newview/llfloaterreporter.cpp index 31d658b..7a5f2ba 100644 --- a/linden/indra/newview/llfloaterreporter.cpp +++ b/linden/indra/newview/llfloaterreporter.cpp @@ -952,8 +952,8 @@ void LLFloaterReporter::takeScreenshot() mResourceDatap->mAssetInfo.mType); // store in the image list so it doesn't try to fetch from the server - LLPointer image_in_list = new LLViewerImage(mResourceDatap->mAssetInfo.mUuid, TRUE); - image_in_list->createGLTexture(0, raw); + LLPointer image_in_list = new LLViewerImage(mResourceDatap->mAssetInfo.mUuid); + image_in_list->createGLTexture(0, raw, 0, TRUE, LLViewerImageBoostLevel::OTHER); gImageList.addImage(image_in_list); // the texture picker then uses that texture diff --git a/linden/indra/newview/llpreviewtexture.cpp b/linden/indra/newview/llpreviewtexture.cpp index 13e7cca..dcbb669 100644 --- a/linden/indra/newview/llpreviewtexture.cpp +++ b/linden/indra/newview/llpreviewtexture.cpp @@ -224,6 +224,11 @@ void LLPreviewTexture::draw() // Pump the texture priority F32 pixel_area = mLoadingFullImage ? (F32)MAX_IMAGE_AREA : (F32)(interior.getWidth() * interior.getHeight() ); mImage->addTextureStats( pixel_area ); + if(pixel_area > 0.f) + { + //boost the previewed image priority to the highest to make it to get loaded first. + mImage->setAdditionalDecodePriority(1.0f) ; + } // Don't bother decoding more than we can display, unless // we're loading the full image. @@ -482,7 +487,8 @@ void LLPreviewTexture::updateDimensions() void LLPreviewTexture::loadAsset() { mImage = gImageList.getImage(mImageID, MIPMAP_TRUE, FALSE); - mImage->setBoostLevel(LLViewerImage::BOOST_PREVIEW); + mImage->setBoostLevel(LLViewerImageBoostLevel::BOOST_PREVIEW); + mImage->forceToSaveRawImage(0) ; mAssetStatus = PREVIEW_ASSET_LOADING; } diff --git a/linden/indra/newview/llstartup.cpp b/linden/indra/newview/llstartup.cpp index 143aa34..7a7e023 100644 --- a/linden/indra/newview/llstartup.cpp +++ b/linden/indra/newview/llstartup.cpp @@ -59,6 +59,7 @@ #include "llfocusmgr.h" #include "llhttpsender.h" #include "imageids.h" +#include "llimageworker.h" #include "lllandmark.h" #include "llloginflags.h" #include "llmd5.h" @@ -3468,7 +3469,7 @@ void init_start_screen(S32 location_id) } raw->expandToPowerOfTwo(); - gStartImageGL->createGLTexture(0, raw); + gStartImageGL->createGLTexture(0, raw, 0, TRUE, LLViewerImageBoostLevel::OTHER); } diff --git a/linden/indra/newview/lltexlayer.cpp b/linden/indra/newview/lltexlayer.cpp index 424c525..968c496 100644 --- a/linden/indra/newview/lltexlayer.cpp +++ b/linden/indra/newview/lltexlayer.cpp @@ -115,7 +115,6 @@ LLTexLayerSetBuffer::~LLTexLayerSetBuffer() if( mBumpTex.notNull()) { mBumpTex = NULL ; - LLImageGL::sGlobalTextureMemoryInBytes -= mWidth * mHeight * 4; LLTexLayerSetBuffer::sGLBumpByteCount -= mWidth * mHeight * 4; } } @@ -132,7 +131,7 @@ void LLTexLayerSetBuffer::destroyGLTexture() if( mBumpTex.notNull() ) { mBumpTex = NULL ; - LLImageGL::sGlobalTextureMemoryInBytes -= mWidth * mHeight * 4; + //LLImageGL::sGlobalTextureMemoryInBytes -= mWidth * mHeight * 4; LLTexLayerSetBuffer::sGLBumpByteCount -= mWidth * mHeight * 4; } @@ -165,6 +164,14 @@ void LLTexLayerSetBuffer::createBumpTexture() LLImageGL::sGlobalTextureMemoryInBytes += mWidth * mHeight * 4; LLTexLayerSetBuffer::sGLBumpByteCount += mWidth * mHeight * 4; + + if(gAuditTexture) + { + mBumpTex->setCategory(LLViewerImageBoostLevel::TEXLAYER_BUMP) ; + mBumpTex->setTextureSize(mWidth * mHeight * 4) ; + mBumpTex->setComponents(4) ; + mBumpTex->incTextureCounter() ; + } } } @@ -619,7 +626,7 @@ void LLTexLayerSetBuffer::bindBumpTexture( U32 stage ) if( mLastBindTime != LLImageGL::sLastFrameTime ) { mLastBindTime = LLImageGL::sLastFrameTime; - LLImageGL::updateBoundTexMem(mWidth * mHeight * 4); + mBumpTex->updateBoundTexMem(); } } else @@ -833,7 +840,7 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height ) if( image_gl ) { LLGLSUIDefault gls_ui; - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(image_gl, TRUE); gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE ); gl_rect_2d_simple_tex( width, height ); } @@ -1418,7 +1425,7 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) LLTexUnit::eTextureAddressMode old_mode = image_gl->getAddressMode(); - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(image_gl, TRUE); gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); gl_rect_2d_simple_tex( width, height ); @@ -1440,7 +1447,7 @@ BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height ) LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask ); if( image_gl ) { - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(image_gl, TRUE); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } @@ -1617,7 +1624,7 @@ BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4 LLTexUnit::eTextureAddressMode old_mode = image_gl->getAddressMode(); - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(image_gl, TRUE); gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); gl_rect_2d_simple_tex( width, height ); @@ -1643,7 +1650,7 @@ BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4 ( (image_gl->getComponents() == 1) && getInfo()->mStaticImageIsMask ) ) { LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(image_gl); + gGL.getTexUnit(0)->bind(image_gl, TRUE); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } @@ -2088,14 +2095,14 @@ BOOL LLTexLayerParamAlpha::render( S32 x, S32 y, S32 width, S32 height ) // Create the GL texture, and then hang onto it for future use. if( mNeedsCreateTexture ) { - mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw); + mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw, 0, TRUE, LLViewerImageBoostLevel::TEXLAYER_CACHE); mNeedsCreateTexture = FALSE; gGL.getTexUnit(0)->bind(mCachedProcessedImageGL); mCachedProcessedImageGL->setAddressMode(LLTexUnit::TAM_CLAMP); } LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(mCachedProcessedImageGL); + gGL.getTexUnit(0)->bind(mCachedProcessedImageGL, TRUE); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); stop_glerror(); @@ -2543,7 +2550,7 @@ LLImageGL* LLTexStaticImageList::getImageGL(const std::string& file_name, BOOL i // that once an image is a mask it's always a mask. image_gl->setExplicitFormat( GL_ALPHA8, GL_ALPHA ); } - image_gl->createGLTexture(0, image_raw); + image_gl->createGLTexture(0, image_raw, 0, TRUE, LLViewerImageBoostLevel::OTHER); gGL.getTexUnit(0)->bind(image_gl); image_gl->setAddressMode(LLTexUnit::TAM_CLAMP); diff --git a/linden/indra/newview/lltexturecache.cpp b/linden/indra/newview/lltexturecache.cpp index 9c3bed2..e0bef61 100644 --- a/linden/indra/newview/lltexturecache.cpp +++ b/linden/indra/newview/lltexturecache.cpp @@ -43,11 +43,18 @@ // Included to allow LLTextureCache::purgeTextures() to pause watchdog timeout #include "llappviewer.h" -#define USE_LFS_READ 0 -#define USE_LFS_WRITE 0 - -// Note: first 4 bytes store file size, rest is j2c data -const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; //1024; +// Cache organization: +// cache/texture.entries +// Unordered array of Entry structs +// cache/texture.cache +// First TEXTURE_CACHE_ENTRY_SIZE bytes of each texture in texture.entries in same order +// Entry size same as header packet, so we're not 0-padding unless whole image is contained in header. +// cache/textures/[0-F]/UUID.texture +// Actual texture body files + +const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; +const F32 TEXTURE_CACHE_PURGE_AMOUNT = .20f; // % amount to reduce the cache by when it exceeds its limit +const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead to regenerate) class LLTextureCacheWorker : public LLWorkerClass { @@ -309,93 +316,74 @@ void LLTextureCacheWorker::startWork(S32 param) { } +// This is where a texture is read from the cache system (header and body) +// Current assumption are: +// - the whole data are in a raw form, will be stored at mReadData +// - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache) +// - the code supports offset reading but this is actually never exercised in the viewer bool LLTextureCacheRemoteWorker::doRead() { + bool done = false; + S32 idx = -1; + S32 local_size = 0; std::string local_filename; + // First state / stage : find out if the file is local if (mState == INIT) { std::string filename = mCache->getLocalFileName(mID); - local_filename = filename + ".j2c"; - local_size = LLAPRFile::size(local_filename); - if (local_size == 0) + // Is it a JPEG2000 file? { - local_filename = filename + ".tga"; + local_filename = filename + ".j2c"; local_size = LLAPRFile::size(local_filename); if (local_size > 0) { - mImageFormat = IMG_CODEC_TGA; - mDataSize = local_size; // Only a complete .tga file is valid + mImageFormat = IMG_CODEC_J2C; } } - if (local_size > 0) - { - mState = LOCAL; - } - else - { - mState = CACHE; - } - } - - if (mState == LOCAL) - { -#if USE_LFS_READ - if (mFileHandle == LLLFSThread::nullHandle()) + // If not, is it a jpeg file? + if (local_size == 0) { - mImageLocal = TRUE; - mImageSize = local_size; - if (!mDataSize || mDataSize + mOffset > local_size) - { - mDataSize = local_size - mOffset; - } - if (mDataSize <= 0) + local_filename = filename + ".jpg"; + local_size = LLAPRFile::size(local_filename); + if (local_size > 0) { - // no more data to read - mDataSize = 0; - return true; + mImageFormat = IMG_CODEC_JPEG; + mDataSize = local_size; // Only a complete .jpg file is valid } - mReadData = new U8[mDataSize]; - mBytesRead = -1; - mBytesToRead = mDataSize; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize, - new ReadResponder(mCache, mRequestHandle)); - return false; } - else + // Hmm... What about a targa file? (used for UI texture mostly) + if (local_size == 0) { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "Error reading file from local cache: " << local_filename -// << " Bytes: " << mDataSize << " Offset: " << mOffset -// << " / " << mDataSize << llendl; - mDataSize = 0; // failed - delete[] mReadData; - mReadData = NULL; - } - return true; - } - else + local_filename = filename + ".tga"; + local_size = LLAPRFile::size(local_filename); + if (local_size > 0) { - return false; + mImageFormat = IMG_CODEC_TGA; + mDataSize = local_size; // Only a complete .tga file is valid } } -#else + // Determine the next stage: if we found a file, then LOCAL else CACHE + mState = (local_size > 0 ? LOCAL : CACHE); + } + + // Second state / stage : if the file is local, load it and leave + if (!done && (mState == LOCAL)) + { + llassert(local_size != 0); // we're assuming there is a non empty local file here... if (!mDataSize || mDataSize > local_size) { mDataSize = local_size; } + // Allocate read buffer mReadData = new U8[mDataSize]; S32 bytes_read = LLAPRFile::readEx(local_filename, mReadData, mOffset, mDataSize); if (bytes_read != mDataSize) { -// llwarns << "Error reading file from local cache: " << local_filename -// << " Bytes: " << mDataSize << " Offset: " << mOffset -// << " / " << mDataSize << llendl; + llwarns << "Error reading file from local cache: " << local_filename + << " Bytes: " << mDataSize << " Offset: " << mOffset + << " / " << mDataSize << llendl; mDataSize = 0; delete[] mReadData; mReadData = NULL; @@ -405,403 +393,273 @@ bool LLTextureCacheRemoteWorker::doRead() mImageSize = local_size; mImageLocal = TRUE; } - return true; -#endif + // We're done... + done = true; } - S32 idx = -1; - - if (mState == CACHE) + // Second state / stage : identify the cache or not... + if (!done && (mState == CACHE)) { - llassert_always(mImageSize == 0); - idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize); - if (idx >= 0 && mImageSize > mOffset) + idx = mCache->getHeaderCacheEntry(mID, mImageSize); + if (idx < 0) { - llassert_always(mImageSize > 0); - if (!mDataSize || mDataSize > mImageSize) - { - mDataSize = mImageSize; - } - mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; + // The texture is *not* cached. We're done here... + mDataSize = 0; // no data + done = true; } else { - mDataSize = 0; // no data - return true; + // If the read offset is bigger than the header cache, we read directly from the body + // Note that currently, we *never* read with offset from the cache, so the result is *always* HEADER + mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; } } - if (mState == HEADER) + // Third state / stage : read data from the header cache (texture.entries) file + if (!done && (mState == HEADER)) { -#if USE_LFS_READ - if (mFileHandle == LLLFSThread::nullHandle()) - { - llassert_always(idx >= 0); - llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); - S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; - S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - llassert_always(mReadData == NULL); - mReadData = new U8[size]; - mBytesRead = -1; - mBytesToRead = size; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName, - mReadData, offset, mBytesToRead, - new ReadResponder(mCache, mRequestHandle)); - return false; - } - else - { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes read from header: " << mBytesRead -// << " != " << mBytesToRead << llendl; - mDataSize = -1; // failed - return true; - } - if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE) - { - return true; // done - } - else - { - mFileHandle = LLLFSThread::nullHandle(); - mState = BODY; - } - } - else - { - return false; - } - } -#else - llassert_always(idx >= 0); + llassert_always(idx >= 0); // we need an entry here or reading the header makes no sense llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; + // Compute the size we need to read (in bytes) S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; + size = llmin(size, mDataSize); + // Allocate the read buffer mReadData = new U8[size]; S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName, mReadData, offset, size); if (bytes_read != size) { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes read from header: " << bytes_read -// << " / " << size << llendl; + llwarns << "LLTextureCacheWorker: " << mID + << " incorrect number of bytes read from header: " << bytes_read + << " / " << size << llendl; + delete[] mReadData; + mReadData = NULL; mDataSize = -1; // failed - return true; + done = true; } - if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE) + // If we already read all we expected, we're actually done + if (mDataSize <= bytes_read) { - return true; // done + done = true; } else { mState = BODY; } -#endif } - if (mState == BODY) + // Fourth state / stage : read the rest of the data from the UUID based cached file + if (!done && (mState == BODY)) { -#if USE_LFS_READ - if (mFileHandle == LLLFSThread::nullHandle()) - { - std::string filename = mCache->getTextureFileName(mID); - S32 filesize = LLAPRFile::size(filename); - if (filesize > mOffset) - { - S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize; - mDataSize = llmin(datasize, mDataSize); - S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - data_offset = llmax(data_offset, 0); - S32 file_size = mDataSize - data_offset; - S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; - file_offset = llmax(file_offset, 0); - - llassert_always(mDataSize > 0); - U8* data = new U8[mDataSize]; - if (data_offset > 0) - { - llassert_always(mReadData); - llassert_always(data_offset <= mDataSize); - memcpy(data, mReadData, data_offset); - delete[] mReadData; - mReadData = NULL; - } - llassert_always(mReadData == NULL); - mReadData = data; - - mBytesRead = -1; - mBytesToRead = file_size; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - llassert_always(data_offset + mBytesToRead <= mDataSize); - mFileHandle = LLLFSThread::sLocal->read(filename, - mReadData + data_offset, file_offset, mBytesToRead, - new ReadResponder(mCache, mRequestHandle)); - return false; - } - else - { - mDataSize = TEXTURE_CACHE_ENTRY_SIZE; - return true; // done - } - } - else - { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes read from body: " << mBytesRead -// << " != " << mBytesToRead << llendl; - mDataSize = -1; // failed - } - return true; - } - else - { - return false; - } - } -#else std::string filename = mCache->getTextureFileName(mID); S32 filesize = LLAPRFile::size(filename); - S32 bytes_read = 0; - if (filesize > mOffset) + + if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset) { - S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize; - mDataSize = llmin(datasize, mDataSize); - S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - data_offset = llmax(data_offset, 0); - S32 file_size = mDataSize - data_offset; - S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; - file_offset = llmax(file_offset, 0); + S32 max_datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize - mOffset; + mDataSize = llmin(max_datasize, mDataSize); + + S32 data_offset, file_size, file_offset; + // Reserve the whole data buffer first U8* data = new U8[mDataSize]; - if (data_offset > 0) + + // Set the data file pointers taking the read offset into account. 2 cases: + if (mOffset < TEXTURE_CACHE_ENTRY_SIZE) { + // Offset within the header record. That means we read something from the header cache. + // Note: most common case is (mOffset = 0), so this is the "normal" code path. + data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; // i.e. TEXTURE_CACHE_ENTRY_SIZE if mOffset nul (common case) + file_offset = 0; + file_size = mDataSize - data_offset; + // Copy the raw data we've been holding from the header cache into the new sized buffer llassert_always(mReadData); memcpy(data, mReadData, data_offset); delete[] mReadData; + mReadData = NULL; + } + else + { + // Offset bigger than the header record. That means we haven't read anything yet. + data_offset = 0; + file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; + file_size = mDataSize; + // No data from header cache to copy in that case, we skipped it all } + + // Now use that buffer as the object read buffer + llassert_always(mReadData == NULL); mReadData = data; - bytes_read = LLAPRFile::readEx(filename, + + // Read the data at last + S32 bytes_read = LLAPRFile::readEx(filename, mReadData + data_offset, file_offset, file_size); if (bytes_read != file_size) { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes read from body: " << bytes_read -// << " / " << file_size << llendl; + llwarns << "LLTextureCacheWorker: " << mID + << " incorrect number of bytes read from body: " << bytes_read + << " / " << file_size << llendl; + delete[] mReadData; + mReadData = NULL; mDataSize = -1; // failed - return true; + done = true; } } else { - mDataSize = TEXTURE_CACHE_ENTRY_SIZE; - } - - return true; -#endif + // No body, we're done. + mDataSize = llmax(TEXTURE_CACHE_ENTRY_SIZE - mOffset, 0); + lldebugs << "No body file for: " << filename << llendl; + } + // Nothing else to do at that point... + done = true; } - - return false; + + // Clean up and exit + return done; } +// This is where *everything* about a texture is written down in the cache system (entry map, header and body) +// Current assumption are: +// - the whole data are in a raw form, starting at mWriteData +// - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache) +// - the code *does not* support offset writing so there are no difference between buffer addresses and start of data bool LLTextureCacheRemoteWorker::doWrite() { + bool done = false; S32 idx = -1; - // No LOCAL state for write() - + // First state / stage : check that what we're trying to cache is in an OK shape if (mState == INIT) { + llassert_always(mOffset == 0); // We currently do not support write offsets + llassert_always(mDataSize > 0); // Things will go badly wrong if mDataSize is nul or negative... + mState = CACHE; + } + + // No LOCAL state for write(): because it doesn't make much sense to cache a local file... + + // Second state / stage : set an entry in the headers entry (texture.entries) file + if (!done && (mState == CACHE)) + { + bool alreadyCached = false; S32 cur_imagesize = 0; - S32 offset = mOffset; - idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize); - if (idx >= 0 && cur_imagesize > 0) + // Checks if this image is already in the entry list + idx = mCache->getHeaderCacheEntry(mID, cur_imagesize); + if (idx >= 0 && (cur_imagesize >= 0)) { - offset = TEXTURE_CACHE_ENTRY_SIZE; // don't re-write header + alreadyCached = true; // already there and non empty } - idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize); // touch entry - if (idx >= 0) + idx = mCache->setHeaderCacheEntry(mID, mImageSize); // create or touch the entry + if (idx < 0) { - if(cur_imagesize > 0 && mImageSize != cur_imagesize) - { -// llwarns << "Header cache entry size: " << cur_imagesize << " != mImageSize: " << mImageSize << llendl; - offset = 0; // re-write header - } - mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; + llwarns << "LLTextureCacheWorker: " << mID + << " Unable to create header entry for writing!" << llendl; + mDataSize = -1; // failed + done = true; } else { - mDataSize = -1; // failed - return true; + if (cur_imagesize > 0 && (mImageSize != cur_imagesize)) + { + alreadyCached = false; // re-write the header if the size changed in all cases + } + if (alreadyCached && (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)) + { + // Small texture already cached case: we're done with writing + done = true; + } + else + { + // If the texture has already been cached, we don't resave the header and go directly to the body part + mState = alreadyCached ? BODY : HEADER; + } } } - - if (mState == HEADER) + + // Third stage / state : write the header record in the header file (texture.cache) + if (!done && (mState == HEADER)) { -#if USE_LFS_WRITE - if (mFileHandle == LLLFSThread::nullHandle()) + llassert_always(idx >= 0); // we need an entry here or storing the header makes no sense + S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE; // skip to the correct spot in the header file + S32 size = TEXTURE_CACHE_ENTRY_SIZE; // record size is fixed for the header + S32 bytes_written; + + if (mDataSize < TEXTURE_CACHE_ENTRY_SIZE) { - llassert_always(idx >= 0); - llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); - S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; - S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - mBytesRead = -1; - mBytesToRead = size; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName, - mWriteData, offset, mBytesToRead, - new WriteResponder(mCache, mRequestHandle)); - return false; + // We need to write a full record in the header cache so, if the amount of data is smaller + // than a record, we need to transfer the data to a buffer padded with 0 and write that + U8* padBuffer = new U8[TEXTURE_CACHE_ENTRY_SIZE]; + memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros + memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer + bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size); + delete [] padBuffer; } else { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes written to header: " << mBytesRead -// << " != " << mBytesToRead << llendl; - mDataSize = -1; // failed - return true; - } - if (mDataSize <= mBytesToRead) - { - return true; // done - } - else - { - mFileHandle = LLLFSThread::nullHandle(); - mState = BODY; - } - } - else - { - return false; - } + // Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file + bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size); } -#else - llassert_always(idx >= 0); - llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); - S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; - S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - S32 bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size); if (bytes_written <= 0) { -// llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl; + llwarns << "LLTextureCacheWorker: " << mID + << " Unable to write header entry!" << llendl; mDataSize = -1; // failed - return true; + done = true; } - if (mDataSize <= size) + // If we wrote everything (may be more with padding) in the header cache, + // we're done so we don't have a body to store + if (mDataSize <= bytes_written) { - return true; // done + done = true; } else { mState = BODY; } -#endif } - if (mState == BODY) + // Fourth stage / state : write the body file, i.e. the rest of the texture in a "UUID" file name + if (!done && (mState == BODY)) { -#if USE_LFS_WRITE - if (mFileHandle == LLLFSThread::nullHandle()) + llassert(mDataSize > TEXTURE_CACHE_ENTRY_SIZE); // wouldn't make sense to be here otherwise... + S32 file_size = mDataSize - TEXTURE_CACHE_ENTRY_SIZE; + if ((file_size > 0) && mCache->updateTextureEntryList(mID, file_size)) { - S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - data_offset = llmax(data_offset, 0); - S32 file_size = mDataSize - data_offset; - S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; - file_offset = llmax(file_offset, 0); - if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size)) - { - std::string filename = mCache->getTextureFileName(mID); - mBytesRead = -1; - mBytesToRead = file_size; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - mFileHandle = LLLFSThread::sLocal->write(filename, - mWriteData + data_offset, file_offset, mBytesToRead, - new WriteResponder(mCache, mRequestHandle)); - return false; - } - else - { - mDataSize = 0; // no data written - return true; // done - } - } - else - { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes written to body: " << mBytesRead -// << " != " << mBytesToRead << llendl; - mDataSize = -1; // failed - } - return true; - } - else - { - return false; - } - } -#else - S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - data_offset = llmax(data_offset, 0); - S32 file_size = mDataSize - data_offset; - S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; - file_offset = llmax(file_offset, 0); - S32 bytes_written = 0; - if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size)) - { - std::string filename = mCache->getTextureFileName(mID); - - bytes_written = LLAPRFile::writeEx(filename, - mWriteData + data_offset, - file_offset, file_size); + // build the cache file name from the UUID + std::string filename = mCache->getTextureFileName(mID); +// llinfos << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << llendl; + S32 bytes_written = LLAPRFile::writeEx( filename, + mWriteData + TEXTURE_CACHE_ENTRY_SIZE, + 0, file_size); if (bytes_written <= 0) { + llwarns << "LLTextureCacheWorker: " << mID + << " incorrect number of bytes written to body: " << bytes_written + << " / " << file_size << llendl; mDataSize = -1; // failed + done = true; } } else { mDataSize = 0; // no data written } - - return true; -#endif + // Nothing else to do at that point... + done = true; } - - return false; + + // Clean up and exit + return done; } //virtual bool LLTextureCacheWorker::doWork(S32 param) { -// *TODO reenable disabled apr_pool usage disabled due to maint-render-9 merge breakage -brad - //allocate a new local apr_pool -// LLAPRPool pool ; - - //save the current mFileAPRPool to avoid breaking anything. -// apr_pool_t* old_pool = mCache->getFileAPRPool() ; - //make mFileAPRPool to point to the local one -// mCache->setFileAPRPool(pool.getAPRPool()) ; - bool res = false; if (param == 0) // read { @@ -815,10 +673,6 @@ bool LLTextureCacheWorker::doWork(S32 param) { llassert_always(0); } - - //set mFileAPRPool back, the local one will be released automatically. -// mCache->setFileAPRPool(old_pool) ; - return res; } @@ -884,6 +738,7 @@ LLTextureCache::LLTextureCache(bool threaded) mWorkersMutex(NULL), mHeaderMutex(NULL), mListMutex(NULL), + mHeaderAPRFile(NULL), mReadOnly(FALSE), mTexturesSizeTotal(0), mDoPurge(FALSE) @@ -892,9 +747,6 @@ LLTextureCache::LLTextureCache(bool threaded) LLTextureCache::~LLTextureCache() { - purgeTextureFilesTimeSliced(TRUE); // force-flush all pending file deletes - - // apr_pool_destroy(mFileAPRPool); } ////////////////////////////////////////////////////////////////////////////// @@ -926,6 +778,9 @@ S32 LLTextureCache::update(U32 max_time_ms) } } + unlockWorkers(); + + // call 'completed' with workers list unlocked (may call readComplete() or writeComplete() for (responder_list_t::iterator iter1 = completed_list.begin(); iter1 != completed_list.end(); ++iter1) { @@ -934,8 +789,6 @@ S32 LLTextureCache::update(U32 max_time_ms) responder->completed(success); } - unlockWorkers(); - return res; } @@ -954,32 +807,48 @@ std::string LLTextureCache::getTextureFileName(const LLUUID& id) { std::string idstr = id.asString(); std::string delem = gDirUtilp->getDirDelimiter(); - std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr; + std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr + ".texture"; return filename; } -bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize) +bool LLTextureCache::updateTextureEntryList(const LLUUID& id, S32 bodysize) { bool res = false; bool purge = false; - // Append UUID to end of texture entries { LLMutexLock lock(&mHeaderMutex); - size_map_t::iterator iter = mTexturesSizeMap.find(id); - if (iter == mTexturesSizeMap.end() || iter->second < bodysize) + size_map_t::iterator iter1 = mTexturesSizeMap.find(id); + if (iter1 == mTexturesSizeMap.end() || iter1->second < bodysize) { llassert_always(bodysize > 0); - Entry* entry = new Entry(id, bodysize, time(NULL)); - LLAPRFile::writeEx(mTexturesDirEntriesFileName, - (U8*)entry, -1, 1*sizeof(Entry)); - delete entry; - if (iter != mTexturesSizeMap.end()) + S32 oldbodysize = 0; + if (iter1 != mTexturesSizeMap.end()) { - mTexturesSizeTotal -= iter->second; + oldbodysize = iter1->second; } + + Entry entry; + S32 idx = openAndReadEntry(id, entry, false); + if (idx < 0) + { + // TODO: change to llwarns + llerrs << "Failed to open entry: " << id << llendl; + removeFromCache(id); + return false; + } + else if (oldbodysize != entry.mBodySize) + { + // TODO: change to llwarns + llerrs << "Entry mismatch in mTextureSizeMap / mHeaderIDMap" + << " idx=" << idx << " oldsize=" << oldbodysize << " entrysize=" << entry.mBodySize << llendl; + } + entry.mBodySize = bodysize; + writeEntryAndClose(idx, entry); + + mTexturesSizeTotal -= oldbodysize; mTexturesSizeTotal += bodysize; - mTexturesSizeMap[id] = bodysize; + if (mTexturesSizeTotal > sCacheMaxTexturesSize) { purge = true; @@ -998,7 +867,7 @@ bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize) //static const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB -F32 LLTextureCache::sHeaderCacheVersion = 1.0f; +F32 LLTextureCache::sHeaderCacheVersion = 1.2f; U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE; S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit const char* entries_filename = "texture.entries"; @@ -1011,7 +880,6 @@ void LLTextureCache::setDirNames(ELLPath location) mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename); mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename); mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname); - mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename; } void LLTextureCache::purgeCache(ELLPath location) @@ -1019,7 +887,7 @@ void LLTextureCache::purgeCache(ELLPath location) if (!mReadOnly) { setDirNames(location); - + llassert_always(mHeaderAPRFile == NULL); LLAPRFile::remove(mHeaderEntriesFileName); LLAPRFile::remove(mHeaderDataFileName); } @@ -1062,80 +930,315 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only) return max_size; // unused cache space } -struct lru_data +//---------------------------------------------------------------------------- +// mHeaderMutex must be locked for the following functions! + +LLAPRFile* LLTextureCache::openHeaderEntriesFile(bool readonly, S32 offset) +{ + llassert_always(mHeaderAPRFile == NULL); + apr_int32_t flags = readonly ? APR_READ|APR_BINARY : APR_READ|APR_WRITE|APR_BINARY; + mHeaderAPRFile = new LLAPRFile(mHeaderEntriesFileName, flags, LLAPRFile::local); + mHeaderAPRFile->seek(APR_SET, offset); + return mHeaderAPRFile; +} + +void LLTextureCache::closeHeaderEntriesFile() +{ + llassert_always(mHeaderAPRFile != NULL); + delete mHeaderAPRFile; + mHeaderAPRFile = NULL; +} + +void LLTextureCache::readEntriesHeader() +{ + // mHeaderEntriesInfo initializes to default values so safe not to read it + llassert_always(mHeaderAPRFile == NULL); + if (LLAPRFile::isExist(mHeaderEntriesFileName)) + { + LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); + } +} + +void LLTextureCache::writeEntriesHeader() { - lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; } - U32 time; - S32 index; - LLUUID uuid; - struct Compare - { - // lhs < rhs - typedef const lru_data* lru_data_ptr; - bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const + llassert_always(mHeaderAPRFile == NULL); + if (!mReadOnly) + { + LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); + } +} + +static S32 mHeaderEntriesMaxWriteIdx = 0; + +S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create) +{ + S32 idx = -1; + + id_map_t::iterator iter1 = mHeaderIDMap.find(id); + if (iter1 != mHeaderIDMap.end()) + { + idx = iter1->second; + } + + if (idx < 0) + { + if (create && !mReadOnly) { - if(a->time == b->time) - return (a->index < b->index); + if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries) + { + // Add an entry to the end of the list + idx = mHeaderEntriesInfo.mEntries++; + + } + else if (!mFreeList.empty()) + { + idx = *(mFreeList.begin()); + mFreeList.erase(mFreeList.begin()); + } else - return (a->time >= b->time); + { + // Look for a still valid entry in the LRU + for (std::set::iterator iter2 = mLRU.begin(); iter2 != mLRU.end();) + { + std::set::iterator curiter2 = iter2++; + LLUUID oldid = *curiter2; + // Erase entry from LRU regardless + mLRU.erase(curiter2); + // Look up entry and use it if it is valid + id_map_t::iterator iter3 = mHeaderIDMap.find(oldid); + if (iter3 != mHeaderIDMap.end() && iter3->second >= 0) + { + idx = iter3->second; + mHeaderIDMap.erase(oldid); + mTexturesSizeMap.erase(oldid); + break; + } + } + // if (idx < 0) at this point, we will rebuild the LRU + // and retry if called from setHeaderCacheEntry(), + // otherwise this shouldn't happen and will trigger an error + } + if (idx >= 0) + { + // Set the header index + mHeaderIDMap[id] = idx; + llassert_always(mTexturesSizeMap.erase(id) == 0); + // Initialize the entry (will get written later) + entry.init(id, time(NULL)); + // Update Header + writeEntriesHeader(); + // Write Entry + S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); + LLAPRFile* aprfile = openHeaderEntriesFile(false, offset); + S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry)); + llassert_always(bytes_written == sizeof(Entry)); + mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx); + closeHeaderEntriesFile(); + } } - }; -}; + } + else + { + // Remove this entry from the LRU if it exists + mLRU.erase(id); + // Read the entry + S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); + LLAPRFile* aprfile = openHeaderEntriesFile(true, offset); + S32 bytes_read = aprfile->read((void*)&entry, (S32)sizeof(Entry)); + llassert_always(bytes_read == sizeof(Entry)); + llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize); + closeHeaderEntriesFile(); + } + return idx; +} + +void LLTextureCache::writeEntryAndClose(S32 idx, Entry& entry) +{ + if (idx >= 0) + { + if (!mReadOnly) + { + entry.mTime = time(NULL); + llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize); + if (entry.mBodySize > 0) + { + mTexturesSizeMap[entry.mID] = entry.mBodySize; + } +// llinfos << "Updating TE: " << idx << ": " << id << " Size: " << entry.mBodySize << " Time: " << entry.mTime << llendl; + S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); + LLAPRFile* aprfile = openHeaderEntriesFile(false, offset); + S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry)); + llassert_always(bytes_written == sizeof(Entry)); + mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx); + closeHeaderEntriesFile(); + } + } +} + +U32 LLTextureCache::openAndReadEntries(std::vector& entries) +{ + U32 num_entries = mHeaderEntriesInfo.mEntries; + + mHeaderIDMap.clear(); + mTexturesSizeMap.clear(); + mFreeList.clear(); + mTexturesSizeTotal = 0; + + LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo)); + for (U32 idx=0; idxread((void*)(&entry), (S32)sizeof(Entry)); + if (bytes_read < sizeof(Entry)) + { + llwarns << "Corrupted header entries, failed at " << idx << " / " << num_entries << llendl; + closeHeaderEntriesFile(); + purgeAllTextures(false); + return 0; + } + entries.push_back(entry); +// llinfos << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << llendl; + if (entry.mImageSize < 0) + { + mFreeList.insert(idx); + } + else + { + mHeaderIDMap[entry.mID] = idx; + if (entry.mBodySize > 0) + { + mTexturesSizeMap[entry.mID] = entry.mBodySize; + mTexturesSizeTotal += entry.mBodySize; + } + llassert_always(entry.mImageSize == 0 || entry.mImageSize > entry.mBodySize); + } + } + closeHeaderEntriesFile(); + return num_entries; +} + +void LLTextureCache::writeEntriesAndClose(const std::vector& entries) +{ + S32 num_entries = entries.size(); + llassert_always(num_entries == mHeaderEntriesInfo.mEntries); + + if (!mReadOnly) + { + LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo)); + for (S32 idx=0; idxwrite((void*)(&entries[idx]), (S32)sizeof(Entry)); + llassert_always(bytes_written == sizeof(Entry)); + } + mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, num_entries-1); + closeHeaderEntriesFile(); + } +} + +//---------------------------------------------------------------------------- // Called from either the main thread or the worker thread void LLTextureCache::readHeaderCache() { LLMutexLock lock(&mHeaderMutex); - mHeaderEntriesInfo.mVersion = 0.f; - mHeaderEntriesInfo.mEntries = 0; - if (LLAPRFile::isExist(mHeaderEntriesFileName)) - { - LLAPRFile::readEx(mHeaderEntriesFileName, - (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); - } + + mLRU.clear(); // always clear the LRU + + readEntriesHeader(); + if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion) { if (!mReadOnly) { - // Info with 0 entries - mHeaderEntriesInfo.mVersion = sHeaderCacheVersion; - - LLAPRFile::writeEx(mHeaderEntriesFileName, - (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); + purgeAllTextures(false); } } else { - S32 num_entries = mHeaderEntriesInfo.mEntries; + std::vector entries; + U32 num_entries = openAndReadEntries(entries); if (num_entries) { - Entry* entries = new Entry[num_entries]; + U32 empty_entries = 0; + typedef std::pair lru_data_t; + std::set lru; + std::vector purge_list; + for (U32 i=0; i 0) + { + if (entry.mBodySize > entry.mImageSize) + { + // Shouldn't happen, failsafe only + llwarns << "Bad entry: " << i << ": " << id << ": BodySize: " << entry.mBodySize << llendl; + purge_list.push_back(id); + } + } + } } - typedef std::set lru_set_t; - lru_set_t lru; - for (S32 i=0; i sCacheMaxEntries) { - if (entries[i].mSize >= 0) // -1 indicates erased entry, skip + // Special case: cache size was reduced, need to remove entries + // Note: After we prune entries, we will call this again and create the LRU + U32 entries_to_purge = (num_entries-empty_entries) - sCacheMaxEntries; + if (entries_to_purge > 0) { - const LLUUID& id = entries[i].mID; - lru.insert(new lru_data(entries[i].mTime, i, id)); - mHeaderIDMap[id] = i; + for (std::set::iterator iter = lru.begin(); iter != lru.end(); ++iter) + { + purge_list.push_back(iter->second); + if (--entries_to_purge <= 0) + break; + } } } - mLRU.clear(); - S32 lru_entries = sCacheMaxEntries / 10; - for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) + else { - lru_data* data = *iter; - mLRU[data->index] = data->uuid; - if (--lru_entries <= 0) - break; + S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE); + for (std::set::iterator iter = lru.begin(); iter != lru.end(); ++iter) + { + mLRU.insert(iter->second); +// llinfos << "LRU: " << iter->first << " : " << iter->second << llendl; + if (--lru_entries <= 0) + break; + } + } + + if (purge_list.size() > 0) + { + for (std::vector::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter) + { + removeFromCache(*iter); + } + // If we removed any entries, we need to rebuild the entries list, + // write the header, and call this again + std::vector new_entries; + for (U32 i=0; i=0) + { + new_entries.push_back(entry); + } + } + llassert_always(new_entries.size() <= sCacheMaxEntries); + mHeaderEntriesInfo.mEntries = new_entries.size(); + writeEntriesAndClose(new_entries); + readHeaderCache(); // repeat with new entries file + } + else + { + writeEntriesAndClose(entries); } - for_each(lru.begin(), lru.end(), DeletePointer()); - delete[] entries; } } } @@ -1158,13 +1261,21 @@ void LLTextureCache::purgeAllTextures(bool purge_directories) LLFile::rmdir(dirname); } } - LLAPRFile::remove(mTexturesDirEntriesFileName); if (purge_directories) { LLFile::rmdir(mTexturesDirName); } } + mHeaderIDMap.clear(); mTexturesSizeMap.clear(); + mTexturesSizeTotal = 0; + mFreeList.clear(); + mTexturesSizeTotal = 0; + + // Info with 0 entries + mHeaderEntriesInfo.mVersion = sHeaderCacheVersion; + mHeaderEntriesInfo.mEntries = 0; + writeEntriesHeader(); } void LLTextureCache::purgeTextures(bool validate) @@ -1178,50 +1289,37 @@ void LLTextureCache::purgeTextures(bool validate) LLAppViewer::instance()->pauseMainloopTimeout(); LLMutexLock lock(&mHeaderMutex); - - S32 filesize = LLAPRFile::size(mTexturesDirEntriesFileName); - S32 num_entries = filesize / sizeof(Entry); - if (num_entries * (S32)sizeof(Entry) != filesize) - { - LL_WARNS("TextureCache") << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL; - purgeAllTextures(false); - return; - } - if (num_entries == 0) + + llinfos << "TEXTURE CACHE: Purging." << llendl; + + // Read the entries list + std::vector entries; + U32 num_entries = openAndReadEntries(entries); + if (!num_entries) { - return; // nothing to do + writeEntriesAndClose(entries); + return; // nothing to purge } - Entry* entries = new Entry[num_entries]; - S32 bytes_read = LLAPRFile::readEx(mTexturesDirEntriesFileName, - (U8*)entries, 0, num_entries*sizeof(Entry)); - if (bytes_read != filesize) + // Use mTexturesSizeMap to collect UUIDs of textures with bodies + typedef std::set > time_idx_set_t; + std::set > time_idx_set; + for (size_map_t::iterator iter1 = mTexturesSizeMap.begin(); + iter1 != mTexturesSizeMap.end(); ++iter1) { - LL_WARNS("TextureCache") << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL; - purgeAllTextures(false); - return; - } - - LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Reading " << num_entries << " Entries from " << mTexturesDirEntriesFileName << LL_ENDL; - - std::map entry_idx_map; - S64 total_size = 0; - for (S32 idx=0; idx::iterator iter = entry_idx_map.find(id); - if (iter != entry_idx_map.end()) + if (iter1->second > 0) { - // Newer entry replacing older entry - S32 pidx = iter->second; - total_size -= entries[pidx].mSize; - entries[pidx].mSize = 0; // flag: skip older entry + id_map_t::iterator iter2 = mHeaderIDMap.find(iter1->first); + if (iter2 != mHeaderIDMap.end()) + { + S32 idx = iter2->second; + time_idx_set.insert(std::make_pair(entries[idx].mTime, idx)); +// llinfos << "TIME: " << entries[idx].mTime << " TEX: " << entries[idx].mID << " IDX: " << idx << " Size: " << entries[idx].mImageSize << llendl; + } } - entry_idx_map[id] = idx; - total_size += entries[idx].mSize; } - + + // Validate 1/256th of the files on startup U32 validate_idx = 0; if (validate) { @@ -1230,19 +1328,17 @@ void LLTextureCache::purgeTextures(bool validate) gSavedSettings.setU32("CacheValidateCounter", next_idx); LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL; } - - S64 min_cache_size = sCacheMaxTexturesSize / 100 * 95; + + S64 cache_size = mTexturesSizeTotal; + S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f-TEXTURE_CACHE_PURGE_AMOUNT)*100)) / 100; S32 purge_count = 0; - S32 next_idx = 0; - for (S32 idx=0; idxsecond; bool purge_entry = false; std::string filename = getTextureFileName(entries[idx].mID); - if (total_size >= min_cache_size) + if (cache_size >= purged_cache_size) { purge_entry = true; } @@ -1252,112 +1348,47 @@ void LLTextureCache::purgeTextures(bool validate) S32 uuididx = entries[idx].mID.mData[0]; if (uuididx == validate_idx) { - LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mSize << LL_ENDL; + LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL; S32 bodysize = LLAPRFile::size(filename); - if (bodysize != entries[idx].mSize) + if (bodysize != entries[idx].mBodySize) { - LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize + LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mBodySize << filename << LL_ENDL; purge_entry = true; } } } - if (purge_entry) + else { - purge_count++; - LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL; - mFilesToDelete.push_back(filename); - total_size -= entries[idx].mSize; - entries[idx].mSize = 0; + break; } - else + + if (purge_entry) { - if (next_idx != idx) - { - entries[next_idx] = entries[idx]; - } - ++next_idx; + purge_count++; + LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL; + LLAPRFile::remove(filename); + cache_size -= entries[idx].mBodySize; + mTexturesSizeTotal -= entries[idx].mBodySize; + entries[idx].mBodySize = 0; + mTexturesSizeMap.erase(entries[idx].mID); } } - num_entries = next_idx; - mTimeLastFileDelete.reset(); + LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " << num_entries << LL_ENDL; - LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " - << num_entries << " (" << num_entries*sizeof(Entry)/1024 << "KB)" - << LL_ENDL; - - LLAPRFile::remove(mTexturesDirEntriesFileName); - LLAPRFile::writeEx(mTexturesDirEntriesFileName, - (U8*)&entries[0], 0, num_entries*sizeof(Entry)); - - mTexturesSizeTotal = 0; - mTexturesSizeMap.clear(); - for (S32 idx=0; idxresumeMainloopTimeout(); LL_INFOS("TextureCache") << "TEXTURE CACHE:" << " PURGED: " << purge_count << " ENTRIES: " << num_entries - << " CACHE SIZE: " << total_size/1024/1024 << " MB" + << " CACHE SIZE: " << mTexturesSizeTotal / 1024*1024 << " MB" << llendl; } - -void LLTextureCache::purgeTextureFilesTimeSliced(BOOL force_all) -{ - LLMutexLock lock(&mHeaderMutex); - - F32 delay_between_passes = 1.0f; // seconds - F32 max_time_per_pass = 0.1f; // seconds - - if (!force_all && mTimeLastFileDelete.getElapsedTimeF32() <= delay_between_passes) - { - return; - } - - LLTimer timer; - S32 howmany = 0; - - if (mFilesToDelete.size() > 0) - { - llinfos << "TEXTURE CACHE: " << mFilesToDelete.size() << " files scheduled for deletion" << llendl; - } - - for (LLTextureCache::filename_list_t::iterator iter = mFilesToDelete.begin(); iter!=mFilesToDelete.end(); ) - { - LLTextureCache::filename_list_t::iterator iter2 = iter++; - LLAPRFile::remove(*iter2); - mFilesToDelete.erase(iter2); - howmany++; - - if (!force_all && timer.getElapsedTimeF32() > max_time_per_pass) - { - break; - } - } - - if (!mFilesToDelete.empty()) - { - llinfos << "TEXTURE CACHE: "<< howmany << " files deleted (" - << mFilesToDelete.size() << " files left for next pass)" - << llendl; - } - - mTimeLastFileDelete.reset(); -} - - - ////////////////////////////////////////////////////////////////////////////// // call lockWorkers() first! @@ -1384,75 +1415,39 @@ LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle) } ////////////////////////////////////////////////////////////////////////////// - // Called from work thread -S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize) -{ - bool retry = false; - S32 idx = -1; +// Reads imagesize from the header, updates timestamp +S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, S32& imagesize) +{ + LLMutexLock lock(&mHeaderMutex); + Entry entry; + S32 idx = openAndReadEntry(id, entry, false); + if (idx >= 0) { - LLMutexLock lock(&mHeaderMutex); - id_map_t::iterator iter = mHeaderIDMap.find(id); - if (iter != mHeaderIDMap.end()) - { - idx = iter->second; - } - else if (touch && !mReadOnly) - { - if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries) - { - // Add an entry - idx = mHeaderEntriesInfo.mEntries++; - mHeaderIDMap[id] = idx; - // Update Info - LLAPRFile::writeEx(mHeaderEntriesFileName, - (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); - } - else if (!mLRU.empty()) - { - idx = mLRU.begin()->first; // will be erased below - const LLUUID& oldid = mLRU.begin()->second; - mHeaderIDMap.erase(oldid); - mTexturesSizeMap.erase(oldid); - mHeaderIDMap[id] = idx; - } - else - { - idx = -1; - retry = true; - } - } - if (idx >= 0) - { - if (touch && !mReadOnly) - { - // Update the lru entry - mLRU.erase(idx); - llassert_always(imagesize && *imagesize > 0); - Entry* entry = new Entry(id, *imagesize, time(NULL)); - S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); - LLAPRFile::writeEx(mHeaderEntriesFileName, - (U8*)entry, offset, sizeof(Entry)); - delete entry; - } - else if (imagesize) - { - // Get the image size - Entry entry; - S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); + imagesize = entry.mImageSize; + writeEntryAndClose(idx, entry); // updates time + } + return idx; +} - LLAPRFile::readEx(mHeaderEntriesFileName, - (U8*)&entry, offset, sizeof(Entry)); - *imagesize = entry.mSize; - } - } +// Writes imagesize to the header, updates timestamp +S32 LLTextureCache::setHeaderCacheEntry(const LLUUID& id, S32 imagesize) +{ + LLMutexLock lock(&mHeaderMutex); + llassert_always(imagesize >= 0); + Entry entry; + S32 idx = openAndReadEntry(id, entry, true); + if (idx >= 0) + { + entry.mImageSize = imagesize; + writeEntryAndClose(idx, entry); } - if (retry) + else // retry { - readHeaderCache(); // updates the lru + readHeaderCache(); // We couldn't write an entry, so refresh the LRU llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries); - idx = getHeaderCacheEntry(id, touch, imagesize); // assert above ensures no inf. recursion + idx = setHeaderCacheEntry(id, imagesize); // assert above ensures no inf. recursion } return idx; } @@ -1468,8 +1463,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const std::string& filena // so let the thread handle it LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheLocalFileWorker(this, priority, filename, id, - NULL, size, offset, 0, - responder); + NULL, size, offset, 0, + responder); handle_t handle = worker->read(); mReaders[handle] = worker; return handle; @@ -1482,8 +1477,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 pri // so let the thread handle it LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, - NULL, size, offset, 0, - responder); + NULL, size, offset, + 0, responder); handle_t handle = worker->read(); mReaders[handle] = worker; return handle; @@ -1494,7 +1489,7 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort) { lockWorkers(); handle_map_t::iterator iter = mReaders.find(handle); - llassert_always(iter != mReaders.end()); + llassert_always(iter != mReaders.end() || abort); LLTextureCacheWorker* worker = iter->second; bool res = worker->complete(); if (res || abort) @@ -1528,14 +1523,10 @@ LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 prio purgeTextures(false); mDoPurge = FALSE; } - - purgeTextureFilesTimeSliced(); // purge textures from cache in a non-hiccup-way - - LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, - data, datasize, 0, - imagesize, responder); + data, datasize, 0, + imagesize, responder); handle_t handle = worker->write(); mWriters[handle] = worker; return handle; @@ -1581,24 +1572,17 @@ void LLTextureCache::addCompleted(Responder* responder, bool success) bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id) { - if (mReadOnly) - { - return false; - } - LLMutexLock lock(&mHeaderMutex); - id_map_t::iterator iter = mHeaderIDMap.find(id); - if (iter != mHeaderIDMap.end()) + if (!mReadOnly) { - S32 idx = iter->second; + LLMutexLock lock(&mHeaderMutex); + Entry entry; + S32 idx = openAndReadEntry(id, entry, false); if (idx >= 0) { - Entry* entry = new Entry(id, -1, time(NULL)); - S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); - - LLAPRFile::writeEx(mHeaderEntriesFileName, - (U8*)entry, offset, sizeof(Entry)); - delete entry; - mLRU[idx] = id; + entry.mImageSize = -1; + entry.mBodySize = 0; + writeEntryAndClose(idx, entry); + mFreeList.insert(idx); mHeaderIDMap.erase(id); mTexturesSizeMap.erase(id); return true; diff --git a/linden/indra/newview/lltexturecache.h b/linden/indra/newview/lltexturecache.h index 1e3ed5b..e4f6934 100644 --- a/linden/indra/newview/lltexturecache.h +++ b/linden/indra/newview/lltexturecache.h @@ -48,6 +48,27 @@ class LLTextureCache : public LLWorkerThread friend class LLTextureCacheRemoteWorker; friend class LLTextureCacheLocalFileWorker; +private: + // Entries + struct EntriesInfo + { + EntriesInfo() : mVersion(0.f), mEntries(0) {} + F32 mVersion; + U32 mEntries; + }; + struct Entry + { + Entry() {} + Entry(const LLUUID& id, S32 imagesize, S32 bodysize, U32 time) : + mID(id), mImageSize(imagesize), mBodySize(bodysize), mTime(time) {} + void init(const LLUUID& id, U32 time) { mID = id, mImageSize = 0; mBodySize = 0; mTime = time; } + LLUUID mID; // 16 bytes + S32 mImageSize; // total size of image if known + S32 mBodySize; // size of body file in body cache + U32 mTime; // seconds since 1/1/1970 + }; + + public: class Responder : public LLResponder @@ -106,10 +127,14 @@ public: // debug S32 getNumReads() { return mReaders.size(); } S32 getNumWrites() { return mWriters.size(); } + S64 getUsage() { return mTexturesSizeTotal; } + S64 getMaxUsage() { return sCacheMaxTexturesSize; } + U32 getEntries() { return mHeaderEntriesInfo.mEntries; } + U32 getMaxEntries() { return sCacheMaxEntries; }; protected: // Accessed by LLTextureCacheWorker - bool appendToTextureEntryList(const LLUUID& id, S32 size); + bool updateTextureEntryList(const LLUUID& id, S32 size); std::string getLocalFileName(const LLUUID& id); std::string getTextureFileName(const LLUUID& id); void addCompleted(Responder* responder, bool success); @@ -122,8 +147,16 @@ private: void readHeaderCache(); void purgeAllTextures(bool purge_directories); void purgeTextures(bool validate); - void purgeTextureFilesTimeSliced(BOOL force_all = FALSE); - S32 getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize = NULL); + LLAPRFile* openHeaderEntriesFile(bool readonly, S32 offset); + void closeHeaderEntriesFile(); + void readEntriesHeader(); + void writeEntriesHeader(); + S32 openAndReadEntry(const LLUUID& id, Entry& entry, bool create); + void writeEntryAndClose(S32 idx, Entry& entry); + U32 openAndReadEntries(std::vector& entries); + void writeEntriesAndClose(const std::vector& entries); + S32 getHeaderCacheEntry(const LLUUID& id, S32& imagesize); + S32 setHeaderCacheEntry(const LLUUID& id, S32 imagesize); bool removeHeaderCacheEntry(const LLUUID& id); void lockHeaders() { mHeaderMutex.lock(); } void unlockHeaders() { mHeaderMutex.unlock(); } @@ -133,6 +166,7 @@ private: LLMutex mWorkersMutex; LLMutex mHeaderMutex; LLMutex mListMutex; + LLAPRFile* mHeaderAPRFile; typedef std::map handle_map_t; handle_map_t mReaders; @@ -143,49 +177,31 @@ private: typedef std::vector, bool> > responder_list_t; responder_list_t mCompletedList; - - typedef std::list filename_list_t; - filename_list_t mFilesToDelete; - LLTimer mTimeLastFileDelete; BOOL mReadOnly; - // Entries - struct EntriesInfo - { - F32 mVersion; - U32 mEntries; - }; - struct Entry - { - Entry() {} - Entry(const LLUUID& id, S32 size, U32 time) : mID(id), mSize(size), mTime(time) {} - LLUUID mID; // 128 bits - S32 mSize; // total size of image if known (NOT size cached) - U32 mTime; // seconds since 1/1/1970 - }; - // HEADERS (Include first mip) std::string mHeaderEntriesFileName; std::string mHeaderDataFileName; EntriesInfo mHeaderEntriesInfo; - typedef std::map index_map_t; - index_map_t mLRU; // index, id; stored as a map for fast removal + std::set mFreeList; // deleted entries + std::set mLRU; typedef std::map id_map_t; id_map_t mHeaderIDMap; // BODIES (TEXTURES minus headers) std::string mTexturesDirName; - std::string mTexturesDirEntriesFileName; typedef std::map size_map_t; size_map_t mTexturesSizeMap; S64 mTexturesSizeTotal; LLAtomic32 mDoPurge; - + // Statics static F32 sHeaderCacheVersion; static U32 sCacheMaxEntries; static S64 sCacheMaxTexturesSize; }; +extern const S32 TEXTURE_CACHE_ENTRY_SIZE; + #endif // LL_LLTEXTURECACHE_H diff --git a/linden/indra/newview/lltexturectrl.cpp b/linden/indra/newview/lltexturectrl.cpp index 5500973..f4476fb 100644 --- a/linden/indra/newview/lltexturectrl.cpp +++ b/linden/indra/newview/lltexturectrl.cpp @@ -556,12 +556,12 @@ void LLFloaterTexturePicker::draw() if(mImageAssetID.notNull()) { mTexturep = gImageList.getImage(mImageAssetID, MIPMAP_YES, IMMEDIATE_NO); - mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW); + mTexturep->setBoostLevel(LLViewerImageBoostLevel::BOOST_PREVIEW); } else if (!mFallbackImageName.empty()) { mTexturep = gImageList.getImageFromFile(mFallbackImageName); - mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW); + mTexturep->setBoostLevel(LLViewerImageBoostLevel::BOOST_PREVIEW); } if (mTentativeLabel) @@ -1314,13 +1314,13 @@ void LLTextureCtrl::draw() else if (!mImageAssetID.isNull()) { mTexturep = gImageList.getImage(mImageAssetID, MIPMAP_YES, IMMEDIATE_NO); - mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW); + mTexturep->setBoostLevel(LLViewerImageBoostLevel::BOOST_PREVIEW); } else if (!mFallbackImageName.empty()) { // Show fallback image. mTexturep = gImageList.getImageFromFile(mFallbackImageName); - mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW); + mTexturep->setBoostLevel(LLViewerImageBoostLevel::BOOST_PREVIEW); } // Border diff --git a/linden/indra/newview/lltexturefetch.cpp b/linden/indra/newview/lltexturefetch.cpp index e9dd792..935b8cc 100644 --- a/linden/indra/newview/lltexturefetch.cpp +++ b/linden/indra/newview/lltexturefetch.cpp @@ -32,108 +32,35 @@ #include "llviewerprecompiledheaders.h" +#include + #include "llstl.h" #include "lltexturefetch.h" #include "llcurl.h" +#include "lldir.h" #include "llhttpclient.h" +#include "llhttpstatuscodes.h" #include "llimage.h" #include "llimageworker.h" #include "llworkerthread.h" #include "llagent.h" #include "lltexturecache.h" +#include "llviewercontrol.h" #include "llviewerimagelist.h" #include "llviewerimage.h" #include "llviewerregion.h" +#include "llworld.h" ////////////////////////////////////////////////////////////////////////////// -//static class LLTextureFetchWorker : public LLWorkerClass { -friend class LLTextureFetch; - -private: -#if 0 - class URLResponder : public LLHTTPClient::Responder - { - public: - URLResponder(LLTextureFetch* fetcher, const LLUUID& id) - : mFetcher(fetcher), mID(id) - { - } - ~URLResponder() - { - } - virtual void error(U32 status, const std::string& reason) - { - mFetcher->lockQueue(); - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { -// llwarns << "LLTextureFetchWorker::URLResponder::error " << status << ": " << reason << llendl; - worker->callbackURLReceived(LLSD(), false); - } - mFetcher->unlockQueue(); - } - virtual void result(const LLSD& content) - { - mFetcher->lockQueue(); - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - worker->callbackURLReceived(content, true); - } - mFetcher->unlockQueue(); - } - private: - LLTextureFetch* mFetcher; - LLUUID mID; - }; - - class HTTPGetResponder : public LLHTTPClient::Responder - { - public: - HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id) - : mFetcher(fetcher), mID(id) - { - } - ~HTTPGetResponder() - { - } - virtual void completed(U32 status, const std::stringstream& content) - { - mFetcher->lockQueue(); - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - const std::string& cstr = content.str(); - if (200 <= status && status < 300) - { - if (203 == status) // partial information (i.e. last block) - { - worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), true); - } - else - { - worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), false); - } - } - else - { -// llinfos << "LLTextureFetchWorker::HTTPGetResponder::error " << status << ": " << cstr << llendl; - worker->callbackHttpGet(NULL, -1, true); - } - } - mFetcher->unlockQueue(); - } - private: - LLTextureFetch* mFetcher; - LLUUID mID; - }; -#endif + friend class LLTextureFetch; + friend class HTTPGetResponder; +private: class CacheReadResponder : public LLTextureCache::ReadResponder { public: @@ -179,20 +106,20 @@ private: LLUUID mID; }; - class DecodeResponder : public LLResponder + class DecodeResponder : public LLImageDecodeThread::Responder { public: DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) : mFetcher(fetcher), mID(id), mWorker(worker) { } - virtual void completed(bool success) + virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) { mFetcher->lockQueue(); LLTextureFetchWorker* worker = mFetcher->getWorker(mID); if (worker) { - worker->callbackDecoded(success); + worker->callbackDecoded(success, raw, aux); } mFetcher->unlockQueue(); } @@ -225,39 +152,47 @@ public: /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) ~LLTextureFetchWorker(); - void relese() { --mActiveCount; } + void release() { --mActiveCount; } + + void callbackHttpGet(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer, + bool last_block, bool success); + void callbackCacheRead(bool success, LLImageFormatted* image, + S32 imagesize, BOOL islocal); + void callbackCacheWrite(bool success); + void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); + + void setGetStatus(U32 status, const std::string& reason) + { + mGetStatus = status; + mGetReason = reason; + } protected: LLTextureFetchWorker(LLTextureFetch* fetcher, const LLUUID& id, const LLHost& host, F32 priority, S32 discard, S32 size); + LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, + F32 priority, S32 discard, S32 size); private: /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) - virtual std::string getName() { return LLStringUtil::null; } void resetFormattedData(); void setImagePriority(F32 priority); void setDesiredDiscard(S32 discard, S32 size); bool insertPacket(S32 index, U8* data, S32 size); void clearPackets(); + void setupPacketData(); U32 calcWorkPriority(); void removeFromCache(); bool processSimulatorPackets(); - bool decodeImage(); bool writeToCacheComplete(); - void lockWorkData() { mWorkMutex.lock(); } - void unlockWorkData() { mWorkMutex.unlock(); } + void lockWorkMutex() { mWorkMutex.lock(); } + void unlockWorkMutex() { mWorkMutex.unlock(); } - void callbackURLReceived(const LLSD& data, bool success); - void callbackHttpGet(U8* data, S32 data_size, bool last_block); - void callbackCacheRead(bool success, LLImageFormatted* image, - S32 imagesize, BOOL islocal); - void callbackCacheWrite(bool success); - void callbackDecoded(bool success); - private: enum e_state // mState { @@ -268,8 +203,8 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - LOAD_FROM_HTTP_GET_URL, - LOAD_FROM_HTTP_GET_DATA, + SEND_HTTP_REQ, + WAIT_HTTP_REQ, DECODE_IMAGE, DECODE_IMAGE_UPDATE, WRITE_TO_CACHE, @@ -280,19 +215,17 @@ private: { UNSENT = 0, QUEUED = 1, - SENT_SIM = 2, - SENT_URL = 3, - SENT_HTTP = 4 + SENT_SIM = 2 }; static const char* sStateDescs[]; e_state mState; LLTextureFetch* mFetcher; - LLImageWorker* mImageWorker; LLPointer mFormattedImage; LLPointer mRawImage; LLPointer mAuxImage; LLUUID mID; LLHost mHost; + std::string mUrl; U8 mType; F32 mImagePriority; U32 mWorkPriority; @@ -314,15 +247,18 @@ private: S32 mCachedSize; BOOL mLoaded; e_request_state mSentRequest; + handle_t mDecodeHandle; BOOL mDecoded; BOOL mWritten; BOOL mNeedsAux; BOOL mHaveAllData; BOOL mInLocalCache; + S32 mHTTPFailCount; S32 mRetryAttempt; - std::string mURL; S32 mActiveCount; - + U32 mGetStatus; + std::string mGetReason; + // Work Data LLMutex mWorkMutex; struct PacketData @@ -340,25 +276,79 @@ private: U8 mImageCodec; }; -class LLTextureFetchLocalFileWorker : public LLTextureFetchWorker -{ -friend class LLTextureFetch; - -protected: - LLTextureFetchLocalFileWorker(LLTextureFetch* fetcher, const std::string& filename, const LLUUID& id, const LLHost& host, - F32 priority, S32 discard, S32 size) - : LLTextureFetchWorker(fetcher, id, host, priority, discard, size), - mFileName(filename) - {} +////////////////////////////////////////////////////////////////////////////// -private: - /*virtual*/ std::string getName() { return mFileName; } +class HTTPGetResponder : public LLCurl::Responder +{ + LOG_CLASS(HTTPGetResponder); +public: + HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset) + : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset) + { + } + ~HTTPGetResponder() + { + } + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"))) + { + mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime); + U64 timeNow = LLTimer::getTotalTime(); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); + } + lldebugs << "HTTP COMPLETE: " << mID << llendl; + mFetcher->lockQueue(); + LLTextureFetchWorker* worker = mFetcher->getWorker(mID); + if (worker) + { + bool success = false; + bool partial = false; + if (200 <= status && status < 300) + { + success = true; + if (HTTP_PARTIAL_CONTENT == status) // partial information (i.e. last block) + { + partial = true; + } + } + else + { + worker->setGetStatus(status, reason); +// llwarns << status << ": " << reason << llendl; + } + if (!success) + { + worker->setGetStatus(status, reason); +// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; + } + mFetcher->removeFromHTTPQueue(mID); + worker->callbackHttpGet(channels, buffer, partial, success); + } + else + { + mFetcher->removeFromHTTPQueue(mID); + llwarns << "Worker not found: " << mID << llendl; + } + mFetcher->unlockQueue(); + } + private: - std::string mFileName; + LLTextureFetch* mFetcher; + LLUUID mID; + U64 mStartTime; + S32 mRequestedSize; + U32 mOffset; }; +////////////////////////////////////////////////////////////////////////////// //static const char* LLTextureFetchWorker::sStateDescs[] = { @@ -368,8 +358,8 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "CACHE_POST", "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", - "LOAD_FROM_HTTP_URL", - "LOAD_FROM_HTTP_DATA", + "SEND_HTTP_REQ", + "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", "WRITE_TO_CACHE", @@ -380,6 +370,7 @@ const char* LLTextureFetchWorker::sStateDescs[] = { // called from MAIN THREAD LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, + const std::string& url, // Optional URL const LLUUID& id, // Image UUID const LLHost& host, // Simulator host F32 priority, // Priority @@ -388,9 +379,9 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, : LLWorkerClass(fetcher, "TextureFetch"), mState(INIT), mFetcher(fetcher), - mImageWorker(NULL), mID(id), mHost(host), + mUrl(url), mImagePriority(priority), mWorkPriority(0), mRequestedPriority(0.f), @@ -409,13 +400,16 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mCachedSize(0), mLoaded(FALSE), mSentRequest(UNSENT), + mDecodeHandle(0), mDecoded(FALSE), mWritten(FALSE), mNeedsAux(FALSE), mHaveAllData(FALSE), mInLocalCache(FALSE), + mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), + mGetStatus(0), mWorkMutex(NULL), mFirstPacket(0), mLastPacket(-1), @@ -425,12 +419,14 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, calcWorkPriority(); mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; // llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl; + lockWorkMutex(); if (!mFetcher->mDebugPause) { U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; addWork(0, work_priority ); } setDesiredDiscard(discard, size); + unlockWorkMutex(); } LLTextureFetchWorker::~LLTextureFetchWorker() @@ -440,7 +436,7 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); - lockWorkData(); + lockWorkMutex(); if (mCacheReadHandle != LLTextureCache::nullHandle()) { mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); @@ -449,13 +445,9 @@ LLTextureFetchWorker::~LLTextureFetchWorker() { mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); } - if (mImageWorker) - { - mImageWorker->scheduleDelete(); - } mFormattedImage = NULL; clearPackets(); - unlockWorkData(); + unlockWorkMutex(); } void LLTextureFetchWorker::clearPackets() @@ -467,6 +459,38 @@ void LLTextureFetchWorker::clearPackets() mFirstPacket = 0; } +void LLTextureFetchWorker::setupPacketData() +{ + S32 data_size = 0; + if (mFormattedImage.notNull()) + { + data_size = mFormattedImage->getDataSize(); + } + if (data_size > 0) + { + // Only used for simulator requests + mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1; + if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size) + { + llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl; + removeFromCache(); + resetFormattedData(); + clearPackets(); + } + else if (mFileSize > 0) + { + mLastPacket = mFirstPacket-1; + mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1; + } + else + { + // This file was cached using HTTP so we have to refetch the first packet + resetFormattedData(); + clearPackets(); + } + } +} + U32 LLTextureFetchWorker::calcWorkPriority() { // llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerImage::maxDecodePriority()); @@ -538,7 +562,6 @@ void LLTextureFetchWorker::resetFormattedData() // Called from MAIN thread void LLTextureFetchWorker::startWork(S32 param) { - llassert(mImageWorker == NULL); llassert(mFormattedImage.isNull()); } @@ -549,6 +572,14 @@ bool LLTextureFetchWorker::doWork(S32 param) { LLMutexLock lock(&mWorkMutex); + if ((mFetcher->isQuitting() || (mImagePriority <= 0.0f) || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) + { + if (mState < WRITE_TO_CACHE) + { + return true; // abort + } + } + if (mFetcher->mDebugPause) { return false; // debug: don't do any work @@ -563,14 +594,6 @@ bool LLTextureFetchWorker::doWork(S32 param) mFetchTimer.reset(); } - if (mImagePriority <= 0.0f) - { - if (mState < WRITE_TO_CACHE) - { - return true; // cancel request - } - } - if (mState == INIT) { mRequestedDiscard = -1; @@ -590,7 +613,6 @@ bool LLTextureFetchWorker::doWork(S32 param) clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); mCacheWriteHandle = LLTextureCache::nullHandle(); - mURL.clear(); mState = LOAD_FROM_TEXTURE_CACHE; // fall through } @@ -612,16 +634,27 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); - if (getName().empty()) + if (mUrl.compare(0, 7, "file://") == 0) + { + // read file from local disk + std::string filename = mUrl.substr(7, std::string::npos); + mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, + offset, size, responder); + } + else if (mUrl.empty()) { mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); } else { - // read file from local disk - mCacheReadHandle = mFetcher->mTextureCache->readFromCache(getName(), mID, cache_priority, - offset, size, responder); + if (!(mUrl.compare(0, 7, "http://") == 0)) + { + // *TODO:?remove this warning + llwarns << "Unknown URL Type: " << mUrl << llendl; + } + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + mState = SEND_HTTP_REQ; } } @@ -659,67 +692,92 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - if (!getName().empty()) + if (mUrl.compare(0, 7, "file://") == 0) { // failed to load local file, we're done. return true; } // need more data - mState = LOAD_FROM_NETWORK; + else + { + mState = LOAD_FROM_NETWORK; // CACHE_POST --> LOAD_FROM_NETWORK, or SEND_HTTP_REQ see below. + // This is true because mSentRequest is set to UNSENT in INIT and if we get here we went through + // the states INIT --> LOAD_FROM_TEXTURE_CACHE --> CACHE_POST. Therefore either + // mFetcher->addToNetworkQueue(this) is called below, or mState is set to SEND_HTTP_REQ. + llassert(mSentRequest == UNSENT); + } // fall through } } if (mState == LOAD_FROM_NETWORK) { - if (mSentRequest == UNSENT) + bool get_url = gSavedSettings.getBOOL("ImagePipelineUseHTTP"); + if (!mUrl.empty()) get_url = false; +// if (mHost != LLHost::invalid) get_url = false; + if ( get_url ) { - if (mFormattedImage.isNull()) - { - mFormattedImage = new LLImageJ2C; - } - // Add this to the network queue and sit here. - // LLTextureFetch::update() will send off a request which will change our state - S32 data_size = mFormattedImage->getDataSize(); - if (data_size > 0) + LLViewerRegion* region = NULL; + if (mHost == LLHost::invalid) + region = gAgent.getRegion(); + else + region = LLWorld::getInstance()->getRegion(mHost); + + if (region) { - // Only used for simulator requests - mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1; - if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size) - { -// llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl; - removeFromCache(); - resetFormattedData(); - clearPackets(); - } - else + std::string http_url = region->getCapability("GetTexture"); + if (!http_url.empty()) { - mLastPacket = mFirstPacket-1; - mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1; + mUrl = http_url + "/?texture_id=" + mID.asString().c_str(); } } + else + { + llwarns << "Region not found for host: " << mHost << llendl; + } + } + if (!mUrl.empty()) + { + mState = LLTextureFetchWorker::SEND_HTTP_REQ; + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + // don't return, fall through to next state + } + else if (mSentRequest == UNSENT) + { + // Add this to the network queue and sit here. + // LLTextureFetch::update() will send off a request which will change our state mRequestedSize = mDesiredSize; mRequestedDiscard = mDesiredDiscard; mSentRequest = QUEUED; - mFetcher->lockQueue(); mFetcher->addToNetworkQueue(this); - mFetcher->unlockQueue(); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + return false; + } + else + { + // Shouldn't need to do anything here + //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); + // Make certain this is in the network queue + //mFetcher->addToNetworkQueue(this); + //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + return false; } - return false; } if (mState == LOAD_FROM_SIMULATOR) { + if (mFormattedImage.isNull()) + { + mFormattedImage = new LLImageJ2C; + } if (processSimulatorPackets()) { - mFetcher->lockQueue(); - mFetcher->removeFromNetworkQueue(this); - mFetcher->unlockQueue(); + mFetcher->removeFromNetworkQueue(this, false); if (mFormattedImage.isNull() || !mFormattedImage->getDataSize()) { // processSimulatorPackets() failed // llwarns << "processSimulatorPackets() failed to load buffer" << llendl; + // FIXME: Don't we need a mState change? return true; // failed } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); @@ -732,103 +790,91 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } -#if 0 - if (mState == LOAD_FROM_HTTP_GET_URL) - { - if (!mSentRequest) - { - mSentRequest = TRUE; - mLoaded = FALSE; - std::string url; - LLViewerRegion* region = gAgent.getRegion(); - if (region) + if (mState == SEND_HTTP_REQ) + { + { + const S32 HTTP_QUEUE_MAX_SIZE = 32; + // *TODO: Integrate this with llviewerthrottle + // Note: LLViewerThrottle uses dynamic throttling which makes sense for UDP, + // but probably not for Textures. + // Set the throttle to the entire bandwidth, assuming UDP packets will get priority + // when they are needed + F32 max_bandwidth = mFetcher->mMaxBandwidth; + if ((mFetcher->getHTTPQueueSize() >= HTTP_QUEUE_MAX_SIZE) || + (mFetcher->getTextureBandwidth() > max_bandwidth)) { - url = region->getCapability("RequestTextureDownload"); - } - if (!url.empty()) - { - LLSD sd; - sd = mID.asString(); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - LLHTTPClient::post(url, sd, new URLResponder(mFetcher, mID)); + // Make normal priority and return (i.e. wait until there is room in the queue) + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return false; } - else + + S32 cur_size = 0; + if (mFormattedImage.notNull()) { -// llwarns << mID << ": HTTP get url failed, requesting from simulator" << llendl; - mSentRequest = FALSE; - mState = LOAD_FROM_SIMULATOR; - return false; + cur_size = mFormattedImage->getDataSize(); // amount of data we already have } - } - else - { - if (mLoaded) - { - if (!mURL.empty()) - { - mState = LOAD_FROM_HTTP_GET_DATA; - mSentRequest = FALSE; // reset - mLoaded = FALSE; // reset - } - else - { -// llwarns << mID << ": HTTP get url is empty, requesting from simulator" << llendl; - mSentRequest = FALSE; - mState = LOAD_FROM_SIMULATOR; - return false; - } - } - } - // fall through - } - - if (mState == LOAD_FROM_HTTP_GET_DATA) - { - if (!mSentRequest) - { - mSentRequest = TRUE; - S32 cur_size = mFormattedImage->getDataSize(); // amount of data we already have mRequestedSize = mDesiredSize; mRequestedDiscard = mDesiredDiscard; -#if 1 // *TODO: LLCurl::getByteRange is broken (ignores range) - cur_size = 0; - mFormattedImage->deleteData(); -#endif mRequestedSize -= cur_size; - // F32 priority = mImagePriority / (F32)LLViewerImage::maxDecodePriority(); // 0-1 +// F32 priority = mImagePriority / (F32)LLViewerImage::maxDecodePriority(); // 0-1 S32 offset = cur_size; mBufferSize = cur_size; // This will get modified by callbackHttpGet() - std::string url; - if (mURL.empty()) + + bool res = false; + if (!mUrl.empty()) { - //url = "http://asset.agni/0000002f-38ae-0e17-8e72-712e58964e9c.texture"; - std::stringstream urlstr; - urlstr << "http://asset.agni/" << mID.asString() << ".texture"; - url = urlstr.str(); + mLoaded = FALSE; + mGetStatus = 0; + mGetReason.clear(); + lldebugs << "HTTP GET: " << mID << " Offset: " << offset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << max_bandwidth + << llendl; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mState = WAIT_HTTP_REQ; + + mFetcher->addToHTTPQueue(mID); + // Will call callbackHttpGet when curl request completes + std::vector headers; + headers.push_back("Accept: image/x-j2c"); + res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, + new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset)); } - else + if (!res) { - url = mURL; + llwarns << "HTTP GET request failed for " << mID << llendl; + resetFormattedData(); + ++mHTTPFailCount; + return true; // failed } - mLoaded = FALSE; -// llinfos << "HTTP GET: " << mID << " Offset: " << offset << " Bytes: " << mRequestedSize << llendl; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - LLCurl::getByteRange(url, offset, mRequestedSize, - new HTTPGetResponder(mFetcher, mID)); // *TODO: use mWorkPriority - return false; // not done + // fall through } - + } + + if (mState == WAIT_HTTP_REQ) + { if (mLoaded) { - S32 cur_size = mFormattedImage->getDataSize(); + S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { -// llwarns << "http get failed for: " << mID << llendl; + const S32 HTTP_MAX_RETRY_COUNT = 4; + llinfos << "HTTP GET failed for: " << mUrl + << " Status: " << mGetStatus << " Reason: " << mGetReason + << " Try:" << mHTTPFailCount+1 << "/" << HTTP_MAX_RETRY_COUNT << llendl; if (cur_size == 0) { - resetFormattedData(); - return true; // failed + ++mHTTPFailCount; + if (mGetStatus == HTTP_NOT_FOUND || mHTTPFailCount >= HTTP_MAX_RETRY_COUNT) + { + resetFormattedData(); + return true; // failed + } + else + { + mState = SEND_HTTP_REQ; + return false; // retry + } } else { @@ -836,6 +882,18 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; // use what we have } } + + if (mFormattedImage.isNull()) + { + // For now, create formatted image based on extension + std::string extension = gDirUtilp->getExtension(mUrl); + mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); + if (mFormattedImage.isNull()) + { + mFormattedImage = new LLImageJ2C; // default + } + } + llassert_always(mBufferSize == cur_size + mRequestedSize); if (mHaveAllData) { @@ -854,16 +912,16 @@ bool LLTextureFetchWorker::doWork(S32 param) mBuffer = NULL; mBufferSize = 0; mLoadedDiscard = mRequestedDiscard; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + return false; + } + else + { + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } - - // NOTE: Priority gets updated when the http get completes (in callbackHTTPGet()) - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - return false; } -#endif if (mState == DECODE_IMAGE) { @@ -871,19 +929,19 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it mRawImage = NULL; mAuxImage = NULL; - llassert_always(mImageWorker == NULL); llassert_always(mFormattedImage.notNull()); S32 discard = mHaveAllData ? 0 : mLoadedDiscard; U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority; mDecoded = FALSE; mState = DECODE_IMAGE_UPDATE; - mImageWorker = new LLImageWorker(mFormattedImage, image_priority, discard, new DecodeResponder(mFetcher, mID, this)); - // fall though (need to call requestDecodedData() to start work) + mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux, + new DecodeResponder(mFetcher, mID, this)); + // fall though } if (mState == DECODE_IMAGE_UPDATE) { - if (decodeImage()) + if (mDecoded) { if (mDecodedDiscard < 0) { @@ -891,6 +949,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // Cache file should be deleted, try again // llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl; + llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; ++mRetryAttempt; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); @@ -918,9 +977,10 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WRITE_TO_CACHE) { - if (mInLocalCache || !mFileSize || mSentRequest == UNSENT) + if (mInLocalCache || mSentRequest == UNSENT || mFormattedImage.isNull()) { - // If we're in a local cache or we didn't actually receive any new data, skip + // If we're in a local cache or we didn't actually receive any new data, + // or we failed to load anything, skip mState = DONE; return false; } @@ -979,10 +1039,10 @@ bool LLTextureFetchWorker::doWork(S32 param) // Called from MAIN thread void LLTextureFetchWorker::endWork(S32 param, bool aborted) { - if (mImageWorker) + if (mDecodeHandle != 0) { - mImageWorker->scheduleDelete(); - mImageWorker = NULL; + mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false); + mDecodeHandle = 0; } mFormattedImage = NULL; } @@ -1035,7 +1095,7 @@ bool LLTextureFetchWorker::deleteOK() if ((haveWork() && // not ok to delete from these states - ((mState >= LOAD_FROM_HTTP_GET_URL && mState <= LOAD_FROM_HTTP_GET_DATA) || + ((mState >= SEND_HTTP_REQ && mState <= WAIT_HTTP_REQ) || (mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE)))) { delete_ok = false; @@ -1044,7 +1104,6 @@ bool LLTextureFetchWorker::deleteOK() return delete_ok; } - void LLTextureFetchWorker::removeFromCache() { if (!mInLocalCache) @@ -1061,6 +1120,7 @@ bool LLTextureFetchWorker::processSimulatorPackets() if (mFormattedImage.isNull() || mRequestedSize < 0) { // not sure how we got here, but not a valid state, abort! + llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; return true; } @@ -1074,6 +1134,12 @@ bool LLTextureFetchWorker::processSimulatorPackets() buffer_size += mPackets[i]->mSize; } bool have_all_data = mLastPacket >= mTotalPackets-1; + if (mRequestedSize <= 0) + { + // We received a packed but haven't requested anything yet (edge case) + // Return true (we're "done") since we didn't request anything + return true; + } if (buffer_size >= mRequestedSize || have_all_data) { /// We have enough (or all) data @@ -1109,50 +1175,36 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// -void LLTextureFetchWorker::callbackURLReceived(const LLSD& data, bool success) +void LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer, + bool last_block, bool success) { -#if 0 LLMutexLock lock(&mWorkMutex); - if (!mSentRequest || mState != LOAD_FROM_HTTP_GET_URL) - { - llwarns << "callbackURLReceived for unrequested fetch worker, req=" - << mSentRequest << " state= " << mState << llendl; - return; - } - if (success) - { - mURL = data.asString(); - } - mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -#endif -} -////////////////////////////////////////////////////////////////////////////// - -void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_block) -{ -#if 0 - LLMutexLock lock(&mWorkMutex); - if (!mSentRequest || mState != LOAD_FROM_HTTP_GET_DATA) + if (mState != WAIT_HTTP_REQ) { - llwarns << "callbackHttpGet for unrequested fetch worker, req=" - << mSentRequest << " state= " << mState << llendl; + llwarns << "callbackHttpGet for unrequested fetch worker: " << mID + << " req=" << mSentRequest << " state= " << mState << llendl; return; } -// llinfos << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << llendl; if (mLoaded) { llwarns << "Duplicate callback for " << mID.asString() << llendl; return; // ignore duplicate callback } - if (data_size >= 0) + if (success) { + // get length of stream: + S32 data_size = buffer->countAfter(channels.in(), NULL); + + gImageList.sTextureBits += data_size * 8; // Approximate - does not include header bits + + //llinfos << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << llendl; if (data_size > 0) { + // *TODO: set the formatted image data here directly to avoid the copy mBuffer = new U8[data_size]; - // *TODO: set the formatted image data here - memcpy(mBuffer, data, data_size); + buffer->readAfter(channels.in(), NULL, mBuffer, data_size); mBufferSize += data_size; if (data_size < mRequestedSize || last_block == true) { @@ -1160,10 +1212,11 @@ void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_bl } else if (data_size > mRequestedSize) { - // *TODO: This will happen until we fix LLCurl::getByteRange() -// llinfos << "HUH?" << llendl; + // *TODO: This shouldn't be happening any more + llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; mHaveAllData = TRUE; - mFormattedImage->deleteData(); + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had mBufferSize = data_size; } } @@ -1181,7 +1234,6 @@ void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_bl } mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -#endif } ////////////////////////////////////////////////////////////////////////////// @@ -1197,7 +1249,7 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } if (success) { - llassert_always(imagesize > 0); + llassert_always(imagesize >= 0); mFileSize = imagesize; mFormattedImage = image; mImageCodec = image->getCodec(); @@ -1225,65 +1277,49 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) ////////////////////////////////////////////////////////////////////////////// -void LLTextureFetchWorker::callbackDecoded(bool success) +void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux) { + LLMutexLock lock(&mWorkMutex); + if (mDecodeHandle == 0) + { + return; // aborted, ignore + } if (mState != DECODE_IMAGE_UPDATE) { // llwarns << "Decode callback for " << mID << " with state = " << mState << llendl; + mDecodeHandle = 0; return; } -// llinfos << mID << " : DECODE COMPLETE " << llendl; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} - -////////////////////////////////////////////////////////////////////////////// - -bool LLTextureFetchWorker::decodeImage() -{ - if(!mImageWorker) - { - //LLTextureFetchWorker is aborted, skip image decoding. - return true ; - } - - bool res = true; - if (mRawImage.isNull()) - { - res = false; - if (mImageWorker->requestDecodedData(mRawImage, -1)) - { - res = true; -// llinfos << mID << " : BASE DECODE FINISHED" << llendl; - } - } - if (res && - (mRawImage.notNull() && mRawImage->getDataSize() > 0) && - (mNeedsAux && mAuxImage.isNull())) + llassert_always(mFormattedImage.notNull()); + + mDecodeHandle = 0; + if (success) { - res = false; - if (mImageWorker->requestDecodedAuxData(mAuxImage, 4, -1)) - { - res = true; -// llinfos << mID << " : AUX DECODE FINISHED" << llendl; - } + mRawImage = raw; + mAuxImage = aux; + mDecodedDiscard = mFormattedImage->getDiscardLevel(); +// llinfos << mID << " : DECODE FINISHED. DISCARD: " << mDecodedDiscard << llendl; } - if (res) + else { - if ((mRawImage.notNull() && mRawImage->getDataSize() > 0) && - (!mNeedsAux || (mAuxImage.notNull() && mAuxImage->getDataSize() > 0))) + if (mFormattedImage.notNull()) { - mDecodedDiscard = mFormattedImage->getDiscardLevel(); -// llinfos << mID << " : DECODE FINISHED. DISCARD: " << mDecodedDiscard << llendl; + LL_WARNS("http-texture") << "DECODE FAILED: id = " << mID << ", Discard = " << (S32)mFormattedImage->getDiscardLevel() << LL_ENDL; } else { -// llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl; - removeFromCache(); + LL_WARNS("http-texture") << "DECODE FAILED: id = " << mID << ", mFormattedImage is Null!" << LL_ENDL; } - mImageWorker->scheduleDelete(); - mImageWorker = NULL; + removeFromCache(); + mDecodedDiscard = -1; // Redundant, here for clarity and paranoia } - return res; + mDecoded = TRUE; +// llinfos << mID << " : DECODE COMPLETE " << llendl; + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + // Set the decode flag at the end of the callback or we trigger race conditions between the fetch thread and the + // decode threads that's calling this callback. The fetch thread might set mFormattedImage to NULL before we + // have time here to call getDiscardLevel() which causes crashes + mDecoded = TRUE; } ////////////////////////////////////////////////////////////////////////////// @@ -1314,15 +1350,21 @@ bool LLTextureFetchWorker::writeToCacheComplete() ////////////////////////////////////////////////////////////////////////////// // public -LLTextureFetch::LLTextureFetch(LLTextureCache* cache, bool threaded) +LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded) : LLWorkerThread("TextureFetch", threaded), mDebugCount(0), mDebugPause(FALSE), mPacketCount(0), mBadPacketCount(0), mQueueMutex(getAPRPool()), - mTextureCache(cache) + mNetworkQueueMutex(getAPRPool()), + mTextureCache(cache), + mImageDecodeThread(imagedecodethread), + mTextureBandwidth(0), + mCurlGetRequest(NULL) { + mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); + mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); } LLTextureFetch::~LLTextureFetch() @@ -1330,13 +1372,7 @@ LLTextureFetch::~LLTextureFetch() // ~LLQueuedThread() called here } -bool LLTextureFetch::createRequest(const LLUUID& id, const LLHost& host, F32 priority, - S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux) -{ - return createRequest(LLStringUtil::null, id, host, priority, w, h, c, desired_discard, needs_aux); -} - -bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id, const LLHost& host, F32 priority, +bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux) { if (mDebugPause) @@ -1361,7 +1397,14 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id } S32 desired_size; - if (desired_discard == 0) + std::string exten = gDirUtilp->getExtension(url); + if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)) + { + // Only do partial requests for J2C at the moment + //llinfos << "Merov : LLTextureFetch::createRequest(), blocking fetch on " << url << llendl; + desired_size = MAX_IMAGE_DATA_SIZE; + } + else if (desired_discard == 0) { // if we want the entire image, and we know its size, then get it all // (calcDataSizeJ2C() below makes assumptions about how the image @@ -1389,28 +1432,25 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id { return false; // need to wait for previous aborted request to complete } - worker->lockWorkData(); + worker->lockWorkMutex(); worker->setImagePriority(priority); worker->setDesiredDiscard(desired_discard, desired_size); - worker->unlockWorkData(); + worker->unlockWorkMutex(); if (!worker->haveWork()) { + worker->lockWorkMutex(); + if (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK || worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) + { + removeFromNetworkQueue(worker, true); + } worker->mState = LLTextureFetchWorker::INIT; + worker->unlockWorkMutex(); worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); } } else { - if (filename.empty()) - { - // do remote fetch - worker = new LLTextureFetchWorker(this, id, host, priority, desired_discard, desired_size); - } - else - { - // do local file fetch - worker = new LLTextureFetchLocalFileWorker(this, filename, id, host, priority, desired_discard, desired_size); - } + worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size); mRequestMap[id] = worker; } worker->mActiveCount++; @@ -1430,10 +1470,9 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) } // protected - -// call lockQueue() first! void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { + LLMutexLock lock(&mNetworkQueueMutex); if (mRequestMap.find(worker->mID) != mRequestMap.end()) { // only add to the queue if in the request map @@ -1447,10 +1486,27 @@ void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) } } -// call lockQueue() first! -void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker) +void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) +{ + LLMutexLock lock(&mNetworkQueueMutex); + size_t erased = mNetworkQueue.erase(worker->mID); + if (cancel && erased > 0) + { + mCancelQueue[worker->mHost].insert(worker->mID); + } +} + +// protected +void LLTextureFetch::addToHTTPQueue(const LLUUID& id) +{ + LLMutexLock lock(&mNetworkQueueMutex); + mHTTPTextureQueue.insert(id); +} + +void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id) { - mNetworkQueue.erase(worker->mID); + LLMutexLock lock(&mNetworkQueueMutex); + mHTTPTextureQueue.erase(id); } // call lockQueue() first! @@ -1458,11 +1514,7 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { size_t erased_1 = mRequestMap.erase(worker->mID); llassert_always(erased_1 > 0) ; - size_t erased = mNetworkQueue.erase(worker->mID); - if (cancel && erased > 0) - { - mCancelQueue[worker->mHost].insert(worker->mID); - } + removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; worker->scheduleDelete(); @@ -1511,7 +1563,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } else { - worker->lockWorkData(); + worker->lockWorkMutex(); if ((worker->mDecodedDiscard >= 0) && (worker->mDecodedDiscard < discard_level || discard_level < 0) && (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) @@ -1521,7 +1573,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, if (worker->mRawImage) raw = worker->mRawImage; if (worker->mAuxImage) aux = worker->mAuxImage; } - worker->unlockWorkData(); + worker->unlockWorkMutex(); } } else @@ -1538,9 +1590,9 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkData(); + worker->lockWorkMutex(); worker->setImagePriority(priority); - worker->unlockWorkData(); + worker->unlockWorkMutex(); res = true; } return res; @@ -1548,40 +1600,106 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) ////////////////////////////////////////////////////////////////////////////// +// MAIN THREAD //virtual S32 LLTextureFetch::update(U32 max_time_ms) { S32 res; + + mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); + res = LLWorkerThread::update(max_time_ms); - const F32 REQUEST_TIME = 1.f; - - // Periodically, gather the list of textures that need data from the network - // And send the requests out to the simulators - if (mNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME) + if (!mDebugPause) { - mNetworkTimer.reset(); sendRequestListToSimulators(); } return res; } +// WORKER THREAD +void LLTextureFetch::startThread() +{ + // Construct mCurlGetRequest from Worker Thread + mCurlGetRequest = new LLCurlRequest(); +} + +// WORKER THREAD +void LLTextureFetch::endThread() +{ + // Destroy mCurlGetRequest from Worker Thread + delete mCurlGetRequest; + mCurlGetRequest = NULL; +} + +// WORKER THREAD +void LLTextureFetch::threadedUpdate() +{ + llassert_always(mCurlGetRequest); + + // Limit update frequency + const F32 PROCESS_TIME = 0.05f; + static LLFrameTimer process_timer; + if (process_timer.getElapsedTimeF32() < PROCESS_TIME) + { + return; + } + process_timer.reset(); + + // Update Curl on same thread as mCurlGetRequest was constructed + S32 processed = mCurlGetRequest->process(); + if (processed > 0) + { + lldebugs << "processed: " << processed << " messages." << llendl; + } + +#if 0 + const F32 INFO_TIME = 1.0f; + static LLFrameTimer info_timer; + if (info_timer.getElapsedTimeF32() >= INFO_TIME) + { + S32 q = mCurlGetRequest->getQueued(); + if (q > 0) + { + llinfos << "Queued gets: " << q << llendl; + info_timer.reset(); + } + } +#endif + +} + ////////////////////////////////////////////////////////////////////////////// void LLTextureFetch::sendRequestListToSimulators() { + // All requests + const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps + + // Sim requests const S32 IMAGES_PER_REQUEST = 50; - const F32 LAZY_FLUSH_TIMEOUT = 15.f; // 10.0f // temp + const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp const F32 MIN_REQUEST_TIME = 1.0f; const F32 MIN_DELTA_PRIORITY = 1000.f; - LLMutexLock lock(&mQueueMutex); + // Periodically, gather the list of textures that need data from the network + // And send the requests out to the simulators + static LLFrameTimer timer; + if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME) + { + return; + } + timer.reset(); + LLMutexLock lock(&mQueueMutex); + // Send requests typedef std::set request_list_t; typedef std::map< LLHost, request_list_t > work_request_map_t; work_request_map_t requests; + { + LLMutexLock lock2(&mNetworkQueueMutex); for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) { queue_t::iterator curiter = iter++; @@ -1591,65 +1709,66 @@ void LLTextureFetch::sendRequestListToSimulators() mNetworkQueue.erase(curiter); continue; // paranoia } + llassert(req->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK || LLTextureFetchWorker::LOAD_FROM_SIMULATOR); + if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && + (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + { + // We really should never ever get here anymore. + llwarns << "SNOW-119 failure: Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; + mNetworkQueue.erase(curiter); + continue; + } if (req->mID == mDebugID) { mDebugCount++; // for setting breakpoints } - if (req->mTotalPackets > 0 && req->mLastPacket >= req->mTotalPackets-1) + if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && + req->mTotalPackets > 0 && + req->mLastPacket >= req->mTotalPackets-1) { // We have all the packets... make sure this is high priority // req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); continue; } F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); - F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); - if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || - (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || - (elapsed >= LAZY_FLUSH_TIMEOUT)) { - requests[req->mHost].insert(req); + F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); + if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || + (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || + (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + { + requests[req->mHost].insert(req); + } } } - - std::string http_url; -#if 0 - if (gSavedSettings.getBOOL("ImagePipelineUseHTTP")) - { - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - http_url = region->getCapability("RequestTextureDownload"); - } } -#endif - + for (work_request_map_t::iterator iter1 = requests.begin(); iter1 != requests.end(); ++iter1) { - bool use_http = http_url.empty() ? false : true; LLHost host = iter1->first; // invalid host = use agent host if (host == LLHost::invalid) { host = gAgent.getRegionHost(); } - else - { - use_http = false; - } - if (use_http) - { - } - else + S32 sim_request_count = 0; + + for (request_list_t::iterator iter2 = iter1->second.begin(); + iter2 != iter1->second.end(); ++iter2) { - S32 request_count = 0; - for (request_list_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) + LLTextureFetchWorker* req = *iter2; + if (gMessageSystem) { - LLTextureFetchWorker* req = *iter2; - req->mSentRequest = LLTextureFetchWorker::SENT_SIM; - if (0 == request_count) + if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) + { + // Initialize packet data based on data read from cache + req->lockWorkMutex(); + req->setupPacketData(); + req->unlockWorkMutex(); + } + if (0 == sim_request_count) { gMessageSystem->newMessageFast(_PREHASH_RequestImage); gMessageSystem->nextBlockFast(_PREHASH_AgentData); @@ -1666,30 +1785,42 @@ void LLTextureFetch::sendRequestListToSimulators() // llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard // << " Packet: " << packet << " Priority: " << req->mImagePriority << llendl; - req->lockWorkData(); + if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"))) + { + mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime()); + mTextureInfo.setRequestOffset(req->mID, 0); + mTextureInfo.setRequestSize(req->mID, 0); + mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); + } + + req->lockWorkMutex(); + req->mSentRequest = LLTextureFetchWorker::SENT_SIM; req->mSimRequestedDiscard = req->mDesiredDiscard; req->mRequestedPriority = req->mImagePriority; req->mRequestedTimer.reset(); - req->unlockWorkData(); - request_count++; - if (request_count >= IMAGES_PER_REQUEST) + req->unlockWorkMutex(); + sim_request_count++; + if (sim_request_count >= IMAGES_PER_REQUEST) { -// llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; +// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; + gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; + sim_request_count = 0; } } - if (request_count > 0 && request_count < IMAGES_PER_REQUEST) - { -// llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; - gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; - } + } + if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST) + { +// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; + gMessageSystem->sendSemiReliable(host, NULL, NULL); + sim_request_count = 0; } } // Send cancelations - if (!mCancelQueue.empty()) + { + LLMutexLock lock2(&mNetworkQueueMutex); + if (gMessageSystem && !mCancelQueue.empty()) { for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); iter1 != mCancelQueue.end(); ++iter1) @@ -1732,6 +1863,7 @@ void LLTextureFetch::sendRequestListToSimulators() } mCancelQueue.clear(); } + } } ////////////////////////////////////////////////////////////////////////////// @@ -1773,16 +1905,20 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 { LLMutexLock lock(&mQueueMutex); LLTextureFetchWorker* worker = getWorker(id); - bool res = true; ++mPacketCount; if (!worker) { // llwarns << "Received header for non active worker: " << id << llendl; - res = false; + ++mBadPacketCount; + mCancelQueue[host].insert(id); + return false; } - else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK || + + bool res = true; + worker->lockWorkMutex(); + if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK || worker->mSentRequest != LLTextureFetchWorker::SENT_SIM) { // llwarns << "receiveImageHeader for worker: " << id @@ -1805,21 +1941,20 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 { ++mBadPacketCount; mCancelQueue[host].insert(id); - return false; } - - worker->lockWorkData(); - - // Copy header data into image object - worker->mImageCodec = codec; - worker->mTotalPackets = packets; - worker->mFileSize = (S32)totalbytes; - llassert_always(totalbytes > 0); - llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); - res = worker->insertPacket(0, data, data_size); - worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); - worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; - worker->unlockWorkData(); + else + { + // Copy header data into image object + worker->mImageCodec = codec; + worker->mTotalPackets = packets; + worker->mFileSize = (S32)totalbytes; + llassert_always(totalbytes > 0); + llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); + res = worker->insertPacket(0, data, data_size); + worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; + } + worker->unlockWorkMutex(); return res; } @@ -1853,7 +1988,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 return false; } - worker->lockWorkData(); + worker->lockWorkMutex(); res = worker->insertPacket(packet_num, data, data_size); @@ -1866,12 +2001,19 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 else { // llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id -// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl; - removeFromNetworkQueue(worker); // failsafe - mCancelQueue[host].insert(id); +// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl; } - - worker->unlockWorkData(); + + if(packet_num >= (worker->mTotalPackets - 1)) + { + if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"))) + { + U64 timeNow = LLTimer::getTotalTime(); + mTextureInfo.setRequestSize(id, worker->mFileSize); + mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow); + } + } + worker->unlockWorkMutex(); return res; } @@ -1892,7 +2034,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r LLTextureFetchWorker* worker = getWorker(id); if (worker && worker->haveWork()) { - worker->lockWorkData(); + worker->lockWorkMutex(); state = worker->mState; fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); @@ -1909,7 +2051,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize; } } - if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::LOAD_FROM_HTTP_GET_DATA) + if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ) { requested_priority = worker->mRequestedPriority; } @@ -1918,7 +2060,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r requested_priority = worker->mImagePriority; } fetch_priority = worker->getPriority(); - worker->unlockWorkData(); + worker->unlockWorkMutex(); } data_progress_p = data_progress; requested_priority_p = requested_priority; @@ -1944,5 +2086,3 @@ void LLTextureFetch::dump() } } - -////////////////////////////////////////////////////////////////////////////// diff --git a/linden/indra/newview/lltexturefetch.h b/linden/indra/newview/lltexturefetch.h index 56650e7..c48f609 100644 --- a/linden/indra/newview/lltexturefetch.h +++ b/linden/indra/newview/lltexturefetch.h @@ -37,26 +37,29 @@ #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" +#include "llcurl.h" +#include "lltextureinfo.h" class LLViewerImage; class LLTextureFetchWorker; +class HTTPGetResponder; class LLTextureCache; +class LLImageDecodeThread; class LLHost; // Interface class class LLTextureFetch : public LLWorkerThread { friend class LLTextureFetchWorker; + friend class HTTPGetResponder; public: - LLTextureFetch(LLTextureCache* cache, bool threaded); + LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded); ~LLTextureFetch(); /*virtual*/ S32 update(U32 max_time_ms); - bool createRequest(const LLUUID& id, const LLHost& host, F32 priority, - S32 w, S32 h, S32 c, S32 discard, bool needs_aux); - bool createRequest(const std::string& filename, const LLUUID& id, const LLHost& host, F32 priority, + bool createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 discard, bool needs_aux); void deleteRequest(const LLUUID& id, bool cancel); bool getRequestFinished(const LLUUID& id, S32& discard_level, @@ -66,24 +69,38 @@ public: bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); + void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } + F32 getTextureBandwidth() { return mTextureBandwidth; } + // Debug S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p); void dump(); S32 getNumRequests() { return mRequestMap.size(); } + S32 getNumHTTPRequests() { return mHTTPTextureQueue.size(); } // Public for access by callbacks void lockQueue() { mQueueMutex.lock(); } void unlockQueue() { mQueueMutex.unlock(); } LLTextureFetchWorker* getWorker(const LLUUID& id); + + LLTextureInfo* getTextureInfo() { return &mTextureInfo; } protected: void addToNetworkQueue(LLTextureFetchWorker* worker); - void removeFromNetworkQueue(LLTextureFetchWorker* worker); + void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); + void addToHTTPQueue(const LLUUID& id); + void removeFromHTTPQueue(const LLUUID& id); + S32 getHTTPQueueSize() { return (S32)mHTTPTextureQueue.size(); } void removeRequest(LLTextureFetchWorker* worker, bool cancel); + // Called from worker thread (during doWork) + void processCurlRequests(); private: void sendRequestListToSimulators(); + /*virtual*/ void startThread(void); + /*virtual*/ void endThread(void); + /*virtual*/ void threadedUpdate(void); public: LLUUID mDebugID; @@ -94,8 +111,11 @@ public: private: LLMutex mQueueMutex; + LLMutex mNetworkQueueMutex; LLTextureCache* mTextureCache; + LLImageDecodeThread* mImageDecodeThread; + LLCurlRequest* mCurlGetRequest; // Map of all requests by UUID typedef std::map map_t; @@ -104,10 +124,13 @@ private: // Set of requests that require network data typedef std::set queue_t; queue_t mNetworkQueue; + queue_t mHTTPTextureQueue; typedef std::map > cancel_queue_t; cancel_queue_t mCancelQueue; - - LLFrameTimer mNetworkTimer; + F32 mTextureBandwidth; + F32 mMaxBandwidth; + LLTextureInfo mTextureInfo; }; #endif // LL_LLTEXTUREFETCH_H + diff --git a/linden/indra/newview/lltextureinfo.cpp b/linden/indra/newview/lltextureinfo.cpp new file mode 100644 index 0000000..672a36a --- /dev/null +++ b/linden/indra/newview/lltextureinfo.cpp @@ -0,0 +1,290 @@ +/** + * @file lltextureinfo.cpp + * @brief Object which handles local texture info + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lltextureinfo.h" +#include "lltexturestats.h" +#include "llviewercontrol.h" + +LLTextureInfo::LLTextureInfo() : + mLogTextureDownloadsToViewerLog(false), + mLogTextureDownloadsToSimulator(false), + mTotalBytes(0), + mTotalMilliseconds(0), + mTextureDownloadsStarted(0), + mTextureDownloadsCompleted(0), + mTextureDownloadProtocol("NONE"), + mTextureLogThreshold(100 * 1024), + mCurrentStatsBundleStartTime(0) +{ + mTextures.clear(); +} + +void LLTextureInfo::setUpLogging(bool writeToViewerLog, bool sendToSim, U32 textureLogThreshold) +{ + mLogTextureDownloadsToViewerLog = writeToViewerLog; + mLogTextureDownloadsToSimulator = sendToSim; + mTextureLogThreshold = textureLogThreshold; +} + +LLTextureInfo::~LLTextureInfo() +{ + std::map::iterator iterator; + for (iterator = mTextures.begin(); iterator != mTextures.end(); iterator++) + { + LLTextureInfoDetails *info = (*iterator).second; + delete info; + } + + mTextures.clear(); +} + +void LLTextureInfo::addRequest(const LLUUID& id) +{ + LLTextureInfoDetails *info = new LLTextureInfoDetails(); + mTextures[id] = info; +} + +U32 LLTextureInfo::getTextureInfoMapSize() +{ + return mTextures.size(); +} + +bool LLTextureInfo::has(const LLUUID& id) +{ + std::map::iterator iterator = mTextures.find(id); + if (iterator == mTextures.end()) + { + return false; + } + else + { + return true; + } +} + +void LLTextureInfo::setRequestStartTime(const LLUUID& id, U64 startTime) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mStartTime = startTime; + mTextureDownloadsStarted++; +} + +void LLTextureInfo::setRequestSize(const LLUUID& id, U32 size) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mSize = size; +} + +void LLTextureInfo::setRequestOffset(const LLUUID& id, U32 offset) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mOffset = offset; +} + +void LLTextureInfo::setRequestType(const LLUUID& id, LLTextureInfoDetails::LLRequestType type) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mType = type; +} + +void LLTextureInfo::setRequestCompleteTimeAndLog(const LLUUID& id, U64 completeTime) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mCompleteTime = completeTime; + + std::string protocol = "NONE"; + switch(mTextures[id]->mType) + { + case LLTextureInfoDetails::REQUEST_TYPE_HTTP: + protocol = "HTTP"; + break; + + case LLTextureInfoDetails::REQUEST_TYPE_UDP: + protocol = "UDP"; + break; + + case LLTextureInfoDetails::REQUEST_TYPE_NONE: + default: + break; + } + + if (mLogTextureDownloadsToViewerLog) + { + llinfos << "texture=" << id + << " start=" << mTextures[id]->mStartTime + << " end=" << mTextures[id]->mCompleteTime + << " size=" << mTextures[id]->mSize + << " offset=" << mTextures[id]->mOffset + << " length_in_ms=" << (mTextures[id]->mCompleteTime - mTextures[id]->mStartTime) / 1000 + << " protocol=" << protocol + << llendl; + } + + if(mLogTextureDownloadsToSimulator) + { + S32 texture_stats_upload_threshold = mTextureLogThreshold; + mTotalBytes += mTextures[id]->mSize; + mTotalMilliseconds += mTextures[id]->mCompleteTime - mTextures[id]->mStartTime; + mTextureDownloadsCompleted++; + mTextureDownloadProtocol = protocol; + if (mTotalBytes >= texture_stats_upload_threshold) + { + LLSD texture_data; + std::stringstream startTime; + startTime << mCurrentStatsBundleStartTime; + texture_data["start_time"] = startTime.str(); + std::stringstream endTime; + endTime << completeTime; + texture_data["end_time"] = endTime.str(); + texture_data["averages"] = getAverages(); + send_texture_stats_to_sim(texture_data); + resetTextureStatistics(); + } + } + + mTextures.erase(id); +} + +LLSD LLTextureInfo::getAverages() +{ + LLSD averagedTextureData; + S32 averageDownloadRate; + if(mTotalMilliseconds == 0) + { + averageDownloadRate = 0; + } + else + { + averageDownloadRate = (mTotalBytes * 8) / mTotalMilliseconds; + } + + averagedTextureData["bits_per_second"] = averageDownloadRate; + averagedTextureData["bytes_downloaded"] = mTotalBytes; + averagedTextureData["texture_downloads_started"] = mTextureDownloadsStarted; + averagedTextureData["texture_downloads_completed"] = mTextureDownloadsCompleted; + averagedTextureData["transport"] = mTextureDownloadProtocol; + + return averagedTextureData; +} + +void LLTextureInfo::resetTextureStatistics() +{ + mTotalMilliseconds = 0; + mTotalBytes = 0; + mTextureDownloadsStarted = 0; + mTextureDownloadsCompleted = 0; + mTextureDownloadProtocol = "NONE"; + mCurrentStatsBundleStartTime = LLTimer::getTotalTime(); +} + +U32 LLTextureInfo::getRequestStartTime(const LLUUID& id) +{ + if (!has(id)) + { + return 0; + } + else + { + std::map::iterator iterator = mTextures.find(id); + return (*iterator).second->mStartTime; + } +} + +U32 LLTextureInfo::getRequestSize(const LLUUID& id) +{ + if (!has(id)) + { + return 0; + } + else + { + std::map::iterator iterator = mTextures.find(id); + return (*iterator).second->mSize; + } +} + +U32 LLTextureInfo::getRequestOffset(const LLUUID& id) +{ + if (!has(id)) + { + return 0; + } + else + { + std::map::iterator iterator = mTextures.find(id); + return (*iterator).second->mOffset; + } +} + +LLTextureInfoDetails::LLRequestType LLTextureInfo::getRequestType(const LLUUID& id) +{ + if (!has(id)) + { + return LLTextureInfoDetails::REQUEST_TYPE_NONE; + } + else + { + std::map::iterator iterator = mTextures.find(id); + return (*iterator).second->mType; + } +} + +U32 LLTextureInfo::getRequestCompleteTime(const LLUUID& id) +{ + if (!has(id)) + { + return 0; + } + else + { + std::map::iterator iterator = mTextures.find(id); + return (*iterator).second->mCompleteTime; + } +} + diff --git a/linden/indra/newview/lltextureinfo.h b/linden/indra/newview/lltextureinfo.h new file mode 100644 index 0000000..71b0ea4 --- /dev/null +++ b/linden/indra/newview/lltextureinfo.h @@ -0,0 +1,80 @@ +/** + * @file lltextureinfo.h + * @brief Object for managing texture information. + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTUREINFO_H +#define LL_LLTEXTUREINFO_H + +#include "lluuid.h" +#include "lltextureinfodetails.h" +#include + +class LLTextureInfo +{ +public: + LLTextureInfo(); + ~LLTextureInfo(); + + void setUpLogging(bool writeToViewerLog, bool sendToSim, U32 textureLogThreshold); + bool has(const LLUUID& id); + void setRequestStartTime(const LLUUID& id, U64 startTime); + void setRequestSize(const LLUUID& id, U32 size); + void setRequestOffset(const LLUUID& id, U32 offset); + void setRequestType(const LLUUID& id, LLTextureInfoDetails::LLRequestType type); + void setRequestCompleteTimeAndLog(const LLUUID& id, U64 completeTime); + U32 getRequestStartTime(const LLUUID& id); + U32 getRequestSize(const LLUUID& id); + U32 getRequestOffset(const LLUUID& id); + LLTextureInfoDetails::LLRequestType getRequestType(const LLUUID& id); + U32 getRequestCompleteTime(const LLUUID& id); + void resetTextureStatistics(); + U32 getTextureInfoMapSize(); + LLSD getAverages(); + +private: + void addRequest(const LLUUID& id); + + std::map mTextures; + + LLSD mAverages; + + bool mLogTextureDownloadsToViewerLog; + bool mLogTextureDownloadsToSimulator; + S32 mTotalBytes; + S32 mTotalMilliseconds; + S32 mTextureDownloadsStarted; + S32 mTextureDownloadsCompleted; + std::string mTextureDownloadProtocol; + U32 mTextureLogThreshold; // in bytes + U64 mCurrentStatsBundleStartTime; +}; + +#endif // LL_LLTEXTUREINFO_H diff --git a/linden/indra/newview/lltextureinfodetails.cpp b/linden/indra/newview/lltextureinfodetails.cpp new file mode 100644 index 0000000..f6ef47a --- /dev/null +++ b/linden/indra/newview/lltextureinfodetails.cpp @@ -0,0 +1,40 @@ +/** + * @file lltextureinfodetails.cpp + * @brief Object which handles details of any individual texture + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lltextureinfodetails.h" + +LLTextureInfoDetails::LLTextureInfoDetails() : mStartTime(0), mCompleteTime(0), mSize(0), mType(REQUEST_TYPE_NONE), mOffset(0) +{ +} + diff --git a/linden/indra/newview/lltextureinfodetails.h b/linden/indra/newview/lltextureinfodetails.h new file mode 100644 index 0000000..091fa01 --- /dev/null +++ b/linden/indra/newview/lltextureinfodetails.h @@ -0,0 +1,58 @@ +/** + * @file lltextureinfo.h + * @brief Object for managing texture information. + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTUREINFODETAILS_H +#define LL_LLTEXTUREINFODETAILS_H + +#include "lluuid.h" + +class LLTextureInfoDetails +{ +public: + enum LLRequestType + { + REQUEST_TYPE_NONE, + REQUEST_TYPE_HTTP, + REQUEST_TYPE_UDP + }; + + U32 mStartTime; + U32 mCompleteTime; + U32 mOffset; + U32 mSize; + LLRequestType mType; + + LLTextureInfoDetails(); +}; + +#endif // LL_LLTEXTUREINFODETAILS_H + diff --git a/linden/indra/newview/lltexturestats.cpp b/linden/indra/newview/lltexturestats.cpp new file mode 100644 index 0000000..c91bfd4 --- /dev/null +++ b/linden/indra/newview/lltexturestats.cpp @@ -0,0 +1,61 @@ +/** + * @file lltexturerstats.cpp + * @brief texture stats helper methods + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "pipeline.h" +#include "llagent.h" +#include "lltexturefetch.h" +#include "lltexturestats.h" +#include "lltexturestatsuploader.h" +#include "llviewerregion.h" + +void send_texture_stats_to_sim(const LLSD &texture_stats) +{ + LLSD texture_stats_report; + // Only send stats if the agent is connected to a region. + if (!gAgent.getRegion() || gNoRender) + { + return; + } + + LLUUID agent_id = gAgent.getID(); + texture_stats_report["agent_id"] = agent_id; + texture_stats_report["region_id"] = gAgent.getRegion()->getRegionID(); + texture_stats_report["stats_data"] = texture_stats; + + std::string texture_cap_url = gAgent.getRegion()->getCapability("TextureStats"); + LLTextureStatsUploader tsu; + llinfos << "uploading texture stats data to simulator" << llendl; + tsu.uploadStatsToSimulator(texture_cap_url, texture_stats); +} + diff --git a/linden/indra/newview/lltexturestats.h b/linden/indra/newview/lltexturestats.h new file mode 100644 index 0000000..2deb377 --- /dev/null +++ b/linden/indra/newview/lltexturestats.h @@ -0,0 +1,41 @@ +/** + * @file lltexturestats.h + * @brief texture stats utilities + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTURESTATS_H +#define LL_LLTEXTURESTATS_H + +#include "llappviewer.h" + +// utility functions to capture data on texture download speeds and send to simulator periodically +void send_texture_stats_to_sim(const LLSD &texture_stats); + +#endif // LL_LLTEXTURESTATS_H diff --git a/linden/indra/newview/lltexturestatsuploader.cpp b/linden/indra/newview/lltexturestatsuploader.cpp new file mode 100644 index 0000000..e0358e1 --- /dev/null +++ b/linden/indra/newview/lltexturestatsuploader.cpp @@ -0,0 +1,59 @@ +/** + * @file lltexturerstats.cpp + * @brief texture stats upload class + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lltexturestatsuploader.h" + +LLTextureStatsUploader::LLTextureStatsUploader() +{ +} + +LLTextureStatsUploader::~LLTextureStatsUploader() +{ +} + +void LLTextureStatsUploader::uploadStatsToSimulator(const std::string texture_cap_url, const LLSD &texture_stats) +{ + if ( texture_cap_url != "" ) + { + LLHTTPClient::post(texture_cap_url, texture_stats, NULL); + } + else + { + llinfos << "Not sending texture stats: " + << texture_stats + << " as there is no cap url." + << llendl; + } +} + diff --git a/linden/indra/newview/lltexturestatsuploader.h b/linden/indra/newview/lltexturestatsuploader.h new file mode 100644 index 0000000..f6cc8be --- /dev/null +++ b/linden/indra/newview/lltexturestatsuploader.h @@ -0,0 +1,48 @@ +/** + * @file lltexturestatsuploader.h + * @brief Class to send the texture stats to the simulatore + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTURESTATSUPLOADER_H +#define LL_LLTEXTURESTATSUPLOADER_H + +#include "llappviewer.h" + +// utility functions to capture data on texture download speeds and send to simulator periodically + +class LLTextureStatsUploader +{ +public: + LLTextureStatsUploader(); + ~LLTextureStatsUploader(); + void uploadStatsToSimulator(const std::string texture_cap_url, const LLSD &texture_stats); +}; + +#endif // LL_LLTEXTURESTATSUPLOADER_H diff --git a/linden/indra/newview/lltextureview.cpp b/linden/indra/newview/lltextureview.cpp index d356bf9..b6159da 100644 --- a/linden/indra/newview/lltextureview.cpp +++ b/linden/indra/newview/lltextureview.cpp @@ -43,16 +43,17 @@ #include "llimageworker.h" #include "llrender.h" +#include "llappviewer.h" #include "llhoverview.h" #include "llselectmgr.h" #include "lltexlayer.h" #include "lltexturecache.h" #include "lltexturefetch.h" +#include "llviewercontrol.h" #include "llviewerobject.h" #include "llviewerimage.h" #include "llviewerimagelist.h" -#include "llappviewer.h" - +#include "llvovolume.h" extern F32 texmem_lower_bound_scale; LLTextureView *gTextureView = NULL; @@ -466,9 +467,9 @@ void LLGLTexMemBar::draw() LLAppViewer::getTextureFetch()->mPacketCount, LLAppViewer::getTextureFetch()->mBadPacketCount, LLAppViewer::getTextureCache()->getNumReads(), LLAppViewer::getTextureCache()->getNumWrites(), LLLFSThread::sLocal->getPending(), - LLImageWorker::sCount, LLImageWorker::getWorkerThread()->getNumDeletes(), - LLImageRaw::sRawImageCount, LLViewerImage::sRawCount, LLViewerImage::sAuxCount, - gImageList.mCallbackList.size()); + LLAppViewer::getImageDecodeThread()->getPending(), + LLImageRaw::sRawImageCount, + LLAppViewer::getTextureFetch()->getNumHTTPRequests()); LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, line_height*2, text_color, LLFontGL::LEFT, LLFontGL::TOP); diff --git a/linden/indra/newview/llviewercamera.cpp b/linden/indra/newview/llviewercamera.cpp index 72d1494..dade65f 100644 --- a/linden/indra/newview/llviewercamera.cpp +++ b/linden/indra/newview/llviewercamera.cpp @@ -102,10 +102,13 @@ LLViewerCamera::LLViewerCamera() : LLCamera() { calcProjection(getFar()); mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW; + mCosHalfCameraFOV = cosf(mCameraFOVDefault * 0.5f); mPixelMeterRatio = 0.f; mScreenPixelArea = 0; mZoomFactor = 1.f; mZoomSubregion = 1; + mAverageSpeed = 0.f; + mAverageAngularSpeed = 0.f; } void LLViewerCamera::updateCameraLocation(const LLVector3 ¢er, @@ -144,15 +147,22 @@ void LLViewerCamera::updateCameraLocation(const LLVector3 ¢er, setOriginAndLookAt(origin, up_direction, point_of_interest); - F32 dpos = (center - last_position).magVec(); + mVelocityDir = center - last_position ; + F32 dpos = mVelocityDir.normVec() ; LLQuaternion rotation; rotation.shortestArc(last_axis, getAtAxis()); F32 x, y, z; F32 drot; rotation.getAngleAxis(&drot, &x, &y, &z); + mVelocityStat.addValue(dpos); mAngularVelocityStat.addValue(drot); + + mAverageSpeed = mVelocityStat.getMeanPerSec() ; + mAverageAngularSpeed = mAngularVelocityStat.getMeanPerSec() ; + mCosHalfCameraFOV = cosf(0.5f * getView() * llmax(1.0f, getAspect())); + // update pixel meter ratio using default fov, not modified one mPixelMeterRatio = getViewHeightInPixels()/ (2.f*tanf(mCameraFOVDefault*0.5)); // update screen pixel area @@ -759,8 +769,8 @@ BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts) BOOL in_frustum = pointInFrustum(LLVector3(vec)) > 0; - if ( !in_frustum && all_verts || - in_frustum && !all_verts) + if (( !in_frustum && all_verts) || + (in_frustum && !all_verts)) { return !all_verts; } @@ -797,9 +807,11 @@ BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts) LLCamera::setView(vertical_fov_rads); // call base implementation } -void LLViewerCamera::setDefaultFOV(F32 vertical_fov_rads) { +void LLViewerCamera::setDefaultFOV(F32 vertical_fov_rads) +{ vertical_fov_rads = llclamp(vertical_fov_rads, getMinView(), getMaxView()); setView(vertical_fov_rads); mCameraFOVDefault = vertical_fov_rads; + mCosHalfCameraFOV = cosf(mCameraFOVDefault * 0.5f); } diff --git a/linden/indra/newview/llviewercamera.h b/linden/indra/newview/llviewercamera.h index 6a0c42b..d25d063 100644 --- a/linden/indra/newview/llviewercamera.h +++ b/linden/indra/newview/llviewercamera.h @@ -71,17 +71,20 @@ public: BOOL projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp = TRUE) const; BOOL projectPosAgentToScreenEdge(const LLVector3 &pos_agent, LLCoordGL &out_point) const; - + const LLVector3* getVelocityDir() const {return &mVelocityDir;} LLStat *getVelocityStat() { return &mVelocityStat; } LLStat *getAngularVelocityStat() { return &mAngularVelocityStat; } + F32 getCosHalfFov() {return mCosHalfCameraFOV;} + F32 getAverageSpeed() {return mAverageSpeed ;} + F32 getAverageAngularSpeed() {return mAverageAngularSpeed;} void getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right); LLVector3 roundToPixel(const LLVector3 &pos_agent); // Sets the current matrix /* virtual */ void setView(F32 vertical_fov_rads); - // Sets the current matrix AND remembers result as default view - void setDefaultFOV(F32 vertical_fov_rads); + + void setDefaultFOV(F32 fov) ; F32 getDefaultFOV() { return mCameraFOVDefault; } BOOL cameraUnderWater() const; @@ -100,9 +103,14 @@ protected: LLStat mVelocityStat; LLStat mAngularVelocityStat; + LLVector3 mVelocityDir ; + F32 mAverageSpeed ; + F32 mAverageAngularSpeed ; + mutable LLMatrix4 mProjectionMatrix; // Cache of perspective matrix mutable LLMatrix4 mModelviewMatrix; F32 mCameraFOVDefault; + F32 mCosHalfCameraFOV; LLVector3 mLastPointOfInterest; F32 mPixelMeterRatio; // Divide by distance from camera to get pixels per meter at that distance. S32 mScreenPixelArea; // Pixel area of entire window diff --git a/linden/indra/newview/llviewercontrol.cpp b/linden/indra/newview/llviewercontrol.cpp index 08e9d45..6d62db5 100644 --- a/linden/indra/newview/llviewercontrol.cpp +++ b/linden/indra/newview/llviewercontrol.cpp @@ -91,6 +91,8 @@ std::string gCurrentVersion; extern BOOL gResizeScreenTexture; extern BOOL gDebugGL; +extern BOOL gAuditTexture; + //////////////////////////////////////////////////////////////////////////// // Listeners @@ -374,6 +376,12 @@ static bool handleRenderUseImpostorsChanged(const LLSD& newvalue) return true; } +static bool handleAuditTextureChanged(const LLSD& newvalue) +{ + gAuditTexture = newvalue.asBoolean(); + return true; +} + static bool handleRenderDebugGLChanged(const LLSD& newvalue) { gDebugGL = newvalue.asBoolean(); @@ -527,6 +535,7 @@ void settings_setup_listeners() gSavedSettings.getControl("AudioLevelDoppler")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _1)); gSavedSettings.getControl("AudioLevelRolloff")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _1)); gSavedSettings.getControl("AudioStreamingMusic")->getSignal()->connect(boost::bind(&handleAudioStreamMusicChanged, _1)); + gSavedSettings.getControl("AuditTexture")->getSignal()->connect(boost::bind(&handleAuditTextureChanged, _1)); gSavedSettings.getControl("MuteAudio")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _1)); gSavedSettings.getControl("MuteMusic")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _1)); gSavedSettings.getControl("MuteMedia")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _1)); diff --git a/linden/indra/newview/llviewerimage.cpp b/linden/indra/newview/llviewerimage.cpp index 272fcb5..3a14bf0 100644 --- a/linden/indra/newview/llviewerimage.cpp +++ b/linden/indra/newview/llviewerimage.cpp @@ -59,6 +59,8 @@ #include "llviewercontrol.h" #include "pipeline.h" #include "llappviewer.h" +#include "llface.h" +#include "llviewercamera.h" /////////////////////////////////////////////////////////////////////////////// // statics @@ -72,6 +74,7 @@ S32 LLViewerImage::sImageCount = 0; S32 LLViewerImage::sRawCount = 0; S32 LLViewerImage::sAuxCount = 0; LLTimer LLViewerImage::sEvaluationTimer; +S8 LLViewerImage::sCameraMovingDiscardBias = 0 ; F32 LLViewerImage::sDesiredDiscardBias = 0.f; static F32 sDesiredDiscardBiasMin = -2.0f; // -max number of levels to improve image quality by static F32 sDesiredDiscardBiasMax = 1.5f; // max number of levels to reduce image quality by @@ -83,16 +86,26 @@ S32 LLViewerImage::sMaxTotalTextureMemInMegaBytes = 0; S32 LLViewerImage::sMaxDesiredTextureMemInBytes = 0 ; BOOL LLViewerImage::sDontLoadVolumeTextures = FALSE; +S32 LLViewerImage::sMaxSculptRez = 128 ; //max sculpt image size +const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64 ; +const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerImage::sMaxSculptRez * LLViewerImage::sMaxSculptRez ; +const S32 MAX_CACHED_RAW_TERRAIN_IMAGE_AREA = 128 * 128 ; +S32 LLViewerImage::sMinLargeImageSize = 65536 ; //256 * 256. +S32 LLViewerImage::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA ; +BOOL LLViewerImage::sFreezeImageScalingDown = FALSE ; +//debug use +S32 LLViewerImage::sLLViewerImageCount = 0 ; + // static void LLViewerImage::initClass() -{ +{ sNullImagep = new LLImageGL(1,1,3,TRUE); LLPointer raw = new LLImageRaw(1,1,3); raw->clear(0x77, 0x77, 0x77, 0xFF); - sNullImagep->createGLTexture(0, raw); + sNullImagep->createGLTexture(0, raw, 0, TRUE, LLViewerImageBoostLevel::OTHER); #if 1 - LLPointer imagep = new LLViewerImage(IMG_DEFAULT, TRUE); + LLPointer imagep = new LLViewerImage(IMG_DEFAULT); sDefaultImagep = imagep; const S32 dim = 128; LLPointer image_raw = new LLImageRaw(dim,dim,3); @@ -118,7 +131,7 @@ void LLViewerImage::initClass() } } } - imagep->createGLTexture(0, image_raw); + imagep->createGLTexture(0, image_raw, 0, TRUE, LLViewerImageBoostLevel::OTHER); image_raw = NULL; gImageList.addImage(imagep); imagep->dontDiscard(); @@ -128,17 +141,48 @@ void LLViewerImage::initClass() sSmokeImagep = gImageList.getImage(IMG_SMOKE, TRUE, TRUE); sSmokeImagep->setNoDelete() ; + if(gAuditTexture) + { + sDefaultTexturep = new LLImageGL() ; + image_raw = new LLImageRaw(dim,dim,3); + data = image_raw->getData(); + for (S32 i = 0; i=(dim-border) || j>=(dim-border)) + { + *data++ = 0xff; + *data++ = 0xff; + *data++ = 0xff; + } + else + { + *data++ = 0xff; + *data++ = 0xff; + *data++ = 0x00; + } + } + } + sDefaultTexturep->createGLTexture(0, image_raw, 0, TRUE, LLViewerImageBoostLevel::OTHER); + image_raw = NULL; + sDefaultTexturep->dontDiscard(); + } } // static void LLViewerImage::cleanupClass() { stop_glerror(); + LLImageGL::cleanupClass() ; + sNullImagep = NULL; sDefaultImagep = NULL; sSmokeImagep = NULL; sMissingAssetImagep = NULL; - sWhiteImagep = NULL; + sWhiteImagep = NULL; + sDefaultTexturep = NULL ; } // tuning params @@ -186,6 +230,13 @@ void LLViewerImage::updateClass(const F32 velocity, const F32 angular_velocity) } } sDesiredDiscardBias = llclamp(sDesiredDiscardBias, sDesiredDiscardBiasMin, sDesiredDiscardBiasMax); + + F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ; + F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed(); + sCameraMovingDiscardBias = (S8)llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1) ; + + LLViewerImage::sFreezeImageScalingDown = (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < 0.75f * sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) && + (BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < 0.75f * sMaxTotalTextureMemInMegaBytes * texmem_middle_bound_scale) ; } // static @@ -198,18 +249,19 @@ LLViewerImage* LLViewerImage::getImage(const LLUUID& image_id) const U32 LLViewerImage::sCurrentFileVersion = 1; -LLViewerImage::LLViewerImage(const LLUUID& id, BOOL usemipmaps) +LLViewerImage::LLViewerImage(const LLUUID& id, const LLHost& host, BOOL usemipmaps) : LLImageGL(usemipmaps), - mID(id) + mID(id), + mTargetHost(host) { init(true); sImageCount++; } -LLViewerImage::LLViewerImage(const std::string& filename, const LLUUID& id, BOOL usemipmaps) +LLViewerImage::LLViewerImage(const std::string& url, const LLUUID& id, BOOL usemipmaps) : LLImageGL(usemipmaps), mID(id), - mLocalFileName(filename) + mUrl(url) { init(true); sImageCount++; @@ -265,7 +317,7 @@ void LLViewerImage::init(bool firstinit) } mIsMediaTexture = FALSE; - mBoostLevel = LLViewerImage::BOOST_NONE; + mBoostLevel = LLViewerImageBoostLevel::BOOST_NONE; // Only set mIsMissingAsset true when we know for certain that the database // does not contain this image. @@ -277,8 +329,6 @@ void LLViewerImage::init(bool firstinit) mRawDiscardLevel = INVALID_DISCARD_LEVEL; mMinDiscardLevel = 0; - mTargetHost = LLHost::invalid; - mHasFetcher = FALSE; mIsFetching = FALSE; mFetchState = 0; @@ -287,6 +337,15 @@ void LLViewerImage::init(bool firstinit) mFetchDeltaTime = 999999.f; mDecodeFrame = 0; mVisibleFrame = 0; + mForSculpt = FALSE ; + mCachedRawImage = NULL ; + mCachedRawDiscardLevel = -1 ; + mCachedRawImageReady = FALSE ; + mNeedsResetMaxVirtualSize = FALSE ; + + mForceToSaveRawImage = FALSE ; + mSavedRawDiscardLevel = -1 ; + mDesiredSavedRawDiscardLevel = -1 ; } // virtual @@ -322,6 +381,7 @@ LLViewerImage::~LLViewerImage() void LLViewerImage::cleanup() { + mFaceList.clear() ; for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); iter != mLoadedCallbackList.end(); ) { @@ -336,7 +396,9 @@ void LLViewerImage::cleanup() // Clean up image data destroyRawImage(); - + mCachedRawImage = NULL ; + mCachedRawDiscardLevel = -1 ; + mCachedRawImageReady = FALSE ; // LLImageGL::cleanup will get called more than once when this is used in the destructor. LLImageGL::cleanup(); } @@ -365,6 +427,63 @@ void LLViewerImage::destroyTexture() destroyGLTexture() ; } +void LLViewerImage::addToCreateTexture() +{ + if(isForSculptOnly()) + { + //just update some variables, not to create a real GL texture. + createGLTexture(mRawDiscardLevel, mRawImage, 0, FALSE) ; + mNeedsCreateTexture = FALSE ; + destroyRawImage(); + } + else + { +#if 1 + // + //if mRequestedDiscardLevel > mDesiredDiscardLevel, we assume the required image res keep going up, + //so do not scale down the over qualified image. + //Note: scaling down image is expensensive. Do it only when very necessary. + // + if(mRequestedDiscardLevel <= mDesiredDiscardLevel) + { + S32 w = mFullWidth >> mRawDiscardLevel; + S32 h = mFullHeight >> mRawDiscardLevel; + + //if big image, do not load extra data + //scale it down to size >= LLViewerImage::sMinLargeImageSize + if(w * h > LLViewerImage::sMinLargeImageSize) + { + S32 d_level = llmin(mRequestedDiscardLevel, (S32)mDesiredDiscardLevel) - mRawDiscardLevel ; + + if(d_level > 0) + { + S32 i = 0 ; + while((d_level > 0) && ((w >> i) * (h >> i) > LLViewerImage::sMinLargeImageSize)) + { + i++; + d_level--; + } + if(i > 0) + { + mRawDiscardLevel += i ; + if(mRawDiscardLevel >= getDiscardLevel() && getDiscardLevel() > 0) + { + mNeedsCreateTexture = FALSE ; + destroyRawImage(); + return ; + } + mRawImage->scale(w >> i, h >> i) ; + } + } + } + } +#endif + mNeedsCreateTexture = TRUE; + gImageList.mCreateTextureList.insert(this); + } + return ; +} + // ONLY called from LLViewerImageList BOOL LLViewerImage::createTexture(S32 usename/*= 0*/) { @@ -386,7 +505,7 @@ BOOL LLViewerImage::createTexture(S32 usename/*= 0*/) if (!gNoRender) { // store original size only for locally-sourced images - if (!mLocalFileName.empty()) + if (mUrl.compare(0, 7, "file://") == 0) { mOrigWidth = mRawImage->getWidth(); mOrigHeight = mRawImage->getHeight(); @@ -457,29 +576,54 @@ BOOL LLViewerImage::createTexture(S32 usename/*= 0*/) //============================================================================ -void LLViewerImage::addTextureStats(F32 virtual_size) const // = 1.0 +void LLViewerImage::addTextureStats(F32 virtual_size, BOOL needs_gltexture) const { - if (virtual_size > mMaxVirtualSize) + if(needs_gltexture) { - mMaxVirtualSize = virtual_size; + mNeedsGLTexture = TRUE ; } -} -void LLViewerImage::resetTextureStats(BOOL zero) -{ - if (zero) + if(mNeedsResetMaxVirtualSize) { - mMaxVirtualSize = 0.0f; + //flag to reset the values because the old values are used. + mNeedsResetMaxVirtualSize = FALSE ; + mMaxVirtualSize = virtual_size; + mAdditionalDecodePriority = 0.f ; + mNeedsGLTexture = needs_gltexture ; } - else + else if (virtual_size > mMaxVirtualSize) { - mMaxVirtualSize -= mMaxVirtualSize * .10f; // decay by 5%/update - } + mMaxVirtualSize = virtual_size; + } +} + +void LLViewerImage::resetTextureStats() +{ + mMaxVirtualSize = 0.0f; + mAdditionalDecodePriority = 0.f ; + mNeedsResetMaxVirtualSize = FALSE ; +} + +BOOL LLViewerImage::isUpdateFrozen() +{ + return LLViewerImage::sFreezeImageScalingDown && !getDiscardLevel() ; +} + +BOOL LLViewerImage::isLargeImage() +{ + return mTexelsPerImage > LLViewerImage::sMinLargeImageSize ; } // This is gauranteed to get called periodically for every texture void LLViewerImage::processTextureStats() -{ +{ + //no need to update if the texture reaches its highest res and the memory is sufficient. + //if(isUpdateFrozen()) + //{ + // return ; + //} + + updateVirtualSize() ; // Generate the request priority and render priority if (mDontDiscard || !getUseMipMaps()) { @@ -487,7 +631,7 @@ void LLViewerImage::processTextureStats() if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT) mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048 } - else if (mBoostLevel < LLViewerImage::BOOST_HIGH && mMaxVirtualSize <= 10.f) + else if (mBoostLevel < LLViewerImageBoostLevel::BOOST_HIGH && mMaxVirtualSize <= 10.f) { // If the image has not been significantly visible in a while, we don't want it mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)(MAX_DISCARD_LEVEL + 1)); @@ -504,15 +648,14 @@ void LLViewerImage::processTextureStats() S32 fullwidth = llmin(mFullWidth,(S32)MAX_IMAGE_SIZE_DEFAULT); S32 fullheight = llmin(mFullHeight,(S32)MAX_IMAGE_SIZE_DEFAULT); mTexelsPerImage = (F32)fullwidth * fullheight; - F32 discard_level = 0.f; // If we know the output width and height, we can force the discard // level to the correct value, and thus not decode more texture // data than we need to. - if (mBoostLevel == LLViewerImage::BOOST_UI || - mBoostLevel == LLViewerImage::BOOST_PREVIEW || - mBoostLevel == LLViewerImage::BOOST_AVATAR_SELF) // JAMESDEBUG what about AVATAR_BAKED_SELF? + if (mBoostLevel == LLViewerImageBoostLevel::BOOST_UI || + mBoostLevel == LLViewerImageBoostLevel::BOOST_PREVIEW || + mBoostLevel == LLViewerImageBoostLevel::BOOST_AVATAR_SELF) // JAMESDEBUG what about AVATAR_BAKED_SELF? { discard_level = 0; // full res } @@ -527,6 +670,12 @@ void LLViewerImage::processTextureStats() } else { + if(isLargeImage() && !isJustBound() && mAdditionalDecodePriority < 1.0f) + { + //if is a big image and not being used recently, nor close to the view point, do not load hi-res data. + mMaxVirtualSize = llmin(mMaxVirtualSize, (F32)LLViewerImage::sMinLargeImageSize) ; + } + if ((mCalculatedDiscardLevel >= 0.f) && (llabs(mMaxVirtualSize - mDiscardVirtualSize) < mMaxVirtualSize*.20f)) { @@ -541,15 +690,14 @@ void LLViewerImage::processTextureStats() mCalculatedDiscardLevel = discard_level; } } - if (mBoostLevel < LLViewerImage::BOOST_HIGH) + if (mBoostLevel < LLViewerImageBoostLevel::BOOST_HIGH) { - static const F32 discard_bias = -.5f; // Must be < 1 or highest discard will never load! - discard_level += discard_bias; discard_level += sDesiredDiscardBias; discard_level *= sDesiredDiscardScale; // scale + + discard_level += sCameraMovingDiscardBias ; } discard_level = floorf(discard_level); -// discard_level -= (gImageList.mVideoMemorySetting>>1); // more video ram = higher detail F32 min_discard = 0.f; if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT) @@ -567,43 +715,76 @@ void LLViewerImage::processTextureStats() // if possible. Now we check to see if we have it, and take the // proper action if we don't. // - - BOOL increase_discard = FALSE; S32 current_discard = getDiscardLevel(); if ((sDesiredDiscardBias > 0.0f) && (current_discard >= 0 && mDesiredDiscardLevel >= current_discard)) { - if ( BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) > sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) + // Limit the amount of GL memory bound each frame + if ( (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) > sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) && + (!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel)) { - // Limit the amount of GL memory bound each frame - if (mDesiredDiscardLevel > current_discard) - { - increase_discard = TRUE; - } + scaleDown() ; } - if ( BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) > sMaxTotalTextureMemInMegaBytes*texmem_middle_bound_scale) + // Only allow GL to have 2x the video card memory + else if ( (BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) > sMaxTotalTextureMemInMegaBytes*texmem_middle_bound_scale) && + (!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel)) { - // Only allow GL to have 2x the video card memory - if (!getBoundRecently()) - { - increase_discard = TRUE; - } + scaleDown() ; } - if (increase_discard) + } + } +} +void LLViewerImage::updateVirtualSize() +{ +#if 1 + if(mNeedsResetMaxVirtualSize) + { + addTextureStats(0.f, FALSE) ;//reset + } + if(mFaceList.size() > 0) + { + for(std::list::iterator iter = mFaceList.begin(); iter != mFaceList.end(); ++iter) + { + LLFace* facep = *iter ; + if(facep->getDrawable()->isRecentlyVisible()) { - // llinfos << "DISCARDED: " << mID << " Discard: " << current_discard << llendl; - sBoundTextureMemoryInBytes -= mTextureMemory; - sTotalTextureMemoryInBytes -= mTextureMemory; - // Increase the discard level (reduce the texture res) - S32 new_discard = current_discard+1; - setDiscardLevel(new_discard); - sBoundTextureMemoryInBytes += mTextureMemory; - sTotalTextureMemoryInBytes += mTextureMemory; + addTextureStats(facep->getVirtualSize()) ; + setAdditionalDecodePriority(facep->getImportanceToCamera()) ; } - } + } } + mNeedsResetMaxVirtualSize = TRUE ; +#endif } +void LLViewerImage::scaleDown() +{ + if(getHasGLTexture() && mCachedRawDiscardLevel > getDiscardLevel()) + { + switchToCachedImage() ; + } +} + +//use the mCachedRawImage to (re)generate the gl texture. +void LLViewerImage::switchToCachedImage() +{ + if(mCachedRawImage.notNull()) + { + mRawImage = mCachedRawImage ; + + if (getComponents() != mRawImage->getComponents()) + { + // We've changed the number of components, so we need to move any + // objects using this pool to a different pool. + mComponents = mRawImage->getComponents(); + gImageList.dirtyImage(this); + } + mIsRawImageValid = TRUE; + mRawDiscardLevel = mCachedRawDiscardLevel ; + gImageList.mCreateTextureList.insert(this); + mNeedsCreateTexture = TRUE; + } +} //============================================================================ F32 LLViewerImage::calcDecodePriority() @@ -619,9 +800,19 @@ F32 LLViewerImage::calcDecodePriority() { return mDecodePriority; // no change while waiting to create } + if(mForceToSaveRawImage) + { + return maxDecodePriority() ; + } - F32 priority; S32 cur_discard = getDiscardLevel(); + + //no need to update if the texture reaches its highest res and the memory is sufficient. + //if(LLViewerImage::sFreezeImageScalingDown && !cur_discard) + //{ + // return -5.0f ; + //} + bool have_all_data = (cur_discard >= 0 && (cur_discard <= mDesiredDiscardLevel)); F32 pixel_priority = fsqrtf(mMaxVirtualSize); const S32 MIN_NOT_VISIBLE_FRAMES = 30; // NOTE: this function is not called every frame @@ -631,23 +822,36 @@ F32 LLViewerImage::calcDecodePriority() mVisibleFrame = mDecodeFrame; } + F32 priority = 0.f; if (mIsMissingAsset) { priority = 0.0f; + } + else if(mDesiredDiscardLevel >= cur_discard && cur_discard > -1) + { + priority = -1.0f ; + } + else if (!isJustBound() && mCachedRawImageReady) + { + priority = -1.0f; + } + else if(mCachedRawDiscardLevel > -1 && mDesiredDiscardLevel >= mCachedRawDiscardLevel) + { + priority = -1.0f; } else if (mDesiredDiscardLevel > mMaxDiscardLevel) { // Don't decode anything we don't need priority = -1.0f; } - else if (mBoostLevel == LLViewerImage::BOOST_UI && !have_all_data) + else if (mBoostLevel == LLViewerImageBoostLevel::BOOST_UI && !have_all_data) { priority = 1.f; } else if (pixel_priority <= 0.f && !have_all_data) { // Not on screen but we might want some data - if (mBoostLevel > BOOST_HIGH) + if (mBoostLevel > LLViewerImageBoostLevel::BOOST_HIGH) { // Always want high boosted images priority = 1.f; @@ -695,12 +899,16 @@ F32 LLViewerImage::calcDecodePriority() ddiscard-=2; } ddiscard = llclamp(ddiscard, 0, 4); + priority = ddiscard*100000.f; } if (priority > 0.0f) { - pixel_priority = llclamp(pixel_priority, 0.0f, priority-1.f); // priority range = 100000-900000 - if ( mBoostLevel > BOOST_HIGH) + // priority range = 100000-900000 + pixel_priority = llclamp(pixel_priority, 0.0f, priority-1.f); + + // priority range = [100000.f, 2000000.f] + if ( mBoostLevel > LLViewerImageBoostLevel::BOOST_HIGH) { priority = 1000000.f + pixel_priority + 1000.f * mBoostLevel; } @@ -708,7 +916,14 @@ F32 LLViewerImage::calcDecodePriority() { priority += 0.f + pixel_priority + 1000.f * mBoostLevel; } + + // priority range = [2100000.f, 5000000.f] if mAdditionalDecodePriority > 1.0 + if(mAdditionalDecodePriority > 1.0f) + { + priority += 2000000.f + mAdditionalDecodePriority ; + } } + return priority; } @@ -716,7 +931,7 @@ F32 LLViewerImage::calcDecodePriority() //static F32 LLViewerImage::maxDecodePriority() { - return 2000000.f; + return 6000000.f; } void LLViewerImage::setDecodePriority(F32 priority) @@ -725,15 +940,30 @@ void LLViewerImage::setDecodePriority(F32 priority) mDecodePriority = priority; } -void LLViewerImage::setBoostLevel(S32 level) +F32 LLViewerImage::maxAdditionalDecodePriority() +{ + return 2000000.f; +} +void LLViewerImage::setAdditionalDecodePriority(F32 priority) { + priority *= maxAdditionalDecodePriority(); + if(mAdditionalDecodePriority < priority) + { + mAdditionalDecodePriority = priority; + } +} +//------------------------------------------------------------ + +void LLViewerImage::setBoostLevel(S32 level) +{ mBoostLevel = level; - if (level >= LLViewerImage::BOOST_HIGH) + + if(gAuditTexture) { - processTextureStats(); + setCategory(mBoostLevel); } - if(mBoostLevel != LLViewerImage::BOOST_NONE) + if(mBoostLevel != LLViewerImageBoostLevel::BOOST_NONE) { setNoDelete() ; } @@ -785,11 +1015,20 @@ bool LLViewerImage::updateFetch() S32 desired_discard = getDesiredDiscardLevel(); F32 decode_priority = getDecodePriority(); decode_priority = llmax(decode_priority, 0.0f); + decode_priority = llmin(decode_priority, maxDecodePriority()); if (mIsFetching) { // Sets mRawDiscardLevel, mRawImage, mAuxRawImage S32 fetch_discard = current_discard; + if(mForceToSaveRawImage) + { + if(fetch_discard >= 0) + { + fetch_discard = llmax(fetch_discard, mSavedRawDiscardLevel) ; + } + } + if (mRawImage.notNull()) sRawCount--; if (mAuxRawImage.notNull()) sAuxCount--; bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage); @@ -809,6 +1048,7 @@ bool LLViewerImage::updateFetch() if (mRawImage.notNull()) { mRawDiscardLevel = fetch_discard; + if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) && (current_discard < 0 || mRawDiscardLevel < current_discard)) { @@ -819,11 +1059,25 @@ bool LLViewerImage::updateFetch() mComponents = mRawImage->getComponents(); gImageList.dirtyImage(this); } - mIsRawImageValid = TRUE; - gImageList.mCreateTextureList.insert(this); - mNeedsCreateTexture = TRUE; + mFullWidth = mRawImage->getWidth() << mRawDiscardLevel; mFullHeight = mRawImage->getHeight() << mRawDiscardLevel; + + if(mFullWidth > MAX_IMAGE_SIZE || mFullHeight > MAX_IMAGE_SIZE) + { + //discard all oversized textures. + destroyRawImage(); + setIsMissingAsset(); + mRawDiscardLevel = INVALID_DISCARD_LEVEL ; + mIsFetching = FALSE ; + } + else + { + mIsRawImageValid = TRUE; + addToCreateTexture() ; + } + + return TRUE ; } else { @@ -846,7 +1100,7 @@ bool LLViewerImage::updateFetch() } else { - llwarns << mID << ": Setting min discard to " << current_discard << llendl; + //llwarns << mID << ": Setting min discard to " << current_discard << llendl; mMinDiscardLevel = current_discard; desired_discard = current_discard; } @@ -865,8 +1119,19 @@ bool LLViewerImage::updateFetch() } } - bool make_request = true; - + if (!mDontDiscard) + { + if (mBoostLevel == 0) + { + desired_discard = llmax(desired_discard, current_discard-1); + } + else + { + desired_discard = llmax(desired_discard, current_discard-2); + } + } + + bool make_request = true; if (decode_priority <= 0) { make_request = false; @@ -879,6 +1144,10 @@ bool LLViewerImage::updateFetch() { make_request = false; } + else if (!isJustBound() && mCachedRawImageReady) + { + make_request = false; + } else { if (mIsFetching) @@ -906,41 +1175,21 @@ bool LLViewerImage::updateFetch() h = getHeight(0); c = getComponents(); } - if (!mDontDiscard) - { - if (mBoostLevel == 0) - { - desired_discard = llmax(desired_discard, current_discard-1); - } - else - { - desired_discard = llmax(desired_discard, current_discard-2); - } - } - + // bypass texturefetch directly by pulling from LLTextureCache bool fetch_request_created = false; - if (mLocalFileName.empty()) - { - fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(getID(), getTargetHost(), decode_priority, - w, h, c, desired_discard, - needsAux()); - } - else - { - fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mLocalFileName, getID(),getTargetHost(), decode_priority, - w, h, c, desired_discard, - needsAux()); - } + fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mUrl, getID(),getTargetHost(), decode_priority, + w, h, c, desired_discard, needsAux()); if (fetch_request_created) - { + { mHasFetcher = TRUE; mIsFetching = TRUE; mRequestedDiscardLevel = desired_discard; + mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, - mFetchPriority, mFetchDeltaTime, mRequestDeltaTime); - } + mFetchPriority, mFetchDeltaTime, mRequestDeltaTime); + } // if createRequest() failed, we're finishing up a request for this UUID, // wait for it to complete @@ -962,9 +1211,81 @@ bool LLViewerImage::updateFetch() return mIsFetching ? true : false; } +// +//force to fetch a new raw image for this texture +//this function is to replace readBackRaw(). +// +BOOL LLViewerImage::forceFetch() +{ + if(!mForceToSaveRawImage) + { + return false ; + } + if (mIsMediaTexture) + { + mForceToSaveRawImage = false ; + llassert_always(!mHasFetcher); + return false; // skip + } + if (mIsMissingAsset) + { + mForceToSaveRawImage = false ; + llassert_always(!mHasFetcher); + return false; // skip + } + if (!mLoadedCallbackList.empty() && mRawImage.notNull()) + { + return false; // process any raw image data in callbacks before replacing + } + if(mRawImage.notNull() && mRawDiscardLevel <= mDesiredSavedRawDiscardLevel) + { + return false ; // mRawImage is enough + } + if(mIsFetching) + { + return false ; + } + + S32 desired_discard = mDesiredSavedRawDiscardLevel ; + S32 current_discard = getDiscardLevel(); + + bool fetch_request_created = false; + S32 w=0, h=0, c=0; + if (current_discard >= 0) + { + w = getWidth(0); + h = getHeight(0); + c = getComponents(); + } + fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mUrl, getID(),getTargetHost(), maxDecodePriority(), + w, h, c, desired_discard, needsAux()); + + if (fetch_request_created) + { + mHasFetcher = TRUE; + mIsFetching = TRUE; + // Set the image's decode priority to maxDecodePriority() too, or updateFetch() will set + // the request priority to 0 and terminate the fetch before we even started (SNOW-203). + gImageList.bumpToMaxDecodePriority(this); + mRequestedDiscardLevel = desired_discard ; + + mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, + mFetchPriority, mFetchDeltaTime, mRequestDeltaTime); + } + + return mIsFetching ? true : false; +} + void LLViewerImage::setIsMissingAsset() { - llwarns << mLocalFileName << " " << mID << ": Marking image as missing" << llendl; + if (mUrl.empty()) + { + llwarns << mID << ": Marking image as missing" << llendl; + } + else + { + llwarns << mUrl << ": Marking image as missing" << llendl; + } if (mHasFetcher) { LLAppViewer::getTextureFetch()->deleteRequest(getID(), true); @@ -1252,7 +1573,7 @@ bool LLViewerImage::bindError(S32 stage) const return res; } -bool LLViewerImage::bindDefaultImage(S32 stage) const +bool LLViewerImage::bindDefaultImage(S32 stage) { if (stage < 0) return false; @@ -1271,24 +1592,17 @@ bool LLViewerImage::bindDefaultImage(S32 stage) const llwarns << "LLViewerImage::bindError failed." << llendl; } stop_glerror(); + + //check if there is cached raw image and switch to it if possible + switchToCachedImage() ; + return res; } //virtual void LLViewerImage::forceImmediateUpdate() { - //only immediately update a deleted texture which is now being re-used. - if(!isDeleted()) - { - return ; - } - //if already called forceImmediateUpdate() - if(mInImageList && mDecodePriority == LLViewerImage::maxDecodePriority()) - { - return ; - } - - gImageList.forceImmediateUpdate(this) ; + gImageList.bumpToMaxDecodePriority(this) ; return ; } @@ -1302,21 +1616,165 @@ LLImageRaw* LLViewerImage::readBackRawImage(S8 discard_level) llerrs << "called with existing mRawImage" << llendl; mRawImage = NULL; } - mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), mComponents); - sRawCount++; - mRawDiscardLevel = discard_level; - readBackRaw(mRawDiscardLevel, mRawImage, false); - mIsRawImageValid = TRUE; + if(mSavedRawDiscardLevel >= 0 && mSavedRawDiscardLevel <= discard_level) + { + mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents()) ; + mRawImage->copy(mSavedRawImage) ; + mRawDiscardLevel = discard_level ; + } + else + { + mRawImage = mCachedRawImage ; + mRawDiscardLevel = mCachedRawDiscardLevel; + } + + sRawCount++; + mIsRawImageValid = TRUE; + return mRawImage; } +void LLViewerImage::saveRawImage() +{ + if(mRawImage.isNull() || mRawDiscardLevel > mDesiredSavedRawDiscardLevel) + { + forceFetch() ; + } + + if(mRawImage.isNull() || mSavedRawDiscardLevel == mRawDiscardLevel) + { + return ; + } + + mSavedRawDiscardLevel = mRawDiscardLevel ; + mSavedRawImage = new LLImageRaw(mRawImage->getData(), mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents()) ; + + if(mSavedRawDiscardLevel <= mDesiredSavedRawDiscardLevel) + { + mForceToSaveRawImage = FALSE ; + } +} + +void LLViewerImage::forceToSaveRawImage(S32 desired_discard) +{ + mForceToSaveRawImage = TRUE ; + mDesiredSavedRawDiscardLevel = desired_discard ; + + forceFetch() ; +} +void LLViewerImage::destroySavedRawImage() +{ + mSavedRawImage = NULL ; + mForceToSaveRawImage = FALSE ; + mSavedRawDiscardLevel = -1 ; + mDesiredSavedRawDiscardLevel = -1 ; +} + void LLViewerImage::destroyRawImage() { if (mRawImage.notNull()) sRawCount--; if (mAuxRawImage.notNull()) sAuxCount--; + + if(mForceToSaveRawImage) + { + saveRawImage() ; + } + + setCachedRawImage() ; + mRawImage = NULL; mAuxRawImage = NULL; mIsRawImageValid = FALSE; mRawDiscardLevel = INVALID_DISCARD_LEVEL; } + +void LLViewerImage::setCachedRawImage() +{ + if(mRawImage == mCachedRawImage) + { + return ; + } + if(!mIsRawImageValid) + { + return ; + } + + if(mCachedRawImageReady) + { + return ; + } + + if(mCachedRawDiscardLevel < 0 || mCachedRawDiscardLevel > mRawDiscardLevel) + { + S32 i = 0 ; + S32 w = mRawImage->getWidth() ; + S32 h = mRawImage->getHeight() ; + + S32 max_size = MAX_CACHED_RAW_IMAGE_AREA ; + if(LLViewerImageBoostLevel::BOOST_TERRAIN == mBoostLevel) + { + max_size = MAX_CACHED_RAW_TERRAIN_IMAGE_AREA ; + } + if(mForSculpt) + { + max_size = MAX_CACHED_RAW_SCULPT_IMAGE_AREA ; + } + + while(((w >> i) * (h >> i)) > max_size) + { + ++i ; + } + mCachedRawImageReady = (!mRawDiscardLevel || ((w * h) >= max_size)) ; + + if(i) + { + if(!(w >> i) || !(h >> i)) + { + --i ; + } + mRawImage->scale(w >> i, h >> i) ; + } + mCachedRawImage = mRawImage ; + mCachedRawDiscardLevel = mRawDiscardLevel + i ; + } +} + +void LLViewerImage::checkCachedRawSculptImage() +{ + if(mCachedRawImageReady && mCachedRawDiscardLevel > 0) + { + if(mCachedRawImage->getWidth() * mCachedRawImage->getHeight() < MAX_CACHED_RAW_SCULPT_IMAGE_AREA) + { + mCachedRawImageReady = FALSE ; + } + else if(isForSculptOnly()) + { + resetTextureStats() ; //do not update this image any more. + } + } +} + +BOOL LLViewerImage::isForSculptOnly() const +{ + return mForSculpt && !mNeedsGLTexture ; +} + +void LLViewerImage::setForSculpt() +{ + mForSculpt = TRUE ; + if(isForSculptOnly() && !getBoundRecently()) + { + destroyGLTexture() ; //sculpt image does not need gl texture. + } + checkCachedRawSculptImage() ; +} + +void LLViewerImage::addFace(LLFace* facep) +{ + mFaceList.push_back(facep) ; +} +void LLViewerImage::removeFace(LLFace* facep) +{ + mFaceList.remove(facep) ; +} diff --git a/linden/indra/newview/llviewerimage.h b/linden/indra/newview/llviewerimage.h index 980ff56..fdf8ff0 100644 --- a/linden/indra/newview/llviewerimage.h +++ b/linden/indra/newview/llviewerimage.h @@ -41,6 +41,7 @@ #include #include +class LLFace; #define MIN_VIDEO_RAM_IN_MEGA_BYTES 32 #define MAX_VIDEO_RAM_IN_MEGA_BYTES 512 // 512MB max for performance reasons. @@ -75,6 +76,43 @@ public: class LLTextureBar; +//===================================== +struct LLViewerImageBoostLevel +{ + enum + { + BOOST_NONE = 0, + BOOST_AVATAR_BAKED = 1, + BOOST_AVATAR = 2, + BOOST_CLOUDS = 3, + BOOST_SCULPTED = 4, + + BOOST_HIGH = 5, + BOOST_TERRAIN , // has to be high priority for minimap / low detail + BOOST_SELECTED , + BOOST_HUD , + BOOST_AVATAR_BAKED_SELF , + BOOST_UI , + BOOST_PREVIEW , + BOOST_MAP , + BOOST_MAP_VISIBLE , + BOOST_AVATAR_SELF ,// needed for baking avatar + BOOST_MAX_LEVEL, + + //LLImageGLCategory + TEXLAYER_BUMP = BOOST_MAX_LEVEL, + AVATAR_SCRATCH_TEX, + FONT, + BUMP_IMAGE, + DYNAMIC_TEX, + TEXLAYER_CACHE, + MEDIA, + ATLAS, + OTHER, + MAX_GL_IMAGE_CATEGORY + }; +}; +//===================================== class LLViewerImage : public LLImageGL { LOG_CLASS(LLViewerImage); @@ -175,15 +213,15 @@ protected: /*virtual*/ ~LLViewerImage(); public: - LLViewerImage(const std::string& filename, const LLUUID& id, BOOL usemipmaps = TRUE); - LLViewerImage(const LLUUID& id, BOOL usemipmaps = TRUE); + LLViewerImage(const std::string& url, const LLUUID& id, BOOL usemipmaps = TRUE); + LLViewerImage(const LLUUID& id, const LLHost& host = LLHost::invalid, BOOL usemipmaps = TRUE); LLViewerImage(const U32 width, const U32 height, const U8 components, BOOL usemipmaps); LLViewerImage(const LLImageRaw* raw, BOOL usemipmaps); /*virtual*/ void dump(); // debug info to llinfos /*virtual*/ bool bindError(const S32 stage = 0) const; - /*virtual*/ bool bindDefaultImage(const S32 stage = 0) const; + /*virtual*/ bool bindDefaultImage(const S32 stage = 0) ; /*virtual*/ void forceImmediateUpdate() ; void reinit(BOOL usemipmaps = TRUE); @@ -192,8 +230,13 @@ public: // New methods for determining image quality/priority // texel_area_ratio is ("scaled" texel area)/(original texel area), approximately. - void addTextureStats(F32 virtual_size) const; - void resetTextureStats(BOOL zero = FALSE); + void addTextureStats(F32 virtual_size, BOOL needs_gltexture = TRUE) const; + void resetTextureStats(); + void setAdditionalDecodePriority(F32 priority) ; + F32 maxAdditionalDecodePriority() ; + + BOOL isLargeImage() ; + BOOL isUpdateFrozen() ; // Process image stats to determine priority/quality requirements. void processTextureStats(); @@ -207,6 +250,7 @@ public: // ONLY call from LLViewerImageList BOOL createTexture(S32 usename = 0); void destroyTexture() ; + void addToCreateTexture(); BOOL needsAux() const { return mNeedsAux; } @@ -217,32 +261,12 @@ public: void setMinDiscardLevel(S32 discard) { mMinDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel,(S8)discard); } // Host we think might have this image, used for baked av textures. - void setTargetHost(LLHost host) { mTargetHost = host; } LLHost getTargetHost() const { return mTargetHost; } - enum - { - BOOST_NONE = 0, - BOOST_AVATAR_BAKED = 1, - BOOST_AVATAR = 2, - BOOST_CLOUDS = 3, - BOOST_SCULPTED = 4, - - BOOST_HIGH = 10, - BOOST_TERRAIN = 11, // has to be high priority for minimap / low detail - BOOST_SELECTED = 12, - BOOST_HUD = 13, - BOOST_AVATAR_BAKED_SELF = 14, - BOOST_UI = 15, - BOOST_PREVIEW = 16, - BOOST_MAP = 17, - BOOST_MAP_LAYER = 18, - BOOST_AVATAR_SELF = 19, // needed for baking avatar - BOOST_MAX_LEVEL - }; void setBoostLevel(S32 level); S32 getBoostLevel() { return mBoostLevel; } + void updateVirtualSize() ; F32 getDecodePriority() const { return mDecodePriority; }; F32 calcDecodePriority(); static F32 maxDecodePriority(); @@ -271,6 +295,24 @@ public: S32 getOriginalWidth() { return mOrigWidth; } S32 getOriginalHeight() { return mOrigHeight; } + BOOL isForSculptOnly() const ; + void setForSculpt(); + + void checkCachedRawSculptImage() ; + LLImageRaw* getRawImage()const { return mRawImage ;} + S32 getRawImageLevel() const {return mRawDiscardLevel;} + LLImageRaw* getCachedRawImage() const { return mCachedRawImage ;} + S32 getCachedRawImageLevel() const {return mCachedRawDiscardLevel;} + BOOL isCachedRawImageReady() const {return mCachedRawImageReady ;} + BOOL isRawImageValid()const { return mIsRawImageValid ; } + + void forceToSaveRawImage(S32 desired_discard = 0) ; + void destroySavedRawImage() ; + + BOOL isSameTexture(const LLViewerImage* tex) const ; + + void addFace(LLFace* facep) ; + void removeFace(LLFace* facep) ; private: /*virtual*/ void cleanup(); // Cleanup the LLViewerImage (so we can reinitialize it) @@ -279,14 +321,19 @@ private: // Used to be in LLImageGL LLImageRaw* readBackRawImage(S8 discard_level = 0); void destroyRawImage(); - + void saveRawImage() ; + BOOL forceFetch() ; + + void scaleDown() ; + void switchToCachedImage(); + void setCachedRawImage() ; public: S32 mFullWidth; S32 mFullHeight; S32 mOrigWidth; S32 mOrigHeight; - std::string mLocalFileName; + std::string mUrl; // Data used for calculating required image priority/quality level/decimation mutable F32 mMaxVirtualSize; // The largest virtual size of the image, in pixels - how much data to we need? @@ -318,6 +365,7 @@ private: S8 mDesiredDiscardLevel; // The discard level we'd LIKE to have - if we have it and there's space S8 mMinDesiredDiscardLevel; // The minimum discard level we'd like to have S8 mNeedsCreateTexture; + mutable S8 mNeedsGLTexture; S8 mNeedsAux; // We need to decode the auxiliary channels S8 mDecodingAux; // Are we decoding high components S8 mIsRawImageValid; @@ -332,6 +380,7 @@ private: S32 mKnownDrawHeight; F32 mDecodePriority; // The priority for decoding this image. + mutable F32 mAdditionalDecodePriority; // priority add to mDecodePriority. S32 mBoostLevel; // enum describing priority level typedef std::list callback_list_t; @@ -342,12 +391,30 @@ private: S32 mMinDiscardLevel; F32 mCalculatedDiscardLevel; // Last calculated discard level + //keep a copy of mRawImage for some special purposes + //when mForceToSaveRawImage is set. + BOOL mForceToSaveRawImage ; + LLPointer mSavedRawImage; + S32 mSavedRawDiscardLevel; + S32 mDesiredSavedRawDiscardLevel; + // Used ONLY for cloth meshes right now. Make SURE you know what you're // doing if you use it for anything else! - djs LLPointer mAuxRawImage; + //a small version of the copy of the raw image (<= 64 * 64) + LLPointer mCachedRawImage; + S32 mCachedRawDiscardLevel; + BOOL mCachedRawImageReady; //the rez of the mCachedRawImage reaches the upper limit. + LLHost mTargetHost; // if LLHost::invalid, just request from agent's simulator + BOOL mForSculpt ; //a flag if the texture is used for a sculpt data. + mutable BOOL mNeedsResetMaxVirtualSize ; + + typedef std::list ll_face_list_t ; + ll_face_list_t mFaceList ; //reverse pointer pointing to the faces using this image as texture + public: static const U32 sCurrentFileVersion; // Default textures @@ -361,6 +428,7 @@ public: static S32 sRawCount; static S32 sAuxCount; static LLTimer sEvaluationTimer; + static S8 sCameraMovingDiscardBias; static F32 sDesiredDiscardBias; static F32 sDesiredDiscardScale; static S32 sBoundTextureMemoryInBytes; @@ -369,6 +437,12 @@ public: static S32 sMaxTotalTextureMemInMegaBytes; static S32 sMaxDesiredTextureMemInBytes ; static BOOL sDontLoadVolumeTextures; + + static S32 sMaxSculptRez ; + static S32 sMinLargeImageSize ; + static S32 sMaxSmallImageSize ; + static BOOL sFreezeImageScalingDown ;//do not scale down image res if set. + static S32 sLLViewerImageCount ; }; #endif diff --git a/linden/indra/newview/llviewerimagelist.cpp b/linden/indra/newview/llviewerimagelist.cpp index bd834a0..2280937 100644 --- a/linden/indra/newview/llviewerimagelist.cpp +++ b/linden/indra/newview/llviewerimagelist.cpp @@ -42,6 +42,7 @@ #include "llimagetga.h" #include "llimagejpeg.h" #include "llimagepng.h" +#include "llimageworker.h" #include "llsdserialize.h" #include "llsys.h" @@ -58,6 +59,7 @@ #include "llviewerimage.h" #include "llviewermedia.h" #include "llviewerregion.h" +#include "llviewerstats.h" #include "pipeline.h" #include "llappviewer.h" @@ -70,11 +72,6 @@ void (*LLViewerImageList::sUUIDCallback)(void **, const LLUUID&) = NULL; U32 LLViewerImageList::sTextureBits = 0; U32 LLViewerImageList::sTexturePackets = 0; -const S32 IMAGES_PER_REQUEST = 42; -const S32 IMAGES_MIN_UPDATES = 4; // Always update the highest N images each frame -const S32 IMAGES_MAX_PACKET_UPDATES = 1; // Only send N packets of IMAGES_PER_REQUEST in a frame -const F32 RESEND_IMAGE_REQUEST_TIME = 15.f; // seconds - LLViewerImageList gImageList; S32 LLViewerImageList::sNumImages = 0; @@ -182,6 +179,7 @@ static std::string get_texture_list_name() void LLViewerImageList::doPrefetchImages() { +#if 1 if (LLAppViewer::instance()->getPurgeCache()) { // cache was purged, no point @@ -209,7 +207,7 @@ void LLViewerImageList::doPrefetchImages() image->addTextureStats((F32)pixel_area); } } - +#endif } @@ -237,6 +235,10 @@ void LLViewerImageList::shutdown() { continue; // avoid UI, baked, and other special images } + if(!image->getBoundRecently()) + { + continue ; + } S32 desired = image->getDesiredDiscardLevel(); if (desired >= 0 && desired < MAX_DISCARD_LEVEL) { @@ -323,17 +325,29 @@ LLViewerImage* LLViewerImageList::getImageFromFile(const std::string& filename, LLGLenum primary_format, const LLUUID& force_id) { - if (gNoRender) + std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename); + if (full_path.empty()) { - // Never mind that this ignores image_set_id; - // getImage() will handle that later. + llwarns << "Failed to find local image file: " << filename << llendl; return getImage(IMG_DEFAULT, TRUE, TRUE); } - std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename); - if (full_path.empty()) + std::string url = "file://" + full_path; + + return getImageFromUrl(url, usemipmaps, level_immediate, internal_format, primary_format, force_id); +} + +LLViewerImage* LLViewerImageList::getImageFromUrl(const std::string& url, + BOOL usemipmaps, + BOOL level_immediate, + LLGLint internal_format, + LLGLenum primary_format, + const LLUUID& force_id) +{ + if (gNoRender) { - llwarns << "Failed to find local image file: " << filename << llendl; + // Never mind that this ignores image_set_id; + // getImage() will handle that later. return getImage(IMG_DEFAULT, TRUE, TRUE); } @@ -345,14 +359,14 @@ LLViewerImage* LLViewerImageList::getImageFromFile(const std::string& filename, } else { - new_id.generate(full_path); + new_id.generate(url); } LLPointer imagep = hasImage(new_id); if (imagep.isNull()) { - imagep = new LLViewerImage(full_path, new_id, usemipmaps); + imagep = new LLViewerImage(url, new_id, usemipmaps); if (internal_format && primary_format) { @@ -364,7 +378,7 @@ LLViewerImage* LLViewerImageList::getImageFromFile(const std::string& filename, if (level_immediate) { imagep->dontDiscard(); - imagep->setBoostLevel(LLViewerImage::BOOST_UI); + imagep->setBoostLevel(LLViewerImageBoostLevel::BOOST_UI); } } @@ -394,9 +408,7 @@ LLViewerImage* LLViewerImageList::getImage(const LLUUID &image_id, if (imagep.isNull()) { - imagep = new LLViewerImage(image_id, usemipmaps); - // Might want to request from host other than where the agent is. JC - imagep->setTargetHost(request_from_host); + imagep = new LLViewerImage(image_id, request_from_host, usemipmaps); if (internal_format && primary_format) { @@ -408,7 +420,7 @@ LLViewerImage* LLViewerImageList::getImage(const LLUUID &image_id, if (level_immediate) { imagep->dontDiscard(); - imagep->setBoostLevel(LLViewerImage::BOOST_UI); + imagep->setBoostLevel(LLViewerImageBoostLevel::BOOST_UI); } else { @@ -511,23 +523,29 @@ void LLViewerImageList::dirtyImage(LLViewerImage *image) void LLViewerImageList::updateImages(F32 max_time) { llpushcallstacks ; + LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec()); + sNumImagesStat.addValue(sNumImages); sNumRawImagesStat.addValue(LLImageRaw::sRawImageCount); sGLTexMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sGlobalTextureMemoryInBytes)); sGLBoundMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sBoundTextureMemoryInBytes)); sRawMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageRaw::sGlobalRawMemory)); sFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory)); - + llpushcallstacks ; + updateImagesDecodePriorities(); + llpushcallstacks ; + F32 total_max_time = max_time; max_time -= updateImagesFetchTextures(max_time); + llpushcallstacks ; - max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f); + max_time = llmax(max_time, total_max_time*.25f); // at least 25% of max_time max_time -= updateImagesCreateTextures(max_time); + llpushcallstacks ; - max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f); - llpushcallstacks ; + if (!mDirtyTextureList.empty()) { LLFastTimer t(LLFastTimer::FTM_IMAGE_MARK_DIRTY); @@ -541,7 +559,7 @@ void LLViewerImageList::updateImages(F32 max_time) { //trigger loaded callbacks on local textures immediately LLViewerImage* image = *iter++; - if (!image->mLocalFileName.empty()) + if (!image->mUrl.empty()) { // Do stuff to handle callbacks, update priorities, etc. didone = image->doLoadedCallbacks(); @@ -627,7 +645,7 @@ void LLViewerImageList::updateImagesDecodePriorities() imagep->setInactive() ; } } - + imagep->processTextureStats(); F32 old_priority = imagep->getDecodePriority(); F32 old_priority_test = llmax(old_priority, 0.0f); @@ -701,7 +719,7 @@ F32 LLViewerImageList::updateImagesCreateTextures(F32 max_time) return create_timer.getElapsedTimeF32(); } -void LLViewerImageList::forceImmediateUpdate(LLViewerImage* imagep) +void LLViewerImageList::bumpToMaxDecodePriority(LLViewerImage* imagep) { if(!imagep) { @@ -709,6 +727,11 @@ void LLViewerImageList::forceImmediateUpdate(LLViewerImage* imagep) } if(imagep->mInImageList) { + if (imagep->getDecodePriority() == LLViewerImage::maxDecodePriority()) + { + // Already at maximum. + return; + } removeImageFromList(imagep); } @@ -731,74 +754,76 @@ F32 LLViewerImageList::updateImagesFetchTextures(F32 max_time) const size_t max_update_count = llmin((S32) (1024*10.f*gFrameIntervalSeconds)+1, 256); // 32 high priority entries - std::set entries; + typedef std::vector entries_list_t; + entries_list_t entries; size_t update_counter = llmin(max_priority_count, mImageList.size()); image_priority_list_t::iterator iter1 = mImageList.begin(); while(update_counter > 0) { - // added extra granularity and verbosity for crash logging during 1.19.1 RC. -Brad - if(iter1 == mImageList.end()) - { - llerrs << "DEV-12002: update_counter not calculated correctly!" << llendl; - return 0.f; - } - - LLPointer const & ptr = *iter1; - - LLViewerImage * img = ptr.get(); - - // added extra granularity and verbosity for crash logging during 1.19.1 RC. -Brad - if(img == NULL) - { - llwarns << "DEV-12002: image is NULL!" << llendl; - } - - entries.insert(img); - + entries.push_back(*iter1); ++iter1; update_counter--; } // 256 cycled entries update_counter = llmin(max_update_count, mUUIDMap.size()); - uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID); - while(update_counter > 0) + if (update_counter > 0) { - if (iter2 == mUUIDMap.end()) + uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID); + uuid_map_t::iterator iter2p = iter2; + while(update_counter > 0) { - iter2 = mUUIDMap.begin(); + if (iter2 == mUUIDMap.end()) + { + iter2 = mUUIDMap.begin(); + } + entries.push_back(iter2->second); + iter2p = iter2++; + update_counter--; } - mLastFetchUUID = iter2->first; - entries.insert(iter2->second); - ++iter2; - update_counter--; + mLastFetchUUID = iter2p->first; } + S32 fetch_count = 0; S32 min_count = max_priority_count + max_update_count/4; - for (std::set::iterator iter3 = entries.begin(); + for (entries_list_t::iterator iter3 = entries.begin(); iter3 != entries.end(); ) { LLPointer imagep = *iter3++; - imagep->updateFetch(); + bool fetching = imagep->updateFetch(); + if (fetching) + { + fetch_count++; + } if (min_count <= 0 && image_op_timer.getElapsedTimeF32() > max_time) { break; } min_count--; } + if (fetch_count == 0) + { + gDebugTimers[0].pause(); + } + else + { + gDebugTimers[0].unpause(); + } + + return image_op_timer.getElapsedTimeF32(); } void LLViewerImageList::updateImagesUpdateStats() { - if (mUpdateStats) + if (mUpdateStats && mForceResetTextureStats) { for (image_priority_list_t::iterator iter = mImageList.begin(); iter != mImageList.end(); ) { LLViewerImage* imagep = *iter++; - imagep->resetTextureStats(mForceResetTextureStats); + imagep->resetTextureStats(); } mUpdateStats = FALSE; mForceResetTextureStats = FALSE; @@ -1348,7 +1373,7 @@ void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerImage *src_vi, LLImag // for images grabbed from local files, apply clipping rectangle to restore original dimensions // from power-of-2 gl image - if (success && imagep.notNull() && src_vi && !src_vi->mLocalFileName.empty()) + if (success && imagep.notNull() && src_vi && (src_vi->mUrl.compare(0, 7, "file://")==0)) { F32 clip_x = (F32)src_vi->getOriginalWidth() / (F32)src_vi->getWidth(0); F32 clip_y = (F32)src_vi->getOriginalHeight() / (F32)src_vi->getHeight(0); diff --git a/linden/indra/newview/llviewerimagelist.h b/linden/indra/newview/llviewerimagelist.h index 5fad0a2..561e8e5 100644 --- a/linden/indra/newview/llviewerimagelist.h +++ b/linden/indra/newview/llviewerimagelist.h @@ -97,7 +97,7 @@ public: LLHost request_from_host = LLHost() ); - LLViewerImage * getImageFromFile(const std::string& filename, + LLViewerImage * getImageFromUrl(const std::string& url, BOOL usemipmap = TRUE, BOOL level_immediate = FALSE, // Get the requested level immediately upon creation. LLGLint internal_format = 0, @@ -105,6 +105,14 @@ public: const LLUUID& force_id = LLUUID::null ); + LLViewerImage * getImageFromFile(const std::string& filename, + BOOL usemipmap = TRUE, + BOOL level_immediate = FALSE, // Get the requested level immediately upon creation. + LLGLint internal_format = 0, + LLGLenum primary_format = 0, + const LLUUID& force_id = LLUUID::null + ); + // Request image from a specific host, used for baked avatar textures. // Implemented in header in case someone changes default params above. JC LLViewerImage* getImageFromHost(const LLUUID& image_id, LLHost host) @@ -121,7 +129,7 @@ public: // Using image stats, determine what images are necessary, and perform image updates. void updateImages(F32 max_time); - void forceImmediateUpdate(LLViewerImage* imagep) ; + void bumpToMaxDecodePriority(LLViewerImage* imagep) ; // Decode and create textures for all images currently in list. void decodeAllImages(F32 max_decode_time); diff --git a/linden/indra/newview/llviewermessage.cpp b/linden/indra/newview/llviewermessage.cpp index 94ae3ab..8bdbc30 100644 --- a/linden/indra/newview/llviewermessage.cpp +++ b/linden/indra/newview/llviewermessage.cpp @@ -106,6 +106,7 @@ #include "llstatenums.h" #include "llstatusbar.h" #include "llimview.h" +#include "lltexturestats.h" #include "lltool.h" #include "lltoolbar.h" #include "lltoolmgr.h" diff --git a/linden/indra/newview/llviewerobject.cpp b/linden/indra/newview/llviewerobject.cpp index 51908a5..860052c 100644 --- a/linden/indra/newview/llviewerobject.cpp +++ b/linden/indra/newview/llviewerobject.cpp @@ -2900,7 +2900,7 @@ F32 LLViewerObject::getMidScale() const } -void LLViewerObject::updateTextures(LLAgent &agent) +void LLViewerObject::updateTextures() { } @@ -2915,14 +2915,14 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */) S32 tex_count = getNumTEs(); for (i = 0; i < tex_count; i++) { - getTEImage(i)->setBoostLevel(LLViewerImage::BOOST_SELECTED); + getTEImage(i)->setBoostLevel(LLViewerImageBoostLevel::BOOST_SELECTED); } if (isSculpted()) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); LLUUID sculpt_id = sculpt_params->getSculptTexture(); - gImageList.getImage(sculpt_id)->setBoostLevel(LLViewerImage::BOOST_SELECTED); + gImageList.getImage(sculpt_id)->setBoostLevel(LLViewerImageBoostLevel::BOOST_SELECTED); } if (boost_children) diff --git a/linden/indra/newview/llviewerobject.h b/linden/indra/newview/llviewerobject.h index 633e0eb..88f967f 100644 --- a/linden/indra/newview/llviewerobject.h +++ b/linden/indra/newview/llviewerobject.h @@ -190,7 +190,7 @@ public: S32 getNumFaces() const { return mNumFaces; } // Graphical stuff for objects - maybe broken out into render class later? - virtual void updateTextures(LLAgent &agent); + virtual void updateTextures(); virtual void boostTexturePriority(BOOL boost_children = TRUE); // When you just want to boost priority of this object virtual LLDrawable* createDrawable(LLPipeline *pipeline); diff --git a/linden/indra/newview/llviewerobjectlist.cpp b/linden/indra/newview/llviewerobjectlist.cpp index 059925e..3ac8fc4 100644 --- a/linden/indra/newview/llviewerobjectlist.cpp +++ b/linden/indra/newview/llviewerobjectlist.cpp @@ -612,7 +612,7 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent) // Update distance & gpw objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area - objectp->updateTextures(agent); // Update the image levels of textures for this object. + objectp->updateTextures(); // Update the image levels of textures for this object. } } diff --git a/linden/indra/newview/llviewerparceloverlay.cpp b/linden/indra/newview/llviewerparceloverlay.cpp index 866c2a9..5cea9c2 100644 --- a/linden/indra/newview/llviewerparceloverlay.cpp +++ b/linden/indra/newview/llviewerparceloverlay.cpp @@ -71,7 +71,7 @@ LLViewerParcelOverlay::LLViewerParcelOverlay(LLViewerRegion* region, F32 region_ // Use mipmaps = FALSE, clamped, NEAREST filter, for sharp edges mTexture = new LLImageGL(FALSE); mImageRaw = new LLImageRaw(mParcelGridsPerEdge, mParcelGridsPerEdge, OVERLAY_IMG_COMPONENTS); - mTexture->createGLTexture(0, mImageRaw); + mTexture->createGLTexture(0, mImageRaw, 0, TRUE, LLViewerImageBoostLevel::OTHER); gGL.getTexUnit(0)->activate(); gGL.getTexUnit(0)->bind(mTexture); mTexture->setAddressMode(LLTexUnit::TAM_CLAMP); diff --git a/linden/indra/newview/llviewerregion.cpp b/linden/indra/newview/llviewerregion.cpp index 19b646c..1d380d7 100644 --- a/linden/indra/newview/llviewerregion.cpp +++ b/linden/indra/newview/llviewerregion.cpp @@ -1408,9 +1408,9 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); capabilityNames.append("FetchInventory"); - capabilityNames.append("WebFetchInventoryDescendents"); capabilityNames.append("FetchLib"); capabilityNames.append("FetchLibDescendents"); + capabilityNames.append("GetTexture"); capabilityNames.append("GroupProposalBallot"); capabilityNames.append("HomeLocation"); capabilityNames.append("MapLayer"); @@ -1429,6 +1429,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("SendUserReportWithScreenshot"); capabilityNames.append("ServerReleaseNotes"); capabilityNames.append("StartGroupProposal"); + capabilityNames.append("TextureStats"); capabilityNames.append("UntrustedSimulatorMessage"); capabilityNames.append("UpdateAgentInformation"); capabilityNames.append("UpdateAgentLanguage"); @@ -1441,6 +1442,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("UploadBakedTexture"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); + capabilityNames.append("WebFetchInventoryDescendents"); // Please add new capabilities alphabetically to reduce // merge conflicts. diff --git a/linden/indra/newview/llviewerwindow.cpp b/linden/indra/newview/llviewerwindow.cpp index ef4e728..3d884ad 100644 --- a/linden/indra/newview/llviewerwindow.cpp +++ b/linden/indra/newview/llviewerwindow.cpp @@ -115,6 +115,7 @@ #include "llhudview.h" #include "llimagebmp.h" #include "llimagej2c.h" +#include "llimageworker.h" #include "llinventoryview.h" #include "llkeyboard.h" #include "lllineeditor.h" @@ -1310,6 +1311,7 @@ LLViewerWindow::LLViewerWindow( // Init the image list. Must happen after GL is initialized and before the images that // LLViewerWindow needs are requested. + LLImageGL::initClass(LLViewerImageBoostLevel::MAX_GL_IMAGE_CATEGORY) ; gImageList.init(); LLViewerImage::initClass(); gBumpImageList.init(); diff --git a/linden/indra/newview/llvlcomposition.cpp b/linden/indra/newview/llvlcomposition.cpp index a269225..9c383c7 100644 --- a/linden/indra/newview/llvlcomposition.cpp +++ b/linden/indra/newview/llvlcomposition.cpp @@ -234,7 +234,7 @@ BOOL LLVLComposition::generateComposition() { if (mDetailTextures[i]->getDiscardLevel() < 0) { - mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); // in case we are at low detail + mDetailTextures[i]->setBoostLevel(LLViewerImageBoostLevel::BOOST_TERRAIN); // in case we are at low detail mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE); return FALSE; } @@ -251,7 +251,7 @@ BOOL LLVLComposition::generateComposition() ddiscard++; min_dim /= 2; } - mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); // in case we are at low detail + mDetailTextures[i]->setBoostLevel(LLViewerImageBoostLevel::BOOST_TERRAIN); // in case we are at low detail mDetailTextures[i]->setMinDiscardLevel(ddiscard); return FALSE; } @@ -292,10 +292,10 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, ddiscard++; min_dim /= 2; } - if (!mDetailTextures[i]->readBackRaw(ddiscard, mRawImages[i], false)) + mRawImages[i] = mDetailTextures[i]->getCachedRawImage() ; + if (!mRawImages[i]) { - llwarns << "Unable to read raw data for terrain detail texture: " << mDetailTextures[i]->getID() << llendl; - mRawImages[i] = NULL; + llwarns << "no cached raw data for terrain detail texture: " << mDetailTextures[i]->getID() << llendl; return FALSE; } if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE || @@ -460,7 +460,7 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, for (S32 i = 0; i < 4; i++) { // Un-boost detatil textures (will get re-boosted if rendering in high detail) - mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_NONE); + mDetailTextures[i]->setBoostLevel(LLViewerImageBoostLevel::BOOST_NONE); mDetailTextures[i]->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1); } diff --git a/linden/indra/newview/llvoavatar.cpp b/linden/indra/newview/llvoavatar.cpp index ab5d83e..d2f4a58 100644 --- a/linden/indra/newview/llvoavatar.cpp +++ b/linden/indra/newview/llvoavatar.cpp @@ -1272,6 +1272,49 @@ void LLVOAvatar::resetImpostors() // static void LLVOAvatar::deleteCachedImages(bool clearAll) { +if(gAuditTexture) + { + S32 total_tex_size = sScratchTexBytes ; + S32 tex_size = SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT ; + + if( LLVOAvatar::sScratchTexNames.checkData( GL_LUMINANCE ) ) + { + LLImageGL::decTextureCounterStatic(tex_size, 1, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; + total_tex_size -= tex_size ; + } + if( LLVOAvatar::sScratchTexNames.checkData( GL_ALPHA ) ) + { + LLImageGL::decTextureCounterStatic(tex_size, 1, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; + total_tex_size -= tex_size ; + } + if( LLVOAvatar::sScratchTexNames.checkData( GL_COLOR_INDEX ) ) + { + LLImageGL::decTextureCounterStatic(tex_size, 1, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; + total_tex_size -= tex_size ; + } + if( LLVOAvatar::sScratchTexNames.checkData( GL_LUMINANCE_ALPHA ) ) + { + LLImageGL::decTextureCounterStatic(tex_size, 2, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; + total_tex_size -= 2 * tex_size ; + } + if( LLVOAvatar::sScratchTexNames.checkData( GL_RGB ) ) + { + LLImageGL::decTextureCounterStatic(tex_size, 3, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; + total_tex_size -= 3 * tex_size ; + } + if( LLVOAvatar::sScratchTexNames.checkData( GL_RGBA ) ) + { + LLImageGL::decTextureCounterStatic(tex_size, 4, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; + total_tex_size -= 4 * tex_size ; + } + //others + while(total_tex_size > 0) + { + LLImageGL::decTextureCounterStatic(tex_size, 4, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; + total_tex_size -= 4 * tex_size ; + } + } + if (LLTexLayerSet::sHasCaches) { lldebugs << "Deleting layer set caches" << llendl; @@ -4701,7 +4744,7 @@ U32 LLVOAvatar::renderFootShadows() LLGLDepthTest test(GL_TRUE, GL_FALSE); //render foot shadows LLGLEnable blend(GL_BLEND); - gGL.getTexUnit(0)->bind(mShadowImagep.get()); + gGL.getTexUnit(0)->bind(mShadowImagep.get(), TRUE); glColor4fv(mShadow0Facep->getRenderColor().mV); mShadow0Facep->renderIndexed(foot_mask); glColor4fv(mShadow1Facep->getRenderColor().mV); @@ -4749,7 +4792,7 @@ U32 LLVOAvatar::renderImpostor(LLColor4U color) //------------------------------------------------------------------------ // LLVOAvatar::updateTextures() //------------------------------------------------------------------------ -void LLVOAvatar::updateTextures(LLAgent &agent) +void LLVOAvatar::updateTextures() { BOOL render_avatar = TRUE; @@ -4816,9 +4859,9 @@ void LLVOAvatar::updateTextures(LLAgent &agent) if (imagep) { // Debugging code - maybe non-self avatars are downloading textures? - //llinfos << "avatar self " << mIsSelf << " tex " << i + //llinfos << "avatar self " << mIsSelf << " tex " << index // << " decode " << imagep->getDecodePriority() - // << " boost " << boost_avatar + // << " boost " << imagep->getBoostLevel() // << " size " << imagep->getWidth() << "x" << imagep->getHeight() // << " discard " << imagep->getDiscardLevel() // << " desired " << imagep->getDesiredDiscardLevel() @@ -4826,7 +4869,7 @@ void LLVOAvatar::updateTextures(LLAgent &agent) const LLTextureEntry *te = getTE(index); F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT); - S32 boost_level = mIsSelf ? LLViewerImage::BOOST_AVATAR_BAKED_SELF : LLViewerImage::BOOST_AVATAR_BAKED; + S32 boost_level = mIsSelf ? LLViewerImageBoostLevel::BOOST_AVATAR_BAKED_SELF : LLViewerImageBoostLevel::BOOST_AVATAR_BAKED; // Spam if this is a baked texture, not set to default image, without valid host info if (isIndexBakedTexture((ETextureIndex)index) @@ -4849,6 +4892,12 @@ void LLVOAvatar::updateTextures(LLAgent &agent) if (texture_dict->mIsLocalTexture) { addLocalTextureStats((ETextureIndex)index, imagep, texel_area_ratio, render_avatar, layer_baked[baked_index]); + // SNOW-8 : temporary snowglobe1.0 fix for baked textures + if (render_avatar && !gGLManager.mIsDisabled ) + { + // bind the texture so that its boost level won't be slammed + gGL.getTexUnit(0)->bind(imagep); + } } else if (texture_dict->mIsBakedTexture) { @@ -4886,12 +4935,12 @@ void LLVOAvatar::addLocalTextureStats( ETextureIndex idx, LLViewerImage* imagep, if( mIsSelf ) { desired_pixels = llmin(mPixelArea, (F32)TEX_IMAGE_AREA_SELF ); - imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR_SELF); + imagep->setBoostLevel(LLViewerImageBoostLevel::BOOST_AVATAR_SELF); } else { desired_pixels = llmin(mPixelArea, (F32)TEX_IMAGE_AREA_OTHER ); - imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR); + imagep->setBoostLevel(LLViewerImageBoostLevel::BOOST_AVATAR); } imagep->addTextureStats( desired_pixels / texel_area_ratio ); if (imagep->getDiscardLevel() < 0) @@ -7111,12 +7160,12 @@ BOOL LLVOAvatar::bindScratchTexture( LLGLenum format ) if( *last_bind_time != LLImageGL::sLastFrameTime ) { *last_bind_time = LLImageGL::sLastFrameTime; - LLImageGL::updateBoundTexMem(texture_bytes); + LLImageGL::updateBoundTexMemStatic(texture_bytes, SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; } } else { - LLImageGL::updateBoundTexMem(texture_bytes); + LLImageGL::updateBoundTexMemStatic(texture_bytes, SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; LLVOAvatar::sScratchTexLastBindTime.addData( format, new F32(LLImageGL::sLastFrameTime) ); } @@ -7138,7 +7187,8 @@ LLGLuint LLVOAvatar::getScratchTexName( LLGLenum format, U32* texture_bytes ) { case GL_LUMINANCE: components = 1; internal_format = GL_LUMINANCE8; break; case GL_ALPHA: components = 1; internal_format = GL_ALPHA8; break; - case GL_COLOR_INDEX: components = 1; internal_format = GL_COLOR_INDEX8_EXT; break; +// Support for GL_EXT_paletted_texture is deprecated +// case GL_COLOR_INDEX: components = 1; internal_format = GL_COLOR_INDEX8_EXT; break; case GL_LUMINANCE_ALPHA: components = 2; internal_format = GL_LUMINANCE8_ALPHA8; break; case GL_RGB: components = 3; internal_format = GL_RGB8; break; case GL_RGBA: components = 4; internal_format = GL_RGBA8; break; @@ -7180,6 +7230,12 @@ LLGLuint LLVOAvatar::getScratchTexName( LLGLenum format, U32* texture_bytes ) LLVOAvatar::sScratchTexBytes += *texture_bytes; LLImageGL::sGlobalTextureMemoryInBytes += *texture_bytes; + + if(gAuditTexture) + { + LLImageGL::incTextureCounterStatic(SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT, components, LLViewerImageBoostLevel::AVATAR_SCRATCH_TEX) ; + } + return name; } } @@ -7495,7 +7551,8 @@ void LLVOAvatar::clearChat() S32 LLVOAvatar::getLocalDiscardLevel( ETextureIndex index ) { - if (!isIndexLocalTexture(index)) return FALSE; + // If the texture is not local, we don't care and treat it as fully loaded + if (!isIndexLocalTexture(index)) return 0; LocalTextureData &local_tex_data = mLocalTextureData[index]; if (index >= 0 diff --git a/linden/indra/newview/llvoavatar.h b/linden/indra/newview/llvoavatar.h index f759e15..33cec69 100644 --- a/linden/indra/newview/llvoavatar.h +++ b/linden/indra/newview/llvoavatar.h @@ -133,7 +133,7 @@ public: LLVector3* bi_normal = NULL // return the surface bi-normal at the intersection point ); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); // If setting a baked texture, need to request it from a non-local sim. /*virtual*/ S32 setTETexture(const U8 te, const LLUUID& uuid); /*virtual*/ void onShift(const LLVector3& shift_vector); diff --git a/linden/indra/newview/llvoclouds.cpp b/linden/indra/newview/llvoclouds.cpp index 9095ee4..a489f91 100644 --- a/linden/indra/newview/llvoclouds.cpp +++ b/linden/indra/newview/llvoclouds.cpp @@ -62,7 +62,7 @@ LLVOClouds::LLVOClouds(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re mbCanSelect = FALSE; setNumTEs(1); LLViewerImage* image = gImageList.getImage(gCloudTextureID); - image->setBoostLevel(LLViewerImage::BOOST_CLOUDS); + image->setBoostLevel(LLViewerImageBoostLevel::BOOST_CLOUDS); setTEImage(0, image); } @@ -101,7 +101,7 @@ void LLVOClouds::setPixelAreaAndAngle(LLAgent &agent) mPixelArea = 1500*100; } -void LLVOClouds::updateTextures(LLAgent &agent) +void LLVOClouds::updateTextures() { getTEImage(0)->addTextureStats(mPixelArea); } diff --git a/linden/indra/newview/llvoclouds.h b/linden/indra/newview/llvoclouds.h index f70ea5b..52e5a68 100644 --- a/linden/indra/newview/llvoclouds.h +++ b/linden/indra/newview/llvoclouds.h @@ -65,7 +65,7 @@ public: /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. F32 getPartSize(S32 idx); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area void updateFaceSize(S32 idx) { } diff --git a/linden/indra/newview/llvograss.cpp b/linden/indra/newview/llvograss.cpp index dd4dabb..d0a36b0 100644 --- a/linden/indra/newview/llvograss.cpp +++ b/linden/indra/newview/llvograss.cpp @@ -339,7 +339,7 @@ void LLVOGrass::setPixelAreaAndAngle(LLAgent &agent) // BUG could speed this up by caching the relative_position and range calculations -void LLVOGrass::updateTextures(LLAgent &agent) +void LLVOGrass::updateTextures() { if (getTEImage(0)) { diff --git a/linden/indra/newview/llvograss.h b/linden/indra/newview/llvograss.h index b759488..abfc7e1 100644 --- a/linden/indra/newview/llvograss.h +++ b/linden/indra/newview/llvograss.h @@ -72,7 +72,7 @@ public: LLStrider& indicesp); void updateFaceSize(S32 idx) { } - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ BOOL updateLOD(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area diff --git a/linden/indra/newview/llvoground.cpp b/linden/indra/newview/llvoground.cpp index fe19e18..0ef0196 100644 --- a/linden/indra/newview/llvoground.cpp +++ b/linden/indra/newview/llvoground.cpp @@ -71,7 +71,7 @@ BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } -void LLVOGround::updateTextures(LLAgent &agent) +void LLVOGround::updateTextures() { } diff --git a/linden/indra/newview/llvoground.h b/linden/indra/newview/llvoground.h index f485bd0..b58ebae 100644 --- a/linden/indra/newview/llvoground.h +++ b/linden/indra/newview/llvoground.h @@ -51,7 +51,7 @@ public: // Graphical stuff for objects - maybe broken out into render class // later? - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/linden/indra/newview/llvopartgroup.cpp b/linden/indra/newview/llvopartgroup.cpp index 29036f4..a0f8068 100644 --- a/linden/indra/newview/llvopartgroup.cpp +++ b/linden/indra/newview/llvopartgroup.cpp @@ -108,7 +108,7 @@ void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent) } } -void LLVOPartGroup::updateTextures(LLAgent &agent) +void LLVOPartGroup::updateTextures() { // Texture stats for particles need to be updated in a different way... } diff --git a/linden/indra/newview/llvopartgroup.h b/linden/indra/newview/llvopartgroup.h index 3dc3292..18583b4 100644 --- a/linden/indra/newview/llvopartgroup.h +++ b/linden/indra/newview/llvopartgroup.h @@ -61,7 +61,7 @@ public: virtual U32 getPartitionType() const; /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/linden/indra/newview/llvosky.cpp b/linden/indra/newview/llvosky.cpp index d4df141..1b9c7c0 100644 --- a/linden/indra/newview/llvosky.cpp +++ b/linden/indra/newview/llvosky.cpp @@ -289,7 +289,7 @@ void LLSkyTex::create(const F32 brightness) void LLSkyTex::createGLImage(S32 which) { - mImageGL[which]->createGLTexture(0, mImageRaw[which]); + mImageGL[which]->createGLTexture(0, mImageRaw[which], 0, TRUE, LLViewerImageBoostLevel::OTHER); mImageGL[which]->setAddressMode(LLTexUnit::TAM_CLAMP); } @@ -1180,7 +1180,7 @@ BOOL LLVOSky::updateSky() return TRUE; } -void LLVOSky::updateTextures(LLAgent &agent) +void LLVOSky::updateTextures() { if (mSunTexturep) { diff --git a/linden/indra/newview/llvosky.h b/linden/indra/newview/llvosky.h index 3cf454d..333e144 100644 --- a/linden/indra/newview/llvosky.h +++ b/linden/indra/newview/llvosky.h @@ -492,7 +492,7 @@ public: // Graphical stuff for objects - maybe broken out into render class // later? - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/linden/indra/newview/llvosurfacepatch.cpp b/linden/indra/newview/llvosurfacepatch.cpp index 4980b50..d86f758 100644 --- a/linden/indra/newview/llvosurfacepatch.cpp +++ b/linden/indra/newview/llvosurfacepatch.cpp @@ -134,7 +134,7 @@ void LLVOSurfacePatch::setPixelAreaAndAngle(LLAgent &agent) } -void LLVOSurfacePatch::updateTextures(LLAgent &agent) +void LLVOSurfacePatch::updateTextures() { } diff --git a/linden/indra/newview/llvosurfacepatch.h b/linden/indra/newview/llvosurfacepatch.h index 2dd8651..d3b1447 100644 --- a/linden/indra/newview/llvosurfacepatch.h +++ b/linden/indra/newview/llvosurfacepatch.h @@ -74,7 +74,7 @@ public: LLStrider &texCoords1p, LLStrider &indicesp); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area /*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax); diff --git a/linden/indra/newview/llvotextbubble.cpp b/linden/indra/newview/llvotextbubble.cpp index de69aac..5943f9b 100644 --- a/linden/indra/newview/llvotextbubble.cpp +++ b/linden/indra/newview/llvotextbubble.cpp @@ -116,7 +116,7 @@ BOOL LLVOTextBubble::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } -void LLVOTextBubble::updateTextures(LLAgent &agent) +void LLVOTextBubble::updateTextures() { // Update the image levels of all textures... diff --git a/linden/indra/newview/llvotextbubble.h b/linden/indra/newview/llvotextbubble.h index 45d4df2..7f84dbf 100644 --- a/linden/indra/newview/llvotextbubble.h +++ b/linden/indra/newview/llvotextbubble.h @@ -44,7 +44,7 @@ public: /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ BOOL updateLOD(); diff --git a/linden/indra/newview/llvotree.cpp b/linden/indra/newview/llvotree.cpp index 3523d87..237f581 100644 --- a/linden/indra/newview/llvotree.cpp +++ b/linden/indra/newview/llvotree.cpp @@ -475,7 +475,7 @@ void LLVOTree::setPixelAreaAndAngle(LLAgent &agent) #endif } -void LLVOTree::updateTextures(LLAgent &agent) +void LLVOTree::updateTextures() { if (mTreeImagep) { diff --git a/linden/indra/newview/llvotree.h b/linden/indra/newview/llvotree.h index 748fc14..df0b5a9 100644 --- a/linden/indra/newview/llvotree.h +++ b/linden/indra/newview/llvotree.h @@ -69,7 +69,7 @@ public: // Graphical stuff for objects - maybe broken out into render class later? /*virtual*/ void render(LLAgent &agent); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/linden/indra/newview/llvotreenew.h b/linden/indra/newview/llvotreenew.h index 02f6d3a..4960d90 100644 --- a/linden/indra/newview/llvotreenew.h +++ b/linden/indra/newview/llvotreenew.h @@ -156,7 +156,7 @@ public: /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ void render(LLAgent &agent); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/linden/indra/newview/llvovolume.cpp b/linden/indra/newview/llvovolume.cpp index 607bb42..5f7327d 100644 --- a/linden/indra/newview/llvovolume.cpp +++ b/linden/indra/newview/llvovolume.cpp @@ -52,12 +52,10 @@ #include "lldrawpoolbump.h" #include "llface.h" #include "llspatialpartition.h" - -// TEMP HACK ventrella #include "llhudmanager.h" #include "llflexibleobject.h" - #include "llsky.h" +#include "lltexturefetch.h" #include "llviewercamera.h" #include "llviewerimagelist.h" #include "llviewerregion.h" @@ -69,7 +67,6 @@ const S32 MIN_QUIET_FRAMES_COALESCE = 30; const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; const F32 FORCE_CULL_AREA = 8.f; -const S32 MAX_SCULPT_REZ = 128; BOOL gAnimateTextures = TRUE; extern BOOL gHideSelectedObjects; @@ -406,28 +403,30 @@ BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) return TRUE; } -void LLVOVolume::updateTextures(LLAgent &agent) +void LLVOVolume::updateTextures() { const F32 TEXTURE_AREA_REFRESH_TIME = 5.f; // seconds - if (mDrawable.notNull() && mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME) + if (mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME) { - if (mDrawable->isVisible()) - { - updateTextures(); - } + updateTextureVirtualSize(); } } -void LLVOVolume::updateTextures() +void LLVOVolume::updateTextureVirtualSize() { // Update the pixel area of all faces + if(mDrawable.isNull() || !mDrawable->isVisible()) + { + return ; + } + if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE)) { return; } - if (LLViewerImage::sDontLoadVolumeTextures || mDrawable.isNull()) // || !mDrawable->isVisible()) + if (LLViewerImage::sDontLoadVolumeTextures || LLAppViewer::getTextureFetch()->mDebugPause) { return; } @@ -444,53 +443,51 @@ void LLVOVolume::updateTextures() LLFace* face = mDrawable->getFace(i); const LLTextureEntry *te = face->getTextureEntry(); LLViewerImage *imagep = face->getTexture(); - if (!imagep || !te || + if (!imagep || !te || face->mExtents[0] == face->mExtents[1]) { continue; } F32 vsize; - + F32 old_size = face->getVirtualSize(); + if (isHUDAttachment()) { F32 area = (F32) LLViewerCamera::getInstance()->getScreenPixelArea(); vsize = area; - imagep->setBoostLevel(LLViewerImage::BOOST_HUD); + imagep->setBoostLevel(LLViewerImageBoostLevel::BOOST_HUD); face->setPixelArea(area); // treat as full screen } else { - vsize = getTextureVirtualSize(face); + vsize = face->getTextureVirtualSize(); } - mPixelArea = llmax(mPixelArea, face->getPixelArea()); - - F32 old_size = face->getVirtualSize(); + mPixelArea = llmax(mPixelArea, face->getPixelArea()); if (face->mTextureMatrix != NULL) { - if (vsize < MIN_TEX_ANIM_SIZE && old_size > MIN_TEX_ANIM_SIZE || - vsize > MIN_TEX_ANIM_SIZE && old_size < MIN_TEX_ANIM_SIZE) + if ((vsize < MIN_TEX_ANIM_SIZE && old_size > MIN_TEX_ANIM_SIZE) || + (vsize > MIN_TEX_ANIM_SIZE && old_size < MIN_TEX_ANIM_SIZE)) { gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, FALSE); } } face->setVirtualSize(vsize); - imagep->addTextureStats(vsize); if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA)) { if (vsize < min_vsize) min_vsize = vsize; if (vsize > max_vsize) max_vsize = vsize; } - else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) - { - F32 pri = imagep->getDecodePriority(); - pri = llmax(pri, 0.0f); - if (pri < min_vsize) min_vsize = pri; - if (pri > max_vsize) max_vsize = pri; - } +// else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) +// { +// F32 pri = imagep->getDecodePriority(); +// pri = llmax(pri, 0.0f); +// if (pri < min_vsize) min_vsize = pri; +// if (pri > max_vsize) max_vsize = pri; +// } else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA)) { F32 pri = mPixelArea; @@ -506,31 +503,44 @@ void LLVOVolume::updateTextures() mSculptTexture = gImageList.getImage(id); if (mSculptTexture.notNull()) { - S32 lod = llmin(mLOD, 3); - F32 lodf = ((F32)(lod + 1.0f)/4.f); - F32 tex_size = lodf * MAX_SCULPT_REZ; - mSculptTexture->addTextureStats(2.f * tex_size * tex_size); mSculptTexture->setBoostLevel(llmax((S32)mSculptTexture->getBoostLevel(), - (S32)LLViewerImage::BOOST_SCULPTED)); - } - - S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture - S32 current_discard = mSculptLevel; + (S32)LLViewerImageBoostLevel::BOOST_SCULPTED)); + mSculptTexture->setForSculpt() ; + + if(!mSculptTexture->isCachedRawImageReady()) + { + S32 lod = llmin(mLOD, 3); + F32 lodf = ((F32)(lod + 1.0f)/4.f); + F32 tex_size = lodf * LLViewerImage::sMaxSculptRez ; + mSculptTexture->addTextureStats(2.f * tex_size * tex_size, FALSE); + + //if the sculpty very close to the view point, load first + { + LLVector3 lookAt = getPositionAgent() - LLViewerCamera::getInstance()->getOrigin(); + F32 dist = lookAt.normVec() ; + F32 cos_angle_to_view_dir = lookAt * LLViewerCamera::getInstance()->getXAxis() ; + mSculptTexture->setAdditionalDecodePriority(0.8f * LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist)) ; + } + } + + S32 texture_discard = mSculptTexture->getCachedRawImageLevel(); //try to match the texture + S32 current_discard = mSculptLevel; - if (texture_discard >= 0 && //texture has some data available - (texture_discard < current_discard || //texture has more data than last rebuild - current_discard < 0)) //no previous rebuild - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE); - mSculptChanged = TRUE; - } + if (texture_discard >= 0 && //texture has some data available + (texture_discard < current_discard || //texture has more data than last rebuild + current_discard < 0)) //no previous rebuild + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE); + mSculptChanged = TRUE; + } - if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED)) + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED)) { setDebugText(llformat("T%d C%d V%d\n%dx%d", - texture_discard, current_discard, getVolume()->getSculptLevel(), - mSculptTexture->getHeight(), mSculptTexture->getWidth())); + texture_discard, current_discard, getVolume()->getSculptLevel(), + mSculptTexture->getHeight(), mSculptTexture->getWidth())); } + } } @@ -538,10 +548,10 @@ void LLVOVolume::updateTextures() { setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize))); } - else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) - { - setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize))); - } +// else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) +// { +// setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize))); +// } else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA)) { setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize))); @@ -553,36 +563,6 @@ void LLVOVolume::updateTextures() } } -F32 LLVOVolume::getTextureVirtualSize(LLFace* face) -{ - //get area of circle around face - LLVector3 center = face->getPositionAgent(); - LLVector3 size = (face->mExtents[1] - face->mExtents[0]) * 0.5f; - - F32 face_area = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance()); - - face->setPixelArea(face_area); - - if (face_area <= 0) - { - return 0.f; - } - - //get area of circle in texture space - LLVector2 tdim = face->mTexExtents[1] - face->mTexExtents[0]; - F32 texel_area = (tdim * 0.5f).lengthSquared()*3.14159f; - if (texel_area <= 0) - { - // Probably animated, use default - texel_area = 1.f; - } - - //apply texel area to face area to get accurate ratio - face_area /= llclamp(texel_area, 1.f/64.f, 16.f); - - return face_area; -} - BOOL LLVOVolume::isActive() const { return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()); @@ -719,31 +699,21 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail // sculpt replaces generate() for sculpted surfaces void LLVOVolume::sculpt() -{ - U16 sculpt_height = 0; - U16 sculpt_width = 0; - S8 sculpt_components = 0; - const U8* sculpt_data = NULL; - +{ if (mSculptTexture.notNull()) - { - S32 discard_level; - S32 desired_discard = 0; // lower discard levels have MUCH less resolution - - discard_level = desired_discard; + { + U16 sculpt_height = 0; + U16 sculpt_width = 0; + S8 sculpt_components = 0; + const U8* sculpt_data = NULL; + + S32 discard_level = mSculptTexture->getCachedRawImageLevel() ; + LLImageRaw* raw_image = mSculptTexture->getCachedRawImage() ; S32 max_discard = mSculptTexture->getMaxDiscardLevel(); if (discard_level > max_discard) discard_level = max_discard; // clamp to the best we can do - S32 best_discard = mSculptTexture->getDiscardLevel(); - if (discard_level < best_discard) - discard_level = best_discard; // clamp to what we have - - if (best_discard == -1) - discard_level = -1; // and if we have nothing, set to nothing - - S32 current_discard = getVolume()->getSculptLevel(); if(current_discard < -2) { @@ -765,28 +735,17 @@ void LLVOVolume::sculpt() if (current_discard == discard_level) // no work to do here return; - LLPointer raw_image = new LLImageRaw(); - BOOL is_valid = mSculptTexture->readBackRaw(discard_level, raw_image, FALSE); - - sculpt_height = raw_image->getHeight(); - sculpt_width = raw_image->getWidth(); - sculpt_components = raw_image->getComponents(); - - if(is_valid) - { - is_valid = mSculptTexture->isValidForSculpt(discard_level, sculpt_width, sculpt_height, sculpt_components) ; - } - if(!is_valid) + if(!raw_image) { sculpt_width = 0; sculpt_height = 0; sculpt_data = NULL ; } else - { - if (raw_image->getDataSize() < sculpt_height * sculpt_width * sculpt_components) - llerrs << "Sculpt: image data size = " << raw_image->getDataSize() - << " < " << sculpt_height << " x " << sculpt_width << " x " <getHeight(); + sculpt_width = raw_image->getWidth(); + sculpt_components = raw_image->getComponents(); sculpt_data = raw_image->getData(); } @@ -818,7 +777,7 @@ BOOL LLVOVolume::calcLOD() } //update face texture sizes on lod calculation - updateTextures(); + updateTextureVirtualSize(); S32 cur_detail = 0; @@ -2295,7 +2254,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) LLVOVolume* vobj = drawablep->getVOVolume(); llassert_always(vobj); - vobj->updateTextures(); + vobj->updateTextureVirtualSize(); vobj->preRebuild(); //for each face @@ -2399,7 +2358,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { //needs normal + binormal bump_faces.push_back(facep); } - else if (te->getShiny() && LLPipeline::sRenderBump || + else if ((te->getShiny() && LLPipeline::sRenderBump) || !te->getFullbright()) { //needs normal simple_faces.push_back(facep); diff --git a/linden/indra/newview/llvovolume.h b/linden/indra/newview/llvovolume.h index 9b4e715..960f6da 100644 --- a/linden/indra/newview/llvovolume.h +++ b/linden/indra/newview/llvovolume.h @@ -130,7 +130,7 @@ public: BOOL getVolumeChanged() const { return mVolumeChanged; } - F32 getTextureVirtualSize(LLFace* face); + /*virtual*/ F32 getRadius() const { return mVObjRadius; }; const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const; @@ -177,8 +177,8 @@ public: /*virtual*/ void updateFaceSize(S32 idx); /*virtual*/ BOOL updateLOD(); void updateRadius(); - /*virtual*/ void updateTextures(LLAgent &agent); - void updateTextures(); + /*virtual*/ void updateTextures(); + void updateTextureVirtualSize(); void updateFaceFlags(); void regenFaces(); diff --git a/linden/indra/newview/llvowater.cpp b/linden/indra/newview/llvowater.cpp index d23c746..5b6a949 100644 --- a/linden/indra/newview/llvowater.cpp +++ b/linden/indra/newview/llvowater.cpp @@ -101,7 +101,7 @@ void LLVOWater::setPixelAreaAndAngle(LLAgent &agent) // virtual -void LLVOWater::updateTextures(LLAgent &agent) +void LLVOWater::updateTextures() { } diff --git a/linden/indra/newview/llvowater.h b/linden/indra/newview/llvowater.h index cdda48f..9c33e74 100644 --- a/linden/indra/newview/llvowater.h +++ b/linden/indra/newview/llvowater.h @@ -68,7 +68,7 @@ public: /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area virtual U32 getPartitionType() const; diff --git a/linden/indra/newview/llworldmapview.cpp b/linden/indra/newview/llworldmapview.cpp index 64e84c5..c10faa5 100644 --- a/linden/indra/newview/llworldmapview.cpp +++ b/linden/indra/newview/llworldmapview.cpp @@ -378,7 +378,7 @@ void LLWorldMapView::draw() continue; } - current_image->setBoostLevel(LLViewerImage::BOOST_MAP_LAYER); + current_image->setBoostLevel(LLViewerImageBoostLevel::BOOST_MAP_VISIBLE); current_image->setKnownDrawSize(llround(pix_width * LLUI::sGLScaleFactor.mV[VX]), llround(pix_height * LLUI::sGLScaleFactor.mV[VY])); if (!current_image->getHasGLTexture()) @@ -557,13 +557,13 @@ void LLWorldMapView::draw() S32 draw_size = llround(gMapScale); if (simimage != NULL) { - simimage->setBoostLevel(LLViewerImage::BOOST_MAP); + simimage->setBoostLevel(LLViewerImageBoostLevel::BOOST_MAP); simimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY])); } if (overlayimage != NULL) { - overlayimage->setBoostLevel(LLViewerImage::BOOST_MAP); + overlayimage->setBoostLevel(LLViewerImageBoostLevel::BOOST_MAP); overlayimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY])); } diff --git a/linden/indra/newview/primbackup.cpp b/linden/indra/newview/primbackup.cpp index 4f93ddf..a9a8af7 100644 --- a/linden/indra/newview/primbackup.cpp +++ b/linden/indra/newview/primbackup.cpp @@ -590,8 +590,8 @@ void primbackup::export_next_texture() S32 cur_discard = imagep->getDiscardLevel(); if(cur_discard>0) { - if(imagep->getBoostLevel()!=LLViewerImage::BOOST_PREVIEW) - imagep->setBoostLevel(LLViewerImage::BOOST_PREVIEW); //we want to force discard 0 this one does this. + if(imagep->getBoostLevel()!=LLViewerImageBoostLevel::BOOST_PREVIEW) + imagep->setBoostLevel(LLViewerImageBoostLevel::BOOST_PREVIEW); //we want to force discard 0 this one does this. } else { -- cgit v1.1