phoenix-firestorm/indra/llimage/llimageworker.cpp

226 lines
6.7 KiB
C++

/**
* @file llimageworker.cpp
* @brief Base class for images.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#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,
U32 request_id);
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;
U32 mRequestId;
bool mNeedsAux;
// output
LLPointer<LLImageRaw> mDecodedImageRaw;
LLPointer<LLImageRaw> mDecodedImageAux;
bool mDecodedRaw;
bool mDecodedAux;
LLPointer<LLImageDecodeThread::Responder> mResponder;
std::string mErrorString;};
//----------------------------------------------------------------------------
// MAIN THREAD
LLImageDecodeThread::LLImageDecodeThread(bool /*threaded*/)
: mDecodeCount(0)
{
mThreadPool.reset(new LL::ThreadPool("ImageDecode", 8));
mThreadPool->start();
}
//virtual
LLImageDecodeThread::~LLImageDecodeThread()
{}
// MAIN THREAD
// virtual
size_t LLImageDecodeThread::update(F32 max_time_ms)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
return getPending();
}
size_t LLImageDecodeThread::getPending()
{
return mThreadPool->getQueue().size();
}
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;
U32 decode_id = ++mDecodeCount;
// Instantiate the ImageRequest right in the lambda, why not?
bool posted = mThreadPool->getQueue().post(
[req = ImageRequest(image, discard, needs_aux, responder, decode_id)]
() mutable
{
auto done = req.processRequest();
req.finishRequest(done);
});
if (! posted)
{
LL_DEBUGS() << "Tried to start decoding on shutdown" << LL_ENDL;
return 0;
}
return decode_id;
}
void LLImageDecodeThread::shutdown()
{
mThreadPool->close();
}
LLImageDecodeThread::Responder::~Responder()
{
}
//----------------------------------------------------------------------------
ImageRequest::ImageRequest(const LLPointer<LLImageFormatted>& image,
S32 discard,
bool needs_aux,
const LLPointer<LLImageDecodeThread::Responder>& responder,
U32 request_id)
: mFormattedImage(image),
mDiscardLevel(discard),
mNeedsAux(needs_aux),
mDecodedRaw(false),
mDecodedAux(false),
mResponder(responder),
mRequestId(request_id)
{
}
ImageRequest::~ImageRequest()
{
mDecodedImageRaw = NULL;
mDecodedImageAux = NULL;
mFormattedImage = NULL;
}
//----------------------------------------------------------------------------
// Returns true when done, whether or not decode was successful.
bool ImageRequest::processRequest()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
if (mFormattedImage.isNull())
return true;
const F32 decode_time_slice = 0.f; //disable time slicing
bool done = true;
LLImageDataLock lockFormatted(mFormattedImage);
LLImageDataLock lockDecodedRaw(mDecodedImageRaw);
LLImageDataLock lockDecodedAux(mDecodedImageAux);
if (!mDecodedRaw)
{
// Decode primary channels
if (mDecodedImageRaw.isNull())
{
// parse formatted header
if (!mFormattedImage->updateData())
{
return true; // done (failed)
}
if ((mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents()) == 0)
{
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);
// some decoders are removing data when task is complete and there were errors
mDecodedRaw = done && mDecodedImageRaw->getData();
// Pick up errors from decoding
mErrorString = LLImage::getLastThreadError();
}
if (done && mNeedsAux && !mDecodedAux && mFormattedImage.notNull())
{
// Decode aux channel
if (!mDecodedImageAux)
{
mDecodedImageAux = new LLImageRaw(mFormattedImage->getWidth(),
mFormattedImage->getHeight(),
1);
}
done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4);
mDecodedAux = done && mDecodedImageAux->getData();
// Pick up errors from decoding
mErrorString = LLImage::getLastThreadError();
}
return done;
}
void ImageRequest::finishRequest(bool completed)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
if (mResponder.notNull())
{
bool success = completed && mDecodedRaw && (!mNeedsAux || mDecodedAux);
mResponder->completed(success, mErrorString, mDecodedImageRaw, mDecodedImageAux, mRequestId);
}
// Will automatically be deleted
}