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" | 
