From df09fd8e8b5b73330e4942c2cb218a216d7aca99 Mon Sep 17 00:00:00 2001
From: Merov Linden <merov@lindenlab.com>
Date: Mon, 2 Apr 2012 19:05:32 -0700
Subject: [PATCH] SH-3060 : Preliminary implementation of the new byte range
 computation, implement setting to turn it on or off

---
 indra/llimage/llimage.cpp               |  4 ++-
 indra/llimage/llimage.h                 |  5 ++-
 indra/llimage/llimagej2c.cpp            | 47 +++++++++----------------
 indra/llkdu/llimagej2ckdu.cpp           | 14 ++++++--
 indra/newview/app_settings/settings.xml | 11 ++++++
 indra/newview/llappviewer.cpp           |  2 +-
 6 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 937655a22d7..7f954410752 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() ;
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index eba8362f1c1..b757547ab8b 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -89,15 +89,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;
 };
 
 //============================================================================
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index fbf4b769e1b..cbb6f75b437 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -262,10 +262,12 @@ 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;
+	// Note: This provides an estimation for the first quality layer of a given discard level
+	// This is however an efficient approximation, as the true discard level boundary would be
+	// in general too big for fast fetching.
+	// For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study
+	if (rate <= 0.f) rate = 1.f/8.f;
+	// Compute w/pow(2,discard_level) and h/pow(2,discard_level)
 	while (discard_level > 0)
 	{
 		if (w < 1 || h < 1)
@@ -274,7 +276,13 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r
 		h >>= 1;
 		discard_level--;
 	}
-	S32 bytes = (S32)((F32)(w*h*comp)*rate);
+	// 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
+	// *TODO: Replace the magic "7" by the number of quality layers in the j2c image
+	S32 bytes;
+	S32 new_bytes = sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/7.f;
+	S32 old_bytes = (S32)((F32)(w*h*comp)*rate);
+	bytes = (LLImage::useNewByteRange() ? new_bytes : old_bytes);
 	bytes = llmax(bytes, calcHeaderSizeJ2C());
 	return bytes;
 }
@@ -284,11 +292,7 @@ S32 LLImageJ2C::calcHeaderSize()
 	return calcHeaderSizeJ2C();
 }
 
-
-// calcDataSize() returns how many bytes to read 
-// to load discard_level (including header and higher discard levels)
-// *TODO: This is deeply wrong. That size should be taken from the image file header or other 
-// relevant infos. In any case, this is only an approximation.
+// 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);
@@ -304,25 +308,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];
 }
@@ -337,8 +322,8 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
 	}
 	while (1)
 	{
-		S32 bytes_needed = calcDataSize(discard_level); // virtual
-		if (bytes >= bytes_needed - (bytes_needed>>2)) // For J2c, up the res at 75% of the optimal number of bytes
+		S32 bytes_needed = calcDataSize(discard_level);
+		if (bytes >= bytes_needed)
 		{
 			break;
 		}
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index fdfab5506ab..4468b8563b3 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -294,6 +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();
+	
+	//kdu_coords idx; idx.x = 0; idx.y = 0;
+	//kdu_dims tile_indices_in;  
+	//mCodeStreamp->get_valid_tiles(tile_indices_in);
+	//mCodeStreamp->create_tile(idx+tile_indices_in.pos);
+	//int layers = mCodeStreamp->get_max_tile_layers();
+	//llinfos << "Merov debug : setupCodeStream, levels = " << mLevels << ", layers = " << layers << llendl;
 
 	// Set the base dimensions
 	base.setSize(dims.size.x, dims.size.y, components);
@@ -390,7 +397,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);
 		
@@ -468,6 +475,9 @@ BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 				{
 					kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos);
 
+					int layers = mCodeStreamp->get_max_tile_layers();
+					llinfos << "Merov debug : decodeImpl, levels = " << mLevels << ", layers = " << layers << llendl;
+
 					// Find the region of the buffer occupied by this
 					// tile.  Note that we have no control over
 					// sub-sampling factors which might have been used
@@ -675,7 +685,7 @@ 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());
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9fff543b130..5fc9c5d863e 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 bea8303d69a..1a68d0317e6 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1947,7 +1947,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);
-- 
GitLab