diff options
Diffstat (limited to 'indra/llimage')
| -rw-r--r-- | indra/llimage/llimage.cpp | 51 | ||||
| -rw-r--r-- | indra/llimage/llimage.h | 6 | ||||
| -rw-r--r-- | indra/llimage/llimagej2c.cpp | 2 | ||||
| -rw-r--r-- | indra/llimage/llimageworker.cpp | 122 | ||||
| -rw-r--r-- | indra/llimage/llimageworker.h | 65 | ||||
| -rw-r--r-- | indra/llimage/tests/llimageworker_test.cpp | 99 | 
6 files changed, 136 insertions, 209 deletions
| diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 0fa027c9c3..031471d1fe 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -798,7 +798,6 @@ U8* LLImageBase::allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 si  // LLImageRaw  //--------------------------------------------------------------------------- -S32 LLImageRaw::sGlobalRawMemory = 0;  S32 LLImageRaw::sRawImageCount = 0;  LLImageRaw::LLImageRaw() @@ -815,6 +814,15 @@ LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components)  	++sRawImageCount;  } +LLImageRaw::LLImageRaw(const U8* data, U16 width, U16 height, S8 components) +    : LLImageBase() +{ +    if (allocateDataSize(width, height, components)) +    { +        memcpy(getData(), data, width * height * components); +    } +} +  LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy)  	: LLImageBase()  { @@ -847,16 +855,13 @@ LLImageRaw::~LLImageRaw()  U8* LLImageRaw::allocateData(S32 size)  {  	U8* res = LLImageBase::allocateData(size); -	sGlobalRawMemory += getDataSize();  	return res;  }  // virtual  U8* LLImageRaw::reallocateData(S32 size)  { -	sGlobalRawMemory -= getDataSize();  	U8* res = LLImageBase::reallocateData(size); -	sGlobalRawMemory += getDataSize();  	return res;  } @@ -869,7 +874,6 @@ void LLImageRaw::releaseData()  // virtual  void LLImageRaw::deleteData()  { -	sGlobalRawMemory -= getDataSize();  	LLImageBase::deleteData();  } @@ -985,6 +989,43 @@ void LLImageRaw::verticalFlip()  } +bool LLImageRaw::optimizeAwayAlpha() +{ +    if (getComponents() == 4) +    { +        U8* data = getData(); +        U32 pixels = getWidth() * getHeight(); + +        // check alpha channel for all 255 +        for (U32 i = 0; i < pixels; ++i) +        { +            if (data[i * 4 + 3] != 255) +            { +                return false; +            } +        } + +        // alpha channel is all 255, make a new copy of data without alpha channel +        U8* new_data = (U8*) ll_aligned_malloc_16(getWidth() * getHeight() * 3); + +        for (U32 i = 0; i < pixels; ++i) +        { +            U32 di = i * 3; +            U32 si = i * 4; +            for (U32 j = 0; j < 3; ++j) +            { +                new_data[di+j] = data[si+j]; +            } +        } + +        setDataAndSize(new_data, getWidth(), getHeight(), 3); + +        return true; +    } + +    return false; +} +  void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image)  {  	// Find new sizes diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 7a588cfb03..8f9e1b3c54 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -184,6 +184,7 @@ protected:  public:  	LLImageRaw();  	LLImageRaw(U16 width, U16 height, S8 components); +    LLImageRaw(const U8* data, U16 width, U16 height, S8 components);  	LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy = false);  	// Construct using createFromFile (used by tools)  	//LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false); @@ -207,6 +208,10 @@ public:  	void clear(U8 r=0, U8 g=0, U8 b=0, U8 a=255);  	void verticalFlip(); +     +    // if the alpha channel is all 100% opaque, delete it +    // returns true if alpha channel was deleted +    bool optimizeAwayAlpha();      static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);      static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE); @@ -275,7 +280,6 @@ protected:  	void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;  public: -	static S32 sGlobalRawMemory;  	static S32 sRawImageCount;  private: diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index e1809dbe59..8dba1641a6 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -146,6 +146,7 @@ bool LLImageJ2C::initEncode(LLImageRaw &raw_image, int blocks_size, int precinct  bool LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)  { +    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;  	return decodeChannels(raw_imagep, decode_time, 0, 4);  } @@ -153,6 +154,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 )  { +    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;  	LLTimer elapsed;  	bool res = true; diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp index 0dbb744bcf..0093958e6d 100644 --- a/indra/llimage/llimageworker.cpp +++ b/indra/llimage/llimageworker.cpp @@ -28,64 +28,88 @@  #include "llimageworker.h"  #include "llimagedxt.h" +#include "threadpool.h" + +/*--------------------------------------------------------------------------*/ +class ImageRequest +{ +public: +	ImageRequest(const LLPointer<LLImageFormatted>& image, +				 S32 discard, BOOL needs_aux, +				 const LLPointer<LLImageDecodeThread::Responder>& responder); +	virtual ~ImageRequest(); + +	/*virtual*/ bool processRequest(); +	/*virtual*/ void finishRequest(bool completed); + +private: +	// LLPointers stored in ImageRequest MUST be LLPointer instances rather +	// than references: we need to increment the refcount when storing these. +	// input +	LLPointer<LLImageFormatted> mFormattedImage; +	S32 mDiscardLevel; +	BOOL mNeedsAux; +	// output +	LLPointer<LLImageRaw> mDecodedImageRaw; +	LLPointer<LLImageRaw> mDecodedImageAux; +	BOOL mDecodedRaw; +	BOOL mDecodedAux; +	LLPointer<LLImageDecodeThread::Responder> mResponder; +}; +  //----------------------------------------------------------------------------  // MAIN THREAD -LLImageDecodeThread::LLImageDecodeThread(bool threaded) -	: LLQueuedThread("imagedecode", threaded) +LLImageDecodeThread::LLImageDecodeThread(bool /*threaded*/)  { -	mCreationMutex = new LLMutex(); +    mThreadPool.reset(new LL::ThreadPool("ImageDecode", 8)); +    mThreadPool->start();  }  //virtual   LLImageDecodeThread::~LLImageDecodeThread() -{ -	delete mCreationMutex ; -} +{}  // MAIN THREAD  // virtual  S32 LLImageDecodeThread::update(F32 max_time_ms)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	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); +    return getPending(); +} -		bool res = addRequest(req); -		if (!res) -		{ -			LL_ERRS() << "request added after LLLFSThread::cleanupClass()" << LL_ENDL; -		} -	} -	mCreationList.clear(); -	S32 res = LLQueuedThread::update(max_time_ms); -	return res; +S32 LLImageDecodeThread::getPending() +{ +    return mThreadPool->getQueue().size();  } -LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image,  -	U32 priority, S32 discard, BOOL needs_aux, Responder* responder) +LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage( +    const LLPointer<LLImageFormatted>& image,  +    S32 discard, +    BOOL needs_aux, +    const LLPointer<LLImageDecodeThread::Responder>& responder)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	LLMutexLock lock(mCreationMutex); -	handle_t handle = generateHandle(); -	mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder)); -	return handle; + +    // Instantiate the ImageRequest right in the lambda, why not? +    mThreadPool->getQueue().post( +        [req = ImageRequest(image, discard, needs_aux, responder)] +        () mutable +        { +            auto done = req.processRequest(); +            req.finishRequest(done); +        }); + +    // It's important to our consumer (LLTextureFetchWorker) that we return a +    // nonzero handle. It is NOT important that the nonzero handle be unique: +    // nothing is ever done with it except to compare it to zero, or zero it. +    return 17;  } -// Used by unit test only -// Returns the size of the mutex guarded list as an indication of sanity -S32 LLImageDecodeThread::tut_size() +void LLImageDecodeThread::shutdown()  { -	LLMutexLock lock(mCreationMutex); -	S32 res = mCreationList.size(); -	return res; +    mThreadPool->close();  }  LLImageDecodeThread::Responder::~Responder() @@ -94,11 +118,10 @@ LLImageDecodeThread::Responder::~Responder()  //---------------------------------------------------------------------------- -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), +ImageRequest::ImageRequest(const LLPointer<LLImageFormatted>& image,  +							S32 discard, BOOL needs_aux, +							const LLPointer<LLImageDecodeThread::Responder>& responder) +	: mFormattedImage(image),  	  mDiscardLevel(discard),  	  mNeedsAux(needs_aux),  	  mDecodedRaw(FALSE), @@ -107,7 +130,7 @@ LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatte  {  } -LLImageDecodeThread::ImageRequest::~ImageRequest() +ImageRequest::~ImageRequest()  {  	mDecodedImageRaw = NULL;  	mDecodedImageAux = NULL; @@ -118,10 +141,10 @@ LLImageDecodeThread::ImageRequest::~ImageRequest()  // Returns true when done, whether or not decode was successful. -bool LLImageDecodeThread::ImageRequest::processRequest() +bool ImageRequest::processRequest()  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	const F32 decode_time_slice = .1f; +	const F32 decode_time_slice = 0.f; //disable time slicing  	bool done = true;  	if (!mDecodedRaw && mFormattedImage.notNull())  	{ @@ -145,7 +168,7 @@ bool LLImageDecodeThread::ImageRequest::processRequest()  											  mFormattedImage->getHeight(),  											  mFormattedImage->getComponents());  		} -		done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms +		done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice);  		// some decoders are removing data when task is complete and there were errors  		mDecodedRaw = done && mDecodedImageRaw->getData();  	} @@ -158,14 +181,14 @@ bool LLImageDecodeThread::ImageRequest::processRequest()  											  mFormattedImage->getHeight(),  											  1);  		} -		done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms +		done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4);  		mDecodedAux = done && mDecodedImageAux->getData();  	}  	return done;  } -void LLImageDecodeThread::ImageRequest::finishRequest(bool completed) +void ImageRequest::finishRequest(bool completed)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;  	if (mResponder.notNull()) @@ -175,10 +198,3 @@ void LLImageDecodeThread::ImageRequest::finishRequest(bool completed)  	}  	// Will automatically be deleted  } - -// Used by unit test only -// Checks that a responder exists for this instance so that something can happen when completion is reached -bool LLImageDecodeThread::ImageRequest::tut_isOK() -{ -	return mResponder.notNull(); -} diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h index 1bfb0ddfd3..fb35108a7a 100644 --- a/indra/llimage/llimageworker.h +++ b/indra/llimage/llimageworker.h @@ -29,9 +29,9 @@  #include "llimage.h"  #include "llpointer.h" -#include "llworkerthread.h" +#include "threadpool_fwd.h" -class LLImageDecodeThread : public LLQueuedThread +class LLImageDecodeThread  {  public:  	class Responder : public LLThreadSafeRefCount @@ -42,63 +42,24 @@ public:  		virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) = 0;  	}; -	class ImageRequest : public LLQueuedThread::QueuedRequest -	{ -	protected: -		virtual ~ImageRequest(); // use deleteRequest() -		 -	public: -		ImageRequest(handle_t handle, LLImageFormatted* image, -					 U32 priority, S32 discard, BOOL needs_aux, -					 LLImageDecodeThread::Responder* responder); - -		/*virtual*/ bool processRequest(); -		/*virtual*/ void finishRequest(bool completed); - -		// Used by unit tests to check the consitency of the request instance -		bool tut_isOK(); -		 -	private: -		// input -		LLPointer<LLImageFormatted> mFormattedImage; -		S32 mDiscardLevel; -		BOOL mNeedsAux; -		// output -		LLPointer<LLImageRaw> mDecodedImageRaw; -		LLPointer<LLImageRaw> mDecodedImageAux; -		BOOL mDecodedRaw; -		BOOL mDecodedAux; -		LLPointer<LLImageDecodeThread::Responder> mResponder; -	}; -	  public:  	LLImageDecodeThread(bool threaded = true);  	virtual ~LLImageDecodeThread(); -	handle_t decodeImage(LLImageFormatted* image, -						 U32 priority, S32 discard, BOOL needs_aux, -						 Responder* responder); +	// meant to resemble LLQueuedThread::handle_t +	typedef U32 handle_t; +	handle_t decodeImage(const LLPointer<LLImageFormatted>& image, +						 S32 discard, BOOL needs_aux, +						 const LLPointer<Responder>& responder); +	S32 getPending();  	S32 update(F32 max_time_ms); +	void shutdown(); -	// Used by unit tests to check the consistency of the thread instance -	S32 tut_size(); -	  private: -	struct creation_info -	{ -		handle_t handle; -		LLPointer<LLImageFormatted> image; -		U32 priority; -		S32 discard; -		BOOL needs_aux; -		LLPointer<Responder> responder; -		creation_info(handle_t h, LLImageFormatted* i, U32 p, S32 d, BOOL aux, Responder* r) -			: handle(h), image(i), priority(p), discard(d), needs_aux(aux), responder(r) -		{} -	}; -	typedef std::list<creation_info> creation_list_t; -	creation_list_t mCreationList; -	LLMutex* mCreationMutex; +	// As of SL-17483, LLImageDecodeThread is no longer itself an +	// LLQueuedThread - instead this is the API by which we submit work to the +	// "ImageDecode" ThreadPool. +	std::unique_ptr<LL::ThreadPool> mThreadPool;  };  #endif diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index 9011ac615c..0a97b739b0 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -125,42 +125,11 @@ namespace tut  		}  	}; -	// Test wrapper declaration : image worker -	// Note: this class is not meant to be instantiated outside an LLImageDecodeThread instance -	// but it's not a bad idea to get its public API a good shake as part of a thorough unit test set. -	// Some gotcha with the destructor though (see below). -	struct imagerequest_test -	{ -		// Instance to be tested -		LLImageDecodeThread::ImageRequest* mRequest; -		bool done; - -		// Constructor and destructor of the test wrapper -		imagerequest_test() -		{ -			done = false; - -			mRequest = new LLImageDecodeThread::ImageRequest(0, 0, -											 LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, -											 new responder_test(&done)); -		} -		~imagerequest_test() -		{ -			// We should delete the object *but*, because its destructor is protected, that cannot be -			// done from outside an LLImageDecodeThread instance... So we leak memory here... It's fine... -			//delete mRequest; -		} -	}; -  	// Tut templating thingamagic: test group, object and test instance  	typedef test_group<imagedecodethread_test> imagedecodethread_t;  	typedef imagedecodethread_t::object imagedecodethread_object_t;  	tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread"); -	typedef test_group<imagerequest_test> imagerequest_t; -	typedef imagerequest_t::object imagerequest_object_t; -	tut::imagerequest_t tut_imagerequest("LLImageRequest"); -  	// ---------------------------------------------------------------------------------------  	// Test functions  	// Notes: @@ -172,64 +141,18 @@ namespace tut  	// ---------------------------------------------------------------------------------------  	// Test the LLImageDecodeThread interface  	// --------------------------------------------------------------------------------------- -	// -	// Note on Unit Testing Queued Thread Classes -	// -	// Since methods on such a class are called on a separate loop and that we can't insert tut -	// ensure() calls in there, we exercise the class with 2 sets of tests: -	// - 1: Test as a single threaded instance: We declare the class but ask for no thread -	//   to be spawned (easy with LLThreads since there's a boolean argument on the constructor -	//   just for that). We can then unit test each public method like we do on a normal class. -	// - 2: Test as a threaded instance: We let the thread launch and check that its external  -	//   behavior is as expected (i.e. it runs, can accept a work order and processes -	//   it). Typically though there's no guarantee that this exercises all the methods of the -	//   class which is why we also need the previous "non threaded" set of unit tests for -	//   complete coverage. -	// -	// ---------------------------------------------------------------------------------------  	template<> template<>  	void imagedecodethread_object_t::test<1>()  	{ -		// Test a *non threaded* instance of the class -		mThread = new LLImageDecodeThread(false); -		ensure("LLImageDecodeThread: non threaded constructor failed", mThread != NULL); -		// Test that we start with an empty list right at creation -		ensure("LLImageDecodeThread: non threaded init state incorrect", mThread->tut_size() == 0); -		// Insert something in the queue -		bool done = false; -		LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done)); -		// Verifies we got a valid handle -		ensure("LLImageDecodeThread: non threaded decodeImage(), returned handle is null", decodeHandle != 0); -		// Verifies that we do now have something in the queued list -		ensure("LLImageDecodeThread: non threaded decodeImage() insertion in threaded list failed", mThread->tut_size() == 1); -		// Trigger queue handling "manually" (on a threaded instance, this is done on the thread loop) -		S32 res = mThread->update(0); -		// Verifies that we successfully handled the list -		ensure("LLImageDecodeThread: non threaded update() list handling test failed", res == 0); -		// Verifies that the list is now empty -		ensure("LLImageDecodeThread: non threaded update() list emptying test failed", mThread->tut_size() == 0); -	} - -	template<> template<> -	void imagedecodethread_object_t::test<2>() -	{  		// Test a *threaded* instance of the class  		mThread = new LLImageDecodeThread(true);  		ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL); -		// Test that we start with an empty list right at creation -		ensure("LLImageDecodeThread: threaded init state incorrect", mThread->tut_size() == 0);  		// Insert something in the queue  		bool done = false; -		LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done)); +		LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, 0, FALSE, new responder_test(&done));  		// Verifies we get back a valid handle  		ensure("LLImageDecodeThread:  threaded decodeImage(), returned handle is null", decodeHandle != 0); -		// Wait a little so to simulate the main thread doing something on its main loop... -		ms_sleep(500);		// 500 milliseconds -		// Verifies that the responder has *not* been called yet in the meantime -		ensure("LLImageDecodeThread: responder creation failed", done == false); -		// Ask the thread to update: that means tells the queue to check itself and creates work requests -		mThread->update(1);  		// Wait till the thread has time to handle the work order (though it doesn't do much per work order...)  		const U32 INCREMENT_TIME = 500;				// 500 milliseconds  		const U32 MAX_TIME = 20 * INCREMENT_TIME;	// Do the loop 20 times max, i.e. wait 10 seconds but no more @@ -242,24 +165,4 @@ namespace tut  		// Verifies that the responder has now been called  		ensure("LLImageDecodeThread: threaded work unit not processed", done == true);  	} - -	// --------------------------------------------------------------------------------------- -	// Test the LLImageDecodeThread::ImageRequest interface -	// --------------------------------------------------------------------------------------- -	 -	template<> template<> -	void imagerequest_object_t::test<1>() -	{ -		// Test that we start with a correct request at creation -		ensure("LLImageDecodeThread::ImageRequest::ImageRequest() constructor test failed", mRequest->tut_isOK()); -		bool res = mRequest->processRequest(); -		// Verifies that we processed the request successfully -		ensure("LLImageDecodeThread::ImageRequest::processRequest() processing request test failed", res == true); -		// Check that we can call the finishing call safely -		try { -			mRequest->finishRequest(false); -		} catch (...) { -			fail("LLImageDecodeThread::ImageRequest::finishRequest() test failed"); -		} -	}  } | 
