SH-3060 : Implement new byte range computation, cleaned up use of compression rate as well.
parent
782981866a
commit
91094d92a7
|
|
@ -1337,8 +1337,7 @@ LLImageFormatted::LLImageFormatted(S8 codec)
|
|||
mDecoding(0),
|
||||
mDecoded(0),
|
||||
mDiscardLevel(-1),
|
||||
mLevels(0),
|
||||
mLayers(0)
|
||||
mLevels(0)
|
||||
{
|
||||
mMemType = LLMemType::MTYPE_IMAGEFORMATTED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 *
|
|||
// *TODO: change both to 1024 when SIM texture fetching is deprecated
|
||||
const S32 FIRST_PACKET_SIZE = 600;
|
||||
const S32 MAX_IMG_PACKET_SIZE = 1000;
|
||||
const S32 HTTP_PACKET_SIZE = 1496;
|
||||
|
||||
// Base classes for images.
|
||||
// There are two major parts for the image:
|
||||
|
|
@ -320,8 +321,6 @@ public:
|
|||
S8 getDiscardLevel() const { return mDiscardLevel; }
|
||||
S8 getLevels() const { return mLevels; }
|
||||
void setLevels(S8 nlevels) { mLevels = nlevels; }
|
||||
S32 getLayers() const { return mLayers; }
|
||||
void setLayers(S32 nlayers) { mLayers = nlayers; }
|
||||
|
||||
// setLastError needs to be deferred for J2C images since it may be called from a DLL
|
||||
virtual void resetLastError();
|
||||
|
|
@ -336,7 +335,6 @@ protected:
|
|||
S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
|
||||
S8 mDiscardLevel; // Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc...
|
||||
S8 mLevels; // Number of resolution levels in that image. Min is 1. 0 means unknown.
|
||||
S32 mLayers; // Number of quality layers in that image. Min is 1. 0 means unknown.
|
||||
|
||||
public:
|
||||
static S32 sGlobalFormattedMemory;
|
||||
|
|
|
|||
|
|
@ -56,10 +56,9 @@ std::string LLImageJ2C::getEngineInfo()
|
|||
LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C),
|
||||
mMaxBytes(0),
|
||||
mRawDiscardLevel(-1),
|
||||
mRate(0.0f),
|
||||
mRate(DEFAULT_COMPRESSION_RATE),
|
||||
mReversible(FALSE),
|
||||
mAreaUsedForDataSizeCalcs(0),
|
||||
mLayersUsedForDataSizeCalcs(0)
|
||||
mAreaUsedForDataSizeCalcs(0)
|
||||
{
|
||||
mImpl = fallbackCreateLLImageJ2CImpl();
|
||||
|
||||
|
|
@ -257,33 +256,40 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text,
|
|||
//static
|
||||
S32 LLImageJ2C::calcHeaderSizeJ2C()
|
||||
{
|
||||
return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size...
|
||||
return HTTP_PACKET_SIZE; // Hack. just needs to be >= actual header size...
|
||||
}
|
||||
|
||||
//static
|
||||
S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, S32 nb_layers, F32 rate)
|
||||
S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
|
||||
{
|
||||
// Note: This provides an estimation for the first quality layer of a given discard level
|
||||
// Note: This provides an estimation for the first to last quality layer of a given discard level
|
||||
// This is however an efficient approximation, as the true discard level boundary would be
|
||||
// in general too big for fast fetching.
|
||||
// For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study
|
||||
if (rate <= 0.f) rate = 1.f/8.f;
|
||||
// Compute w/pow(2,discard_level) and h/pow(2,discard_level)
|
||||
while (discard_level > 0)
|
||||
|
||||
// Estimate the number of layers. This is consistent with what's done in j2c encoding
|
||||
S32 nb_layers = 1;
|
||||
S32 surface = w*h;
|
||||
S32 s = 64*64;
|
||||
while (surface > s)
|
||||
{
|
||||
if (w < 1 || h < 1)
|
||||
break;
|
||||
w >>= 1;
|
||||
h >>= 1;
|
||||
discard_level--;
|
||||
nb_layers++;
|
||||
s *= 4;
|
||||
}
|
||||
F32 layer_factor = 3.0f * (7 - llclamp(nb_layers,1,6));
|
||||
|
||||
// Compute w/pow(2,discard_level) and h/pow(2,discard_level)
|
||||
w >>= discard_level;
|
||||
h >>= discard_level;
|
||||
w = llmax(w, 1);
|
||||
h = llmax(h, 1);
|
||||
|
||||
// Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange
|
||||
// *TODO: Take the old code out once we have enough tests done
|
||||
S32 bytes;
|
||||
F32 layer_factor = ((nb_layers > 0) && (nb_layers < 7) ? 3.0f * (7 - nb_layers): 3.0f);
|
||||
S32 new_bytes = sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor;
|
||||
S32 old_bytes = (S32)((F32)(w*h*comp)*rate);
|
||||
llinfos << "Merov debug : calcDataSizeJ2C, layers = " << nb_layers << ", old = " << old_bytes << ", new = " << new_bytes << llendl;
|
||||
//llinfos << "Merov debug : w = " << w << ", h = " << h << ", c = " << comp << ", r = " << rate << ", d = " << discard_level << ", l = " << nb_layers << ", old = " << old_bytes << ", new = " << new_bytes << llendl;
|
||||
bytes = (LLImage::useNewByteRange() ? new_bytes : old_bytes);
|
||||
bytes = llmax(bytes, calcHeaderSizeJ2C());
|
||||
return bytes;
|
||||
|
|
@ -298,26 +304,22 @@ S32 LLImageJ2C::calcHeaderSize()
|
|||
S32 LLImageJ2C::calcDataSize(S32 discard_level)
|
||||
{
|
||||
discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL);
|
||||
|
||||
return calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), discard_level, mRate);
|
||||
/*
|
||||
if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth())
|
||||
|| (mLayersUsedForDataSizeCalcs != getLayers())
|
||||
|| (mDataSizes[0] == 0))
|
||||
{
|
||||
if (mLayersUsedForDataSizeCalcs != getLayers())
|
||||
{
|
||||
llinfos << "Merov debug : recomputing data size because " << mLayersUsedForDataSizeCalcs << " != " << getLayers() << llendl;
|
||||
}
|
||||
mAreaUsedForDataSizeCalcs = getHeight() * getWidth();
|
||||
mLayersUsedForDataSizeCalcs = getLayers();
|
||||
|
||||
S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard
|
||||
while ( level >= 0 )
|
||||
{
|
||||
mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, getLayers(), mRate);
|
||||
mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
|
||||
level--;
|
||||
}
|
||||
}
|
||||
return mDataSizes[discard_level];
|
||||
*/
|
||||
}
|
||||
|
||||
S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
|
||||
|
|
@ -344,11 +346,6 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
|
|||
return discard_level;
|
||||
}
|
||||
|
||||
void LLImageJ2C::setRate(F32 rate)
|
||||
{
|
||||
mRate = rate;
|
||||
}
|
||||
|
||||
void LLImageJ2C::setMaxBytes(S32 max_bytes)
|
||||
{
|
||||
mMaxBytes = max_bytes;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
#include "llassettype.h"
|
||||
#include "llmetricperformancetester.h"
|
||||
|
||||
// JPEG2000 : compression rate used in j2c conversion.
|
||||
const F32 DEFAULT_COMPRESSION_RATE = 1.f/8.f;
|
||||
|
||||
class LLImageJ2CImpl;
|
||||
class LLImageCompressionTester ;
|
||||
|
||||
|
|
@ -67,12 +70,11 @@ public:
|
|||
|
||||
// Encode accessors
|
||||
void setReversible(const BOOL reversible); // Use non-lossy?
|
||||
void setRate(F32 rate);
|
||||
void setMaxBytes(S32 max_bytes);
|
||||
S32 getMaxBytes() const { return mMaxBytes; }
|
||||
|
||||
static S32 calcHeaderSizeJ2C();
|
||||
static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, S32 nb_layers = 0, F32 rate = 0.f);
|
||||
static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = DEFAULT_COMPRESSION_RATE);
|
||||
|
||||
static std::string getEngineInfo();
|
||||
|
||||
|
|
@ -88,7 +90,6 @@ protected:
|
|||
|
||||
S32 mDataSizes[MAX_DISCARD_LEVEL+1]; // Size of data required to reach a given level
|
||||
U32 mAreaUsedForDataSizeCalcs; // Height * width used to calculate mDataSizes
|
||||
S32 mLayersUsedForDataSizeCalcs; // Numbers of layers used to calculate mDataSizes
|
||||
|
||||
S8 mRawDiscardLevel;
|
||||
F32 mRate;
|
||||
|
|
|
|||
|
|
@ -195,8 +195,7 @@ mRawImagep(NULL),
|
|||
mDecodeState(NULL),
|
||||
mBlocksSize(-1),
|
||||
mPrecinctsSize(-1),
|
||||
mLevels(0),
|
||||
mLayers(0)
|
||||
mLevels(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -472,13 +471,6 @@ BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
|
|||
{
|
||||
kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos);
|
||||
|
||||
int layers = mCodeStreamp->get_max_tile_layers();
|
||||
if (layers > mLayers)
|
||||
{
|
||||
mLayers = layers;
|
||||
base.setLayers(mLayers);
|
||||
}
|
||||
|
||||
// Find the region of the buffer occupied by this
|
||||
// tile. Note that we have no control over
|
||||
// sub-sampling factors which might have been used
|
||||
|
|
@ -608,21 +600,14 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
|
|||
}
|
||||
|
||||
// Set codestream options
|
||||
mLayers = 0;
|
||||
int nb_layers = 0;
|
||||
kdu_long layer_bytes[MAX_NB_LAYERS];
|
||||
U32 max_bytes = (U32)(base.getWidth() * base.getHeight() * base.getComponents());
|
||||
|
||||
// Rate is the argument passed into the LLImageJ2C which
|
||||
// specifies the target compression rate. The default is 8:1.
|
||||
// *TODO: mRate is actually always 8:1 in the viewer. Test different values. Also force to reversible for small (< 500 bytes) textures.
|
||||
if (base.mRate != 0.f)
|
||||
{
|
||||
max_bytes = (U32)((F32)(max_bytes) * base.mRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
max_bytes = (U32)((F32)(max_bytes) / 8.0f);
|
||||
}
|
||||
// Rate is the argument passed into the LLImageJ2C which specifies the target compression rate. The default is 8:1.
|
||||
// *TODO: mRate is actually always 8:1 in the viewer. Test different values.
|
||||
llassert (base.mRate > 0.f);
|
||||
max_bytes = (U32)((F32)(max_bytes) * base.mRate);
|
||||
|
||||
// If the image is very small, code it in a lossless way.
|
||||
// Note: it'll also have only 1 layer which is fine as there's no point reordering blocks in that case.
|
||||
|
|
@ -635,15 +620,15 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
|
|||
// We're using a logarithmic spacing rule that fits with our way of fetching texture data.
|
||||
// Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h
|
||||
U32 i = FIRST_PACKET_SIZE;
|
||||
while ((i < max_bytes) && (mLayers < (MAX_NB_LAYERS-1)))
|
||||
while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1)))
|
||||
{
|
||||
if (i == FIRST_PACKET_SIZE * 4)
|
||||
{
|
||||
// That really just means that the first layer is FIRST_PACKET_SIZE and the second is MIN_LAYER_SIZE
|
||||
i = MIN_LAYER_SIZE;
|
||||
}
|
||||
layer_bytes[mLayers] = i;
|
||||
mLayers++;
|
||||
layer_bytes[nb_layers] = i;
|
||||
nb_layers++;
|
||||
i *= 4;
|
||||
}
|
||||
|
||||
|
|
@ -656,18 +641,17 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
|
|||
//codestream.access_siz()->parse_string("Cycc=no");
|
||||
// In the reversible case, set the last entry of that table to 0 so that all generated bits will
|
||||
// indeed be output by the time the last quality layer is encountered.
|
||||
layer_bytes[mLayers] = 0;
|
||||
layer_bytes[nb_layers] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Truncate the last quality layer if necessary so to fit the set compression ratio
|
||||
layer_bytes[mLayers] = max_bytes;
|
||||
layer_bytes[nb_layers] = max_bytes;
|
||||
}
|
||||
mLayers++;
|
||||
nb_layers++;
|
||||
|
||||
std::string layer_string = llformat("Clayers=%d",mLayers);
|
||||
std::string layer_string = llformat("Clayers=%d",nb_layers);
|
||||
codestream.access_siz()->parse_string(layer_string.c_str());
|
||||
base.setLayers(mLayers);
|
||||
|
||||
// Set up data ordering, markers, etc... if precincts or blocks specified
|
||||
// Note: This code is *not* used in the encoding made by the viewer. It is currently used only
|
||||
|
|
@ -720,7 +704,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
|
|||
}
|
||||
|
||||
// Produce the compressed output
|
||||
codestream.flush(layer_bytes,mLayers);
|
||||
codestream.flush(layer_bytes,nb_layers);
|
||||
|
||||
// Cleanup
|
||||
delete tile;
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ private:
|
|||
int mBlocksSize;
|
||||
int mPrecinctsSize;
|
||||
int mLevels;
|
||||
int mLayers;
|
||||
|
||||
// Temporary variables for in-progress decodes...
|
||||
LLImageRaw *mRawImagep;
|
||||
|
|
|
|||
|
|
@ -497,7 +497,6 @@ void LLTexLayerSetBuffer::doUpload()
|
|||
}
|
||||
|
||||
LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C;
|
||||
compressedImage->setRate(0.f);
|
||||
const char* comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // writes into baked_color_data. 5 channels (rgb, heightfield/alpha, mask)
|
||||
if (compressedImage->encode(baked_image, comment_text))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1030,7 +1030,6 @@ LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImage
|
|||
{
|
||||
raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
|
||||
LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();
|
||||
compressedImage->setRate(0.f);
|
||||
|
||||
if (gSavedSettings.getBOOL("LosslessJ2CUpload") &&
|
||||
(raw_image->getWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF))
|
||||
|
|
|
|||
Loading…
Reference in New Issue