summaryrefslogtreecommitdiff
path: root/indra/llimage
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llimage')
-rw-r--r--indra/llimage/llimage.cpp51
-rw-r--r--indra/llimage/llimage.h6
-rw-r--r--indra/llimage/llimagej2c.cpp2
-rw-r--r--indra/llimage/llimageworker.cpp127
-rw-r--r--indra/llimage/llimageworker.h65
-rw-r--r--indra/llimage/tests/llimageworker_test.cpp99
6 files changed, 141 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 ff4336d2de..c1ee052997 100644
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -28,64 +28,93 @@
#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
size_t 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;
+size_t 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?
+ bool posted = mThreadPool->getQueue().post(
+ [req = ImageRequest(image, discard, needs_aux, responder)]
+ () mutable
+ {
+ auto done = req.processRequest();
+ req.finishRequest(done);
+ });
+ if (! posted)
+ {
+ LL_DEBUGS() << "Tried to start decoding on shutdown" << LL_ENDL;
+ // should this return 0?
+ }
+
+ // 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 +123,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 +135,7 @@ LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatte
{
}
-LLImageDecodeThread::ImageRequest::~ImageRequest()
+ImageRequest::~ImageRequest()
{
mDecodedImageRaw = NULL;
mDecodedImageAux = NULL;
@@ -118,10 +146,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 +173,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 +186,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 +203,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 54814c24c5..ca4c0d93d0 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);
+ size_t getPending();
size_t 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");
- }
- }
}