diff options
-rw-r--r-- | .hgignore | 1 | ||||
-rw-r--r-- | indra/integration_tests/llimage_libtest/llimage_libtest.cpp | 68 | ||||
-rw-r--r-- | indra/llimage/llimage.cpp | 22 | ||||
-rw-r--r-- | indra/llimage/llimage.h | 15 | ||||
-rw-r--r-- | indra/llimage/llimagej2c.cpp | 72 | ||||
-rw-r--r-- | indra/llimage/llimagej2c.h | 6 | ||||
-rw-r--r-- | indra/llkdu/llimagej2ckdu.cpp | 333 | ||||
-rw-r--r-- | indra/llkdu/llimagej2ckdu.h | 1 | ||||
-rw-r--r-- | indra/llkdu/tests/llimagej2ckdu_test.cpp | 23 | ||||
-rwxr-xr-x | indra/newview/app_settings/settings.xml | 11 | ||||
-rw-r--r-- | indra/newview/llappviewer.cpp | 2 | ||||
-rw-r--r-- | indra/newview/llspatialpartition.cpp | 111 | ||||
-rw-r--r-- | indra/newview/lltexlayer.cpp | 1 | ||||
-rw-r--r-- | indra/newview/llviewermenu.cpp | 75 | ||||
-rw-r--r-- | indra/newview/llviewertexture.cpp | 17 | ||||
-rw-r--r-- | indra/newview/llviewertexture.h | 12 | ||||
-rw-r--r-- | indra/newview/llviewertexturelist.cpp | 1 | ||||
-rw-r--r-- | indra/newview/pipeline.h | 1 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/menu_viewer.xml | 46 |
19 files changed, 687 insertions, 131 deletions
@@ -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" |