summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Payne (Vir Linden) <vir@lindenlab.com>2012-04-12 16:02:15 -0400
committerBrad Payne (Vir Linden) <vir@lindenlab.com>2012-04-12 16:02:15 -0400
commitb4cf5c0c244d2280b729e6951da2128f00e960b6 (patch)
tree8e7cd50a157bae478e6fcb6627948e4994f02068
parentac50379957b3f8bf03f0604dc3319c14e73ce657 (diff)
parent172b45d5a217c7cdb922f49706b310edc412fc28 (diff)
merge
-rw-r--r--.hgignore1
-rw-r--r--indra/integration_tests/llimage_libtest/llimage_libtest.cpp68
-rw-r--r--indra/llimage/llimage.cpp22
-rw-r--r--indra/llimage/llimage.h15
-rw-r--r--indra/llimage/llimagej2c.cpp72
-rw-r--r--indra/llimage/llimagej2c.h6
-rw-r--r--indra/llkdu/llimagej2ckdu.cpp333
-rw-r--r--indra/llkdu/llimagej2ckdu.h1
-rw-r--r--indra/llkdu/tests/llimagej2ckdu_test.cpp23
-rwxr-xr-xindra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llappviewer.cpp2
-rw-r--r--indra/newview/llspatialpartition.cpp111
-rw-r--r--indra/newview/lltexlayer.cpp1
-rw-r--r--indra/newview/llviewermenu.cpp75
-rw-r--r--indra/newview/llviewertexture.cpp17
-rw-r--r--indra/newview/llviewertexture.h12
-rw-r--r--indra/newview/llviewertexturelist.cpp1
-rw-r--r--indra/newview/pipeline.h1
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml46
19 files changed, 687 insertions, 131 deletions
diff --git a/.hgignore b/.hgignore
index 403b73df6d..94391e11ab 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,5 +1,6 @@
syntax: glob
+
# WinMerge temp files
*.bak
# Compiled python bytecode
diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
index 48e876429d..36c5b67826 100644
--- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
+++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
@@ -54,6 +54,11 @@ static const char USAGE[] = "\n"
" -o, --output <file1 .. file2> OR <type>\n"
" List of image files to create (assumes same order as for input files)\n"
" OR 3 letters file type extension to convert each input file into.\n"
+" -load, --load_size <n>\n"
+" Portion of the input file to load, in bytes."
+" If (load == 0), it will load the whole file."
+" If (load == -1), it will load the size relevant to reach the requested discard level (see -d)."
+" Only valid for j2c images. Default is 0 (load whole file).\n"
" -r, --region <x0, y0, x1, y1>\n"
" Crop region applied to the input files in pixels.\n"
" Only used for j2c images. Default is no region cropping.\n"
@@ -104,22 +109,52 @@ void output_image_stats(LLPointer<LLImageFormatted> image, const std::string &fi
// Print out some statistical data on the image
std::cout << "Image stats for : " << filename << ", extension : " << image->getExtension() << std::endl;
- std::cout << " with : " << (int)(image->getWidth()) << ", height : " << (int)(image->getHeight()) << std::endl;
- std::cout << " comp : " << (int)(image->getComponents()) << ", levels : " << (int)(image->getDiscardLevel()) << std::endl;
- std::cout << " head : " << (int)(image->calcHeaderSize()) << ", data : " << (int)(image->getDataSize()) << std::endl;
+ std::cout << " with : " << (int)(image->getWidth()) << ", height : " << (int)(image->getHeight()) << std::endl;
+ std::cout << " comp : " << (int)(image->getComponents()) << ", levels : " << (int)(image->getLevels()) << std::endl;
+ std::cout << " head : " << (int)(image->calcHeaderSize()) << ", data : " << (int)(image->getDataSize()) << std::endl;
return;
}
// Load an image from file and return a raw (decompressed) instance of its data
-LLPointer<LLImageRaw> load_image(const std::string &src_filename, int discard_level, int* region, bool output_stats)
+LLPointer<LLImageRaw> load_image(const std::string &src_filename, int discard_level, int* region, int load_size, bool output_stats)
{
LLPointer<LLImageFormatted> image = create_image(src_filename);
- // This just loads the image file stream into a buffer. No decoding done.
- if (!image->load(src_filename))
+ // We support partial loading only for j2c images
+ if (image->getCodec() == IMG_CODEC_J2C)
{
- return NULL;
+ // Load the header
+ if (!image->load(src_filename, 600))
+ {
+ return NULL;
+ }
+ S32 h = ((LLImageJ2C*)(image.get()))->calcHeaderSize();
+ S32 d = (load_size > 0 ? ((LLImageJ2C*)(image.get()))->calcDiscardLevelBytes(load_size) : 0);
+ S8 r = ((LLImageJ2C*)(image.get()))->getRawDiscardLevel();
+ std::cout << "Merov debug : header = " << h << ", load_size = " << load_size << ", discard level = " << d << ", raw discard level = " << r << std::endl;
+ for (d = 0; d < MAX_DISCARD_LEVEL; d++)
+ {
+ S32 data_size = ((LLImageJ2C*)(image.get()))->calcDataSize(d);
+ std::cout << "Merov debug : discard_level = " << d << ", data_size = " << data_size << std::endl;
+ }
+ if (load_size < 0)
+ {
+ load_size = (discard_level != -1 ? ((LLImageJ2C*)(image.get()))->calcDataSize(discard_level) : 0);
+ }
+ // Load the requested byte range
+ if (!image->load(src_filename, load_size))
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ // This just loads the image file stream into a buffer. No decoding done.
+ if (!image->load(src_filename))
+ {
+ return NULL;
+ }
}
if( (image->getComponents() != 3) && (image->getComponents() != 4) )
@@ -310,6 +345,7 @@ int main(int argc, char** argv)
bool image_stats = false;
int* region = NULL;
int discard_level = -1;
+ int load_size = 0;
int precincts_size = -1;
int blocks_size = -1;
int levels = 0;
@@ -396,6 +432,22 @@ int main(int argc, char** argv)
discard_level = llclamp(discard_level,0,5);
}
}
+ else if (!strcmp(argv[arg], "--load_size") || !strcmp(argv[arg], "-load"))
+ {
+ std::string value_str;
+ if ((arg + 1) < argc)
+ {
+ value_str = argv[arg+1];
+ }
+ if (((arg + 1) >= argc) || (value_str[0] == '-'))
+ {
+ std::cout << "No valid --load_size argument given, load_size ignored" << std::endl;
+ }
+ else
+ {
+ load_size = atoi(value_str.c_str());
+ }
+ }
else if (!strcmp(argv[arg], "--precincts") || !strcmp(argv[arg], "-p"))
{
std::string value_str;
@@ -510,7 +562,7 @@ int main(int argc, char** argv)
for (; in_file != in_end; ++in_file, ++out_file)
{
// Load file
- LLPointer<LLImageRaw> raw_image = load_image(*in_file, discard_level, region, image_stats);
+ LLPointer<LLImageRaw> raw_image = load_image(*in_file, discard_level, region, load_size, image_stats);
if (!raw_image)
{
std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 56e01ac851..7f95441075 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -48,11 +48,13 @@
//static
std::string LLImage::sLastErrorMessage;
LLMutex* LLImage::sMutex = NULL;
+bool LLImage::sUseNewByteRange = false;
LLPrivateMemoryPool* LLImageBase::sPrivatePoolp = NULL ;
//static
-void LLImage::initClass()
+void LLImage::initClass(bool use_new_byte_range)
{
+ sUseNewByteRange = use_new_byte_range;
sMutex = new LLMutex(NULL);
LLImageBase::createPrivatePool() ;
@@ -1334,7 +1336,8 @@ LLImageFormatted::LLImageFormatted(S8 codec)
mCodec(codec),
mDecoding(0),
mDecoded(0),
- mDiscardLevel(-1)
+ mDiscardLevel(-1),
+ mLevels(0)
{
mMemType = LLMemType::MTYPE_IMAGEFORMATTED;
}
@@ -1561,7 +1564,7 @@ void LLImageFormatted::appendData(U8 *data, S32 size)
//----------------------------------------------------------------------------
-BOOL LLImageFormatted::load(const std::string &filename)
+BOOL LLImageFormatted::load(const std::string &filename, int load_size)
{
resetLastError();
@@ -1580,14 +1583,19 @@ BOOL LLImageFormatted::load(const std::string &filename)
return FALSE;
}
+ // Constrain the load size to acceptable values
+ if ((load_size == 0) || (load_size > file_size))
+ {
+ load_size = file_size;
+ }
BOOL res;
- U8 *data = allocateData(file_size);
- apr_size_t bytes_read = file_size;
+ U8 *data = allocateData(load_size);
+ apr_size_t bytes_read = load_size;
apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read
- if (s != APR_SUCCESS || (S32) bytes_read != file_size)
+ if (s != APR_SUCCESS || (S32) bytes_read != load_size)
{
deleteData();
- setLastError("Unable to read entire file",filename);
+ setLastError("Unable to read file",filename);
res = FALSE;
}
else
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 4469c9e860..a643e4d9f5 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -48,6 +48,8 @@ const S32 MAX_PRECINCT_SIZE = 2048; // No reason to be bigger than MAX_IMAGE_S
const S32 MIN_PRECINCT_SIZE = 4; // Can't be smaller than MIN_BLOCK_SIZE
const S32 MAX_BLOCK_SIZE = 64; // Max total block size is 4096, hence 64x64 when using square blocks
const S32 MIN_BLOCK_SIZE = 4; // Min block dim is 4 according to jpeg2000 spec
+const S32 MIN_LAYER_SIZE = 2000; // Size of the first quality layer (after header). Must be > to FIRST_PACKET_SIZE!!
+const S32 MAX_NB_LAYERS = 64; // Max number of layers we'll entertain in SL (practical limit)
const S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2
const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 2048
@@ -60,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:
@@ -89,15 +92,18 @@ typedef enum e_image_codec
class LLImage
{
public:
- static void initClass();
+ static void initClass(bool use_new_byte_range = false);
static void cleanupClass();
static const std::string& getLastError();
static void setLastError(const std::string& message);
+ static bool useNewByteRange() { return sUseNewByteRange; }
+
protected:
static LLMutex* sMutex;
static std::string sLastErrorMessage;
+ static bool sUseNewByteRange;
};
//============================================================================
@@ -294,7 +300,7 @@ public:
// getRawDiscardLevel() by default returns mDiscardLevel, but may be overridden (LLImageJ2C)
virtual S8 getRawDiscardLevel() { return mDiscardLevel; }
- BOOL load(const std::string& filename);
+ BOOL load(const std::string& filename, int load_size = 0);
BOOL save(const std::string& filename);
virtual BOOL updateData() = 0; // pure virtual
@@ -313,6 +319,8 @@ public:
BOOL isDecoded() const { return mDecoded ? TRUE : FALSE; }
void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; }
S8 getDiscardLevel() const { return mDiscardLevel; }
+ S8 getLevels() const { return mLevels; }
+ void setLevels(S8 nlevels) { mLevels = nlevels; }
// setLastError needs to be deferred for J2C images since it may be called from a DLL
virtual void resetLastError();
@@ -325,7 +333,8 @@ protected:
S8 mCodec;
S8 mDecoding;
S8 mDecoded; // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
- S8 mDiscardLevel;
+ 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.
public:
static S32 sGlobalFormattedMemory;
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index cc8cb66d73..69d261c9a6 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -56,7 +56,7 @@ 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)
{
@@ -142,6 +142,7 @@ BOOL LLImageJ2C::updateData()
BOOL LLImageJ2C::initDecode(LLImageRaw &raw_image, int discard_level, int* region)
{
+ setDiscardLevel(discard_level != -1 ? discard_level : 0);
return mImpl->initDecode(*this,raw_image,discard_level,region);
}
@@ -261,19 +262,35 @@ S32 LLImageJ2C::calcHeaderSizeJ2C()
//static
S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
{
- // Note: this only provides an *estimate* of the size in bytes of an image level
- // *TODO: find a way to read the true size (when available) and convey the fact
- // that the result is an estimate in the other cases
- if (rate <= 0.f) rate = .125f;
- while (discard_level > 0)
+ // 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
+
+ // Estimate the number of layers. This is consistent with what's done for j2c encoding in LLImageJ2CKDU::encodeImpl().
+ 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;
}
- S32 bytes = (S32)((F32)(w*h*comp)*rate);
+ 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;
+ 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 : 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;
}
@@ -283,15 +300,12 @@ S32 LLImageJ2C::calcHeaderSize()
return calcHeaderSizeJ2C();
}
-
-// calcDataSize() returns how many bytes to read
-// to load discard_level (including header and higher discard levels)
+// calcDataSize() returns how many bytes to read to load discard_level (including header)
S32 LLImageJ2C::calcDataSize(S32 discard_level)
{
discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL);
-
if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth())
- || mDataSizes[0] == 0)
+ || (mDataSizes[0] == 0))
{
mAreaUsedForDataSizeCalcs = getHeight() * getWidth();
@@ -301,25 +315,6 @@ S32 LLImageJ2C::calcDataSize(S32 discard_level)
mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
level--;
}
-
- /* This is technically a more correct way to calculate the size required
- for each discard level, since they should include the size needed for
- lower levels. Unfortunately, this doesn't work well and will lead to
- download stalls. The true correct way is to parse the header. This will
- all go away with http textures at some point.
-
- // Calculate the size for each discard level. Lower levels (higher quality)
- // contain the cumulative size of higher levels
- S32 total_size = calcHeaderSizeJ2C();
-
- S32 level = MAX_DISCARD_LEVEL; // Start at the highest discard
- while ( level >= 0 )
- { // Add in this discard level and all before it
- total_size += calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
- mDataSizes[level] = total_size;
- level--;
- }
- */
}
return mDataSizes[discard_level];
}
@@ -348,11 +343,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;
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index 914174fc57..91c344d12f 100644
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
@@ -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, 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();
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index c156ed0cef..cbfc34ebb8 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -32,6 +32,7 @@
#include "llmath.h"
#include "llkdumem.h"
+#include "kdu_block_coding.h"
class kdc_flow_control {
@@ -244,7 +245,9 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod
mCodeStreamp->create(mInputp);
// Set the maximum number of bytes to use from the codestream
- mCodeStreamp->set_max_bytes(max_bytes);
+ // *TODO: This seems to be wrong. The base class should have no idea of how j2c compression works so no
+ // good way of computing what's the byte range to be used.
+ mCodeStreamp->set_max_bytes(max_bytes,true);
// If you want to flip or rotate the image for some reason, change
// the resolution, or identify a restricted region of interest, this is
@@ -291,8 +294,13 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod
}
}
+ // Get the number of resolution levels in that image
+ mLevels = mCodeStreamp->get_min_dwt_levels();
+
+ // Set the base dimensions
base.setSize(dims.size.x, dims.size.y, components);
-
+ base.setLevels(mLevels);
+
if (!keep_codestream)
{
mCodeStreamp->destroy();
@@ -351,7 +359,8 @@ BOOL LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int bloc
mLevels = levels;
if (mLevels != 0)
{
- mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MIN_DECOMPOSITION_LEVELS);
+ mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MAX_DECOMPOSITION_LEVELS);
+ base.setLevels(mLevels);
}
return TRUE;
}
@@ -364,6 +373,9 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
// To regain control, we throw an exception, and catch it here.
try
{
+ // Merov : Test!! DO NOT COMMIT!!
+ //findDiscardLevelsBoundaries(base);
+
base.updateRawDiscardLevel();
setupCodeStream(base, TRUE, mode);
@@ -381,7 +393,7 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
region_kdu->size.y = region[3] - region[1];
}
int discard = (discard_level != -1 ? discard_level : base.getRawDiscardLevel());
-
+ //llinfos << "Merov debug : initDecode, discard used = " << discard << ", asked = " << discard_level << llendl;
// Apply loading restrictions
mCodeStreamp->apply_input_restrictions( first_channel, max_channel_count, discard, 0, region_kdu);
@@ -394,12 +406,9 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
// Resize raw_image according to the image to be decoded
kdu_dims dims; mCodeStreamp->get_dims(0,dims);
- // *TODO: Use the real number of levels read from the file throughout the code instead of relying on an infered value from dimensions
- //S32 levels = mCodeStreamp->get_min_dwt_levels();
S32 channels = base.getComponents() - first_channel;
channels = llmin(channels,max_channel_count);
raw_image.resize(dims.size.x, dims.size.y, channels);
- //llinfos << "j2c image dimension: width = " << dims.size.x << ", height = " << dims.size.y << ", channels = " << channels << ", levels = " << levels << llendl;
if (!mTileIndicesp)
{
@@ -583,12 +592,6 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
comment.put_text(comment_text);
}
- // Set codestream options
- int num_layer_specs = 0;
-
- kdu_long layer_bytes[64];
- U32 max_bytes = 0;
-
if (num_components >= 3)
{
// Note that we always use YCC and not YUV
@@ -596,67 +599,63 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
set_default_colour_weights(codestream.access_siz());
}
+ // Set codestream options
+ 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.
+ 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.
+ if (max_bytes < FIRST_PACKET_SIZE)
+ {
+ reversible = true;
+ }
+
+ // This code is where we specify the target number of bytes for each quality layer.
+ // 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) && (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[nb_layers] = i;
+ nb_layers++;
+ i *= 4;
+ }
+
if (reversible)
{
codestream.access_siz()->parse_string("Creversible=yes");
- // *TODO: we should use yuv in reversible mode and one level since those images are small.
+ // *TODO: we should use yuv in reversible mode and one res level since those images are small.
// Don't turn this on now though as both create problems on decoding for the moment
//codestream.access_siz()->parse_string("Clevels=1");
//codestream.access_siz()->parse_string("Cycc=no");
- // If we're doing reversible (i.e. lossless compression), assumes we're not using quality layers.
- // *TODO: this is incorrect and unecessary. Try using the regular layer setting.
- codestream.access_siz()->parse_string("Clayers=1");
- num_layer_specs = 1;
- layer_bytes[0] = 0;
+ // 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[nb_layers] = 0;
}
else
{
- // Rate is the argument passed into the LLImageJ2C which
- // specifies the target compression rate. The default is 8:1.
- // Possibly if max_bytes < 500, we should just use the default setting?
- // *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)(base.mRate*base.getWidth()*base.getHeight()*base.getComponents());
- }
- else
- {
- max_bytes = (U32)(base.getWidth()*base.getHeight()*base.getComponents()*0.125);
- }
-
- const U32 min_bytes = FIRST_PACKET_SIZE;
- if (max_bytes > min_bytes)
- {
- U32 i;
- // This code is where we specify the target number of bytes for
- // each layer. Not sure if we should do this for small images
- // or not. The goal is to have this roughly align with
- // different quality levels that we decode at.
- for (i = min_bytes; i < max_bytes; i*=4)
- {
- if (i == min_bytes * 4)
- {
- i = 2000;
- }
- layer_bytes[num_layer_specs] = i;
- num_layer_specs++;
- }
- layer_bytes[num_layer_specs] = max_bytes;
- num_layer_specs++;
-
- std::string layer_string = llformat("Clayers=%d",num_layer_specs);
- codestream.access_siz()->parse_string(layer_string.c_str());
- }
- else
- {
- layer_bytes[0] = min_bytes;
- num_layer_specs = 1;
- std::string layer_string = llformat("Clayers=%d",num_layer_specs);
- codestream.access_siz()->parse_string(layer_string.c_str());
- }
+ // Truncate the last quality layer if necessary so to fit the set compression ratio
+ layer_bytes[nb_layers] = max_bytes;
}
+ nb_layers++;
+
+ std::string layer_string = llformat("Clayers=%d",nb_layers);
+ codestream.access_siz()->parse_string(layer_string.c_str());
// 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
+ // by llimage_libtest to create various j2c and test alternative compression schemes.
if ((mBlocksSize != -1) || (mPrecinctsSize != -1))
{
if (mPrecinctsSize != -1)
@@ -669,23 +668,26 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
std::string blocks_string = llformat("Cblk={%d,%d}",mBlocksSize,mBlocksSize);
codestream.access_siz()->parse_string(blocks_string.c_str());
}
- std::string ordering_string = llformat("Corder=RPCL");
+ std::string ordering_string = llformat("Corder=LRCP");
codestream.access_siz()->parse_string(ordering_string.c_str());
std::string PLT_string = llformat("ORGgen_plt=yes");
codestream.access_siz()->parse_string(PLT_string.c_str());
std::string Parts_string = llformat("ORGtparts=R");
codestream.access_siz()->parse_string(Parts_string.c_str());
}
+
+ // Set the number of wavelets subresolutions (aka levels)
if (mLevels != 0)
{
std::string levels_string = llformat("Clevels=%d",mLevels);
codestream.access_siz()->parse_string(levels_string.c_str());
}
+ // Complete the encode settings
codestream.access_siz()->finalize_all();
codestream.change_appearance(transpose,vflip,hflip);
- // Now we are ready for sample data processing.
+ // Now we are ready for sample data processing
kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream);
bool done = false;
while (!done)
@@ -702,7 +704,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
}
// Produce the compressed output
- codestream.flush(layer_bytes,num_layer_specs);
+ codestream.flush(layer_bytes,nb_layers);
// Cleanup
delete tile;
@@ -750,6 +752,207 @@ BOOL LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
}
}
+/*****************************************************************************/
+/* STATIC copy_block */
+/*****************************************************************************/
+
+static void copy_block(kdu_block *in, kdu_block *out)
+{
+ if (in->K_max_prime != out->K_max_prime)
+ {
+ std::cout << "Cannot copy blocks belonging to subbands with different quantization parameters." << std::endl;
+ return;
+ }
+ if ((in->size.x != out->size.x) || (in->size.y != out->size.y))
+ {
+ std::cout << "Cannot copy code-blocks with different dimensions." << std::endl;
+ return;
+ }
+ out->missing_msbs = in->missing_msbs;
+ if (out->max_passes < (in->num_passes+2)) // Gives us enough to round up
+ out->set_max_passes(in->num_passes+2,false); // to the next whole bit-plane
+ out->num_passes = in->num_passes;
+ int num_bytes = 0;
+ for (int z=0; z < in->num_passes; z++)
+ {
+ num_bytes += (out->pass_lengths[z] = in->pass_lengths[z]);
+ out->pass_slopes[z] = in->pass_slopes[z];
+ }
+
+ // Just copy compressed code-bytes. Block transcoding not supported.
+ if (out->max_bytes < num_bytes)
+ out->set_max_bytes(num_bytes,false);
+ memcpy(out->byte_buffer,in->byte_buffer,(size_t) num_bytes);
+}
+
+/*****************************************************************************/
+/* STATIC copy_tile */
+/*****************************************************************************/
+
+static void
+copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out,
+ kdu_params *siz_in, kdu_params *siz_out, int skip_components,
+ int &num_blocks)
+{
+ int num_components = tile_out.get_num_components();
+ int new_tpart=0, next_tpart = 1;
+
+ for (int c=0; c < num_components; c++)
+ {
+ kdu_tile_comp comp_in, comp_out;
+ comp_in = tile_in.access_component(c);
+ comp_out = tile_out.access_component(c);
+ int num_resolutions = comp_out.get_num_resolutions();
+ //std::cout << " Copying tile : num_resolutions = " << num_resolutions << std::endl;
+ for (int r=0; r < num_resolutions; r++)
+ {
+ kdu_resolution res_in; res_in = comp_in.access_resolution(r);
+ kdu_resolution res_out; res_out = comp_out.access_resolution(r);
+ int b, min_band;
+ int num_bands = res_in.get_valid_band_indices(min_band);
+ std::cout << " Copying tile : num_bands = " << num_bands << std::endl;
+ for (b=min_band; num_bands > 0; num_bands--, b++)
+ {
+ kdu_subband band_in; band_in = res_in.access_subband(b);
+ kdu_subband band_out; band_out = res_out.access_subband(b);
+ kdu_dims blocks_in; band_in.get_valid_blocks(blocks_in);
+ kdu_dims blocks_out; band_out.get_valid_blocks(blocks_out);
+ if ((blocks_in.size.x != blocks_out.size.x) ||
+ (blocks_in.size.y != blocks_out.size.y))
+ {
+ std::cout << "Transcoding operation cannot proceed: Code-block partitions for the input and output code-streams do not agree." << std::endl;
+ return;
+ }
+ kdu_coords idx;
+ //std::cout << " Copying tile : block indices, x = " << blocks_out.size.x << " and y = " << blocks_out.size.y << std::endl;
+ for (idx.y=0; idx.y < blocks_out.size.y; idx.y++)
+ {
+ for (idx.x=0; idx.x < blocks_out.size.x; idx.x++)
+ {
+ kdu_block *in =
+ band_in.open_block(idx+blocks_in.pos,&new_tpart);
+ for (; next_tpart <= new_tpart; next_tpart++)
+ siz_out->copy_from(siz_in,tnum_in,tnum_out,next_tpart,
+ skip_components);
+ kdu_block *out = band_out.open_block(idx+blocks_out.pos);
+ copy_block(in,out);
+ band_in.close_block(in);
+ band_out.close_block(out);
+ num_blocks++;
+ }
+ }
+ }
+ }
+ }
+}
+
+// Find the block boundary for each discard level in the input image.
+// We parse the input blocks and copy them in a temporary output stream.
+// For the moment, we do nothing more that parsing the raw list of blocks and outputing result.
+void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base)
+{
+ // We need the number of levels in that image before starting.
+ getMetadata(base);
+
+ for (int discard_level = 0; discard_level < mLevels; discard_level++)
+ {
+ //std::cout << "Parsing discard level = " << discard_level << std::endl;
+ // Create the input codestream object.
+ setupCodeStream(base, TRUE, MODE_FAST);
+ mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL);
+ mCodeStreamp->set_max_bytes(KDU_LONG_MAX,true);
+ siz_params *siz_in = mCodeStreamp->access_siz();
+
+ // Create the output codestream object.
+ siz_params siz;
+ siz.copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
+ siz.set(Scomponents,0,0,mCodeStreamp->get_num_components());
+
+ U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents();
+ max_output_size = (max_output_size < 1000 ? 1000 : max_output_size);
+ U8 *output_buffer = new U8[max_output_size];
+ U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size
+ LLKDUMemTarget output(output_buffer, output_size, max_output_size);
+ kdu_codestream codestream_out;
+ codestream_out.create(&siz,&output);
+ //codestream_out.share_buffering(*mCodeStreamp);
+ siz_params *siz_out = codestream_out.access_siz();
+ siz_out->copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
+ codestream_out.access_siz()->finalize_all(-1);
+
+ // Set up rate control variables
+ kdu_long max_bytes = KDU_LONG_MAX;
+ kdu_params *cod = siz_out->access_cluster(COD_params);
+ int total_layers; cod->get(Clayers,0,0,total_layers);
+ kdu_long *layer_bytes = new kdu_long[total_layers];
+ int nel, non_empty_layers = 0;
+
+ // Now ready to perform the transfer of compressed data between streams
+ int flush_counter = INT_MAX;
+ kdu_dims tile_indices_in;
+ mCodeStreamp->get_valid_tiles(tile_indices_in);
+ kdu_dims tile_indices_out;
+ codestream_out.get_valid_tiles(tile_indices_out);
+ assert((tile_indices_in.size.x == tile_indices_out.size.x) &&
+ (tile_indices_in.size.y == tile_indices_out.size.y));
+ int num_blocks=0;
+
+ kdu_coords idx;
+ //std::cout << "Parsing tiles : x = " << tile_indices_out.size.x << " to y = " << tile_indices_out.size.y << std::endl;
+ for (idx.y=0; idx.y < tile_indices_out.size.y; idx.y++)
+ {
+ for (idx.x=0; idx.x < tile_indices_out.size.x; idx.x++)
+ {
+ kdu_tile tile_in = mCodeStreamp->open_tile(idx+tile_indices_in.pos);
+ int tnum_in = tile_in.get_tnum();
+ int tnum_out = idx.x + idx.y*tile_indices_out.size.x;
+ siz_out->copy_from(siz_in,tnum_in,tnum_out,0,0,discard_level,false,false,false);
+ siz_out->finalize_all(tnum_out);
+ // Note: do not open the output tile without first copying any tile-specific code-stream parameters
+ kdu_tile tile_out = codestream_out.open_tile(idx+tile_indices_out.pos);
+ assert(tnum_out == tile_out.get_tnum());
+ copy_tile(tile_in,tile_out,tnum_in,tnum_out,siz_in,siz_out,0,num_blocks);
+ tile_in.close();
+ tile_out.close();
+ flush_counter--;
+ if ((flush_counter <= 0) && codestream_out.ready_for_flush())
+ {
+ flush_counter = INT_MAX;
+ nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
+ non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
+ }
+ }
+ }
+
+ // Generate the output code-stream
+ if (codestream_out.ready_for_flush())
+ {
+ nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
+ non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
+ }
+ if (non_empty_layers > total_layers)
+ non_empty_layers = total_layers; // Can happen if a tile has more layers
+
+ // Print out stats
+ std::cout << "Code stream parsing for discard level = " << discard_level << std::endl;
+ std::cout << " Total compressed memory in = " << mCodeStreamp->get_compressed_data_memory() << " bytes" << std::endl;
+ std::cout << " Total compressed memory out = " << codestream_out.get_compressed_data_memory() << " bytes" << std::endl;
+ //std::cout << " Output contains " << total_layers << " quality layers" << std::endl;
+ std::cout << " Transferred " << num_blocks << " code-blocks from in to out" << std::endl;
+ //std::cout << " Read " << mCodeStreamp->get_num_tparts() << " tile-part(s) from a total of " << (int) tile_indices_in.area() << " tile(s)" << std::endl;
+ std::cout << " Total bytes read = " << mCodeStreamp->get_total_bytes() << std::endl;
+ //std::cout << " Wrote " << codestream_out.get_num_tparts() << " tile-part(s) in a total of " << (int) tile_indices_out.area() << " tile(s)" << std::endl;
+ std::cout << " Total bytes written = " << codestream_out.get_total_bytes() << std::endl;
+ std::cout << "-------------" << std::endl;
+
+ // Clean-up
+ cleanupCodeStream();
+ codestream_out.destroy();
+ delete[] output_buffer;
+ }
+ return;
+}
+
void set_default_colour_weights(kdu_params *siz)
{
kdu_params *cod = siz->access_cluster(COD_params);
diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h
index 1489dbf704..9ab0b9e4a7 100644
--- a/indra/llkdu/llimagej2ckdu.h
+++ b/indra/llkdu/llimagej2ckdu.h
@@ -60,6 +60,7 @@ protected:
BOOL reversible=FALSE);
/*virtual*/ BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL);
/*virtual*/ BOOL initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0);
+ void findDiscardLevelsBoundaries(LLImageJ2C &base);
private:
BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level = -1, int* region = NULL);
diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp
index ab60ab6d50..beee99a522 100644
--- a/indra/llkdu/tests/llimagej2ckdu_test.cpp
+++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp
@@ -29,6 +29,7 @@
// Class to test
#include "llimagej2ckdu.h"
#include "llkdumem.h"
+#include "kdu_block_coding.h"
// Tut header
#include "lltut.h"
@@ -86,7 +87,7 @@ void LLImageFormatted::resetLastError() { }
void LLImageFormatted::sanityCheck() { }
void LLImageFormatted::setLastError(const std::string& , const std::string& ) { }
-LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C) { }
+LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), mRate(DEFAULT_COMPRESSION_RATE) { }
LLImageJ2C::~LLImageJ2C() { }
S32 LLImageJ2C::calcDataSize(S32 ) { return 0; }
S32 LLImageJ2C::calcDiscardLevelBytes(S32 ) { return 0; }
@@ -107,16 +108,25 @@ bool LLKDUMemIn::get(int, kdu_line_buf&, int) { return false; }
// Stub Kakadu Library calls
kdu_tile_comp kdu_tile::access_component(int ) { kdu_tile_comp a; return a; }
+kdu_block_encoder::kdu_block_encoder() { }
+kdu_block_decoder::kdu_block_decoder() { }
+void kdu_block::set_max_passes(int , bool ) { }
+void kdu_block::set_max_bytes(int , bool ) { }
+void kdu_block::set_max_samples(int ) { }
void kdu_tile::close(kdu_thread_env* ) { }
int kdu_tile::get_num_components() { return 0; }
bool kdu_tile::get_ycc() { return false; }
void kdu_tile::set_components_of_interest(int , const int* ) { }
+int kdu_tile::get_tnum() { return 0; }
kdu_resolution kdu_tile_comp::access_resolution() { kdu_resolution a; return a; }
+kdu_resolution kdu_tile_comp::access_resolution(int ) { kdu_resolution a; return a; }
int kdu_tile_comp::get_bit_depth(bool ) { return 8; }
bool kdu_tile_comp::get_reversible() { return false; }
+int kdu_tile_comp::get_num_resolutions() { return 1; }
kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; }
void kdu_resolution::get_dims(kdu_dims& ) { }
int kdu_resolution::which() { return 0; }
+int kdu_resolution::get_valid_band_indices(int &) { return 1; }
kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { }
kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { }
kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { }
@@ -124,6 +134,7 @@ kdu_params::~kdu_params() { }
void kdu_params::set(const char* , int , int , bool ) { }
void kdu_params::set(const char* , int , int , int ) { }
void kdu_params::finalize_all(bool ) { }
+void kdu_params::finalize_all(int, bool ) { }
void kdu_params::copy_from(kdu_params*, int, int, int, int, int, bool, bool, bool) { }
bool kdu_params::parse_string(const char*) { return false; }
bool kdu_params::get(const char*, int, int, bool&, bool, bool, bool) { return false; }
@@ -135,6 +146,7 @@ void kdu_codestream::set_fast() { }
void kdu_codestream::set_fussy() { }
void kdu_codestream::get_dims(int, kdu_dims&, bool ) { }
int kdu_codestream::get_min_dwt_levels() { return 5; }
+int kdu_codestream::get_max_tile_layers() { return 1; }
void kdu_codestream::change_appearance(bool, bool, bool) { }
void kdu_codestream::get_tile_dims(kdu_coords, int, kdu_dims&, bool ) { }
void kdu_codestream::destroy() { }
@@ -148,9 +160,18 @@ void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { }
void kdu_codestream::flush(kdu_long *, int , kdu_uint16 *, bool, bool, double, kdu_thread_env*) { }
void kdu_codestream::set_resilient(bool ) { }
int kdu_codestream::get_num_components(bool ) { return 0; }
+kdu_long kdu_codestream::get_total_bytes(bool ) { return 0; }
+kdu_long kdu_codestream::get_compressed_data_memory(bool ) {return 0; }
+void kdu_codestream::share_buffering(kdu_codestream ) { }
+int kdu_codestream::get_num_tparts() { return 0; }
+int kdu_codestream::trans_out(kdu_long, kdu_long*, int, bool, kdu_thread_env* ) { return 0; }
+bool kdu_codestream::ready_for_flush(kdu_thread_env*) { return false; }
siz_params* kdu_codestream::access_siz() { return NULL; }
kdu_tile kdu_codestream::open_tile(kdu_coords , kdu_thread_env* ) { kdu_tile a; return a; }
kdu_codestream_comment kdu_codestream::add_comment() { kdu_codestream_comment a; return a; }
+void kdu_subband::close_block(kdu_block*, kdu_thread_env*) { }
+void kdu_subband::get_valid_blocks(kdu_dims &indices) { }
+kdu_block* kdu_subband::open_block(kdu_coords, int*, kdu_thread_env*) { return NULL; }
bool kdu_codestream_comment::put_text(const char*) { return false; }
void kdu_customize_warnings(kdu_message*) { }
void kdu_customize_errors(kdu_message*) { }
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9fff543b13..5fc9c5d863 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10686,6 +10686,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>TextureNewByteRange</key>
+ <map>
+ <key>Comment</key>
+ <string>Use the new more accurate byte range computation for j2c discard levels</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
<key>TexturePickerShowFolders</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 98a83175a7..9a52ee4312 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1950,7 +1950,7 @@ bool LLAppViewer::initThreads()
static const bool enable_threads = true;
#endif
- LLImage::initClass();
+ LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange"));
LLVFSThread::initClass(enable_threads && false);
LLLFSThread::initClass(enable_threads && false);
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 5d196a465f..b7a5eea27c 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -3647,6 +3647,110 @@ void renderShadowFrusta(LLDrawInfo* params)
gGL.setSceneBlendType(LLRender::BT_ALPHA);
}
+void renderTexelDensity(LLDrawable* drawable)
+{
+ if (LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_OFF
+ || LLViewerTexture::sCheckerBoardImagep.isNull())
+ {
+ return;
+ }
+
+ LLGLEnable _(GL_BLEND);
+ //gObjectFullbrightProgram.bind();
+
+ LLMatrix4 checkerboard_matrix;
+ S32 discard_level = -1;
+
+ for (S32 f = 0; f < drawable->getNumFaces(); f++)
+ {
+ LLFace* facep = drawable->getFace(f);
+ LLVertexBuffer* buffer = facep->getVertexBuffer();
+ LLViewerTexture* texturep = facep->getTexture();
+
+ if (texturep == NULL) continue;
+
+ switch(LLViewerTexture::sDebugTexelsMode)
+ {
+ case LLViewerTexture::DEBUG_TEXELS_CURRENT:
+ discard_level = -1;
+ break;
+ case LLViewerTexture::DEBUG_TEXELS_DESIRED:
+ {
+ LLViewerFetchedTexture* fetched_texturep = dynamic_cast<LLViewerFetchedTexture*>(texturep);
+ discard_level = fetched_texturep ? fetched_texturep->getDesiredDiscardLevel() : -1;
+ break;
+ }
+ default:
+ case LLViewerTexture::DEBUG_TEXELS_FULL:
+ discard_level = 0;
+ break;
+ }
+
+ checkerboard_matrix.initScale(LLVector3(texturep->getWidth(discard_level) / 8, texturep->getHeight(discard_level) / 8, 1.f));
+
+ gGL.getTexUnit(0)->bind(LLViewerTexture::sCheckerBoardImagep, TRUE);
+ gGL.matrixMode(LLRender::MM_TEXTURE);
+ gGL.loadMatrix((GLfloat*)&checkerboard_matrix.mMatrix);
+
+ if (buffer && (facep->getGeomCount() >= 3))
+ {
+ buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
+ U16 start = facep->getGeomStart();
+ U16 end = start + facep->getGeomCount()-1;
+ U32 count = facep->getIndicesCount();
+ U16 offset = facep->getIndicesStart();
+ buffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
+ }
+
+ gGL.loadIdentity();
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ }
+
+ //S32 num_textures = llmax(1, (S32)params->mTextureList.size());
+
+ //for (S32 i = 0; i < num_textures; i++)
+ //{
+ // LLViewerTexture* texturep = params->mTextureList.empty() ? params->mTexture.get() : params->mTextureList[i].get();
+ // if (texturep == NULL) continue;
+
+ // LLMatrix4 checkboard_matrix;
+ // S32 discard_level = -1;
+ // switch(LLViewerTexture::sDebugTexelsMode)
+ // {
+ // case LLViewerTexture::DEBUG_TEXELS_CURRENT:
+ // discard_level = -1;
+ // break;
+ // case LLViewerTexture::DEBUG_TEXELS_DESIRED:
+ // {
+ // LLViewerFetchedTexture* fetched_texturep = dynamic_cast<LLViewerFetchedTexture*>(texturep);
+ // discard_level = fetched_texturep ? fetched_texturep->getDesiredDiscardLevel() : -1;
+ // break;
+ // }
+ // default:
+ // case LLViewerTexture::DEBUG_TEXELS_FULL:
+ // discard_level = 0;
+ // break;
+ // }
+
+ // checkboard_matrix.initScale(LLVector3(texturep->getWidth(discard_level) / 8, texturep->getHeight(discard_level) / 8, 1.f));
+ // gGL.getTexUnit(i)->activate();
+
+ // glMatrixMode(GL_TEXTURE);
+ // glPushMatrix();
+ // glLoadIdentity();
+ // //gGL.matrixMode(LLRender::MM_TEXTURE);
+ // glLoadMatrixf((GLfloat*) checkboard_matrix.mMatrix);
+
+ // gGL.getTexUnit(i)->bind(LLViewerTexture::sCheckerBoardImagep, TRUE);
+
+ // pushVerts(params, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_NORMAL );
+
+ // glPopMatrix();
+ // glMatrixMode(GL_MODELVIEW);
+ // //gGL.matrixMode(LLRender::MM_MODELVIEW);
+ //}
+}
+
void renderLights(LLDrawable* drawablep)
{
@@ -4042,6 +4146,10 @@ public:
{
renderComplexityDisplay(drawable);
}
+ if(gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY))
+ {
+ renderTexelDensity(drawable);
+ }
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get());
@@ -4291,7 +4399,8 @@ void LLSpatialPartition::renderDebug()
LLPipeline::RENDER_DEBUG_AGENT_TARGET |
//LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA |
- LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
+ LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY |
+ LLPipeline::RENDER_DEBUG_TEXEL_DENSITY))
{
return;
}
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
index 1693cfc9e2..467115c928 100644
--- a/indra/newview/lltexlayer.cpp
+++ b/indra/newview/lltexlayer.cpp
@@ -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))
{
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 301b78ad4e..7bdfd6df1d 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -845,6 +845,73 @@ class LLAdvancedCheckFeature : public view_listener_t
}
};
+class LLAdvancedCheckDisplayTextureDensity : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ std::string mode = userdata.asString();
+ if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY))
+ {
+ return mode == "none";
+ }
+ if (mode == "current")
+ {
+ return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_CURRENT;
+ }
+ else if (mode == "desired")
+ {
+ return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_DESIRED;
+ }
+ else if (mode == "full")
+ {
+ return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_FULL;
+ }
+ return false;
+ }
+};
+
+class LLAdvancedSetDisplayTextureDensity : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ std::string mode = userdata.asString();
+ if (mode == "none")
+ {
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == TRUE)
+ {
+ gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+ }
+ LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
+ }
+ else if (mode == "current")
+ {
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE)
+ {
+ gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+ }
+ LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_CURRENT;
+ }
+ else if (mode == "desired")
+ {
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE)
+ {
+ gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+ }
+ gPipeline.setRenderDebugFeatureControl(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY, true);
+ LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_DESIRED;
+ }
+ else if (mode == "full")
+ {
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE)
+ {
+ gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+ }
+ LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_FULL;
+ }
+ return true;
+ }
+};
+
//////////////////
// INFO DISPLAY //
@@ -959,6 +1026,10 @@ U32 info_display_from_string(std::string info_display)
{
return LLPipeline::RENDER_DEBUG_WIND_VECTORS;
}
+ else if ("texel density" == info_display)
+ {
+ return LLPipeline::RENDER_DEBUG_TEXEL_DENSITY;
+ }
else
{
return 0;
@@ -8104,6 +8175,10 @@ void initialize_menus()
//// Advanced > Render > Features
view_listener_t::addMenu(new LLAdvancedToggleFeature(), "Advanced.ToggleFeature");
view_listener_t::addMenu(new LLAdvancedCheckFeature(), "Advanced.CheckFeature");
+
+ view_listener_t::addMenu(new LLAdvancedCheckDisplayTextureDensity(), "Advanced.CheckDisplayTextureDensity");
+ view_listener_t::addMenu(new LLAdvancedSetDisplayTextureDensity(), "Advanced.SetDisplayTextureDensity");
+
// Advanced > Render > Info Displays
view_listener_t::addMenu(new LLAdvancedToggleInfoDisplay(), "Advanced.ToggleInfoDisplay");
view_listener_t::addMenu(new LLAdvancedCheckInfoDisplay(), "Advanced.CheckInfoDisplay");
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 0360a8faf0..2efc9ad4d0 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -67,6 +67,7 @@
// statics
LLPointer<LLViewerTexture> LLViewerTexture::sNullImagep = NULL;
LLPointer<LLViewerTexture> LLViewerTexture::sBlackImagep = NULL;
+LLPointer<LLViewerTexture> LLViewerTexture::sCheckerBoardImagep = NULL;
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sMissingAssetImagep = NULL;
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sWhiteImagep = NULL;
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sDefaultImagep = NULL;
@@ -96,6 +97,7 @@ S32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA ;
BOOL LLViewerTexture::sFreezeImageScalingDown = FALSE ;
F32 LLViewerTexture::sCurrentTime = 0.0f ;
BOOL LLViewerTexture::sUseTextureAtlas = FALSE ;
+LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
const F32 desired_discard_bias_min = -2.0f; // -max number of levels to improve image quality by
const F32 desired_discard_bias_max = (F32)MAX_DISCARD_LEVEL; // max number of levels to reduce image quality by
@@ -347,6 +349,21 @@ void LLViewerTextureManager::init()
LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, TRUE, LLViewerTexture::BOOST_UI);
LLViewerFetchedTexture::sSmokeImagep->setNoDelete() ;
+ image_raw = new LLImageRaw(32,32,3);
+ data = image_raw->getData();
+
+ for (S32 i = 0; i < (32*32*3); i+=3)
+ {
+ S32 x = (i % (32*3)) / (3*16);
+ S32 y = i / (32*3*16);
+ U8 color = ((x + y) % 2) * 255;
+ data[i] = color;
+ data[i+1] = color;
+ data[i+2] = color;
+ }
+
+ LLViewerTexture::sCheckerBoardImagep = LLViewerTextureManager::getLocalTexture(image_raw.get(), TRUE);
+
LLViewerTexture::initClass() ;
if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName))
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index b96441127d..99053a8ccc 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -139,6 +139,7 @@ public:
OTHER,
MAX_GL_IMAGE_CATEGORY
};
+
static S32 getTotalNumOfCategories() ;
static S32 getIndexFromCategory(S32 category) ;
static S32 getCategoryFromIndex(S32 index) ;
@@ -330,8 +331,19 @@ public:
static F32 sCurrentTime ;
static BOOL sUseTextureAtlas ;
+ enum EDebugTexels
+ {
+ DEBUG_TEXELS_OFF,
+ DEBUG_TEXELS_CURRENT,
+ DEBUG_TEXELS_DESIRED,
+ DEBUG_TEXELS_FULL
+ };
+
+ static EDebugTexels sDebugTexelsMode;
+
static LLPointer<LLViewerTexture> sNullImagep; // Null texture for non-textured objects.
static LLPointer<LLViewerTexture> sBlackImagep; // Texture to show NOTHING (pure black)
+ static LLPointer<LLViewerTexture> sCheckerBoardImagep; // Texture to show NOTHING (pure black)
};
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 089f45ca89..54ae519422 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -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))
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 9c78048c46..670a23ef14 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -461,6 +461,7 @@ public:
RENDER_DEBUG_LOD_INFO = 0x04000000,
RENDER_DEBUG_RENDER_COMPLEXITY = 0x08000000,
RENDER_DEBUG_ATTACHMENT_BYTES = 0x10000000,
+ RENDER_DEBUG_TEXEL_DENSITY = 0x20000000
};
public:
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 58fd1ac829..8b0152b1a2 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2417,6 +2417,52 @@
function="Advanced.ToggleInfoDisplay"
parameter="sculpt" />
</menu_item_check>
+ <menu
+ create_jump_keys="true"
+ label="Texture Density"
+ name="Texture Density"
+ tear_off="true">
+ <menu_item_check
+ label="None"
+ name="None">
+ <menu_item_check.on_check
+ function="Advanced.CheckDisplayTextureDensity"
+ parameter="none" />
+ <menu_item_check.on_click
+ function="Advanced.SetDisplayTextureDensity"
+ parameter="none" />
+ </menu_item_check>
+ <menu_item_check
+ label="Current"
+ name="Current">
+ <menu_item_check.on_check
+ function="Advanced.CheckDisplayTextureDensity"
+ parameter="current" />
+ <menu_item_check.on_click
+ function="Advanced.SetDisplayTextureDensity"
+ parameter="current" />
+ </menu_item_check>
+ <menu_item_check
+ label="Desired"
+ name="Desired">
+ <menu_item_check.on_check
+ function="Advanced.CheckDisplayTextureDensity"
+ parameter="desired" />
+ <menu_item_check.on_click
+ function="Advanced.SetDisplayTextureDensity"
+ parameter="desired" />
+ </menu_item_check>
+ <menu_item_check
+ label="Full"
+ name="Full">
+ <menu_item_check.on_check
+ function="Advanced.CheckDisplayTextureDensity"
+ parameter="full" />
+ <menu_item_check.on_click
+ function="Advanced.SetDisplayTextureDensity"
+ parameter="full" />
+ </menu_item_check>
+ </menu>
</menu>
<menu
create_jump_keys="true"