PageRenderTime 50ms CodeModel.GetById 2ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llimage/llimagej2c.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 580 lines | 411 code | 82 blank | 87 comment | 55 complexity | 3ff587831171bf98d6c3b91017b2db26 MD5 | raw file
  1/** 
  2 * @file llimagej2c.cpp
  3 *
  4 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  5 * Second Life Viewer Source Code
  6 * Copyright (C) 2010, Linden Research, Inc.
  7 * 
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU Lesser General Public
 10 * License as published by the Free Software Foundation;
 11 * version 2.1 of the License only.
 12 * 
 13 * This library is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16 * Lesser General Public License for more details.
 17 * 
 18 * You should have received a copy of the GNU Lesser General Public
 19 * License along with this library; if not, write to the Free Software
 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 21 * 
 22 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 23 * $/LicenseInfo$
 24 */
 25#include "linden_common.h"
 26
 27#include "lldir.h"
 28#include "llimagej2c.h"
 29#include "llmemtype.h"
 30#include "lltimer.h"
 31#include "llmath.h"
 32#include "llmemory.h"
 33
 34typedef LLImageJ2CImpl* (*CreateLLImageJ2CFunction)();
 35typedef void (*DestroyLLImageJ2CFunction)(LLImageJ2CImpl*);
 36typedef const char* (*EngineInfoLLImageJ2CFunction)();
 37
 38// Declare the prototype for theses functions here. Their functionality
 39// will be implemented in other files which define a derived LLImageJ2CImpl
 40// but only ONE static library which has the implementation for these
 41// functions should ever be included.
 42LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl();
 43void fallbackDestroyLLImageJ2CImpl(LLImageJ2CImpl* impl);
 44const char* fallbackEngineInfoLLImageJ2CImpl();
 45
 46// Test data gathering handle
 47LLImageCompressionTester* LLImageJ2C::sTesterp = NULL ;
 48const std::string sTesterName("ImageCompressionTester");
 49
 50//static
 51std::string LLImageJ2C::getEngineInfo()
 52{
 53    return fallbackEngineInfoLLImageJ2CImpl();
 54}
 55
 56LLImageJ2C::LLImageJ2C() : 	LLImageFormatted(IMG_CODEC_J2C),
 57							mMaxBytes(0),
 58							mRawDiscardLevel(-1),
 59							mRate(0.0f),
 60							mReversible(FALSE),
 61							mAreaUsedForDataSizeCalcs(0)
 62{
 63	mImpl = fallbackCreateLLImageJ2CImpl();
 64
 65	// Clear data size table
 66	for( S32 i = 0; i <= MAX_DISCARD_LEVEL; i++)
 67	{	// Array size is MAX_DISCARD_LEVEL+1
 68		mDataSizes[i] = 0;
 69	}
 70
 71	// If that test log has ben requested but not yet created, create it
 72	if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName))
 73	{
 74		sTesterp = new LLImageCompressionTester() ;
 75		if (!sTesterp->isValid())
 76		{
 77			delete sTesterp;
 78			sTesterp = NULL;
 79		}
 80	}
 81}
 82
 83// virtual
 84LLImageJ2C::~LLImageJ2C()
 85{
 86	if ( mImpl )
 87	{
 88        fallbackDestroyLLImageJ2CImpl(mImpl);
 89	}
 90}
 91
 92// virtual
 93void LLImageJ2C::resetLastError()
 94{
 95	mLastError.clear();
 96}
 97
 98//virtual
 99void LLImageJ2C::setLastError(const std::string& message, const std::string& filename)
100{
101	mLastError = message;
102	if (!filename.empty())
103		mLastError += std::string(" FILE: ") + filename;
104}
105
106// virtual
107S8  LLImageJ2C::getRawDiscardLevel()
108{
109	return mRawDiscardLevel;
110}
111
112BOOL LLImageJ2C::updateData()
113{
114	BOOL res = TRUE;
115	resetLastError();
116
117	// Check to make sure that this instance has been initialized with data
118	if (!getData() || (getDataSize() < 16))
119	{
120		setLastError("LLImageJ2C uninitialized");
121		res = FALSE;
122	}
123	else 
124	{
125		res = mImpl->getMetadata(*this);
126	}
127
128	if (res)
129	{
130		// SJB: override discard based on mMaxBytes elsewhere
131		S32 max_bytes = getDataSize(); // mMaxBytes ? mMaxBytes : getDataSize();
132		S32 discard = calcDiscardLevelBytes(max_bytes);
133		setDiscardLevel(discard);
134	}
135
136	if (!mLastError.empty())
137	{
138		LLImage::setLastError(mLastError);
139	}
140	return res;
141}
142
143BOOL LLImageJ2C::initDecode(LLImageRaw &raw_image, int discard_level, int* region)
144{
145	return mImpl->initDecode(*this,raw_image,discard_level,region);
146}
147
148BOOL LLImageJ2C::initEncode(LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels)
149{
150	return mImpl->initEncode(*this,raw_image,blocks_size,precincts_size,levels);
151}
152
153BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
154{
155	return decodeChannels(raw_imagep, decode_time, 0, 4);
156}
157
158
159// Returns TRUE to mean done, whether successful or not.
160BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
161{
162	LLTimer elapsed;
163	LLMemType mt1(mMemType);
164
165	BOOL res = TRUE;
166	
167	resetLastError();
168
169	// Check to make sure that this instance has been initialized with data
170	if (!getData() || (getDataSize() < 16))
171	{
172		setLastError("LLImageJ2C uninitialized");
173		res = TRUE; // done
174	}
175	else
176	{
177		// Update the raw discard level
178		updateRawDiscardLevel();
179		mDecoding = TRUE;
180		res = mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count);
181	}
182	
183	if (res)
184	{
185		if (!mDecoding)
186		{
187			// Failed
188			raw_imagep->deleteData();
189		}
190		else
191		{
192			mDecoding = FALSE;
193		}
194	}
195
196	if (!mLastError.empty())
197	{
198		LLImage::setLastError(mLastError);
199	}
200	
201	LLImageCompressionTester* tester = (LLImageCompressionTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
202	if (tester)
203	{
204		// Decompression stat gathering
205		// Note that we *do not* take into account the decompression failures data so we might overestimate the time spent processing
206
207		// Always add the decompression time to the stat
208		tester->updateDecompressionStats(elapsed.getElapsedTimeF32()) ;
209		if (res)
210		{
211			// The whole data stream is finally decompressed when res is returned as TRUE
212			tester->updateDecompressionStats(this->getDataSize(), raw_imagep->getDataSize()) ;
213		}
214	}
215
216	return res;
217}
218
219
220BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 encode_time)
221{
222	return encode(raw_imagep, NULL, encode_time);
223}
224
225
226BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time)
227{
228	LLTimer elapsed;
229	LLMemType mt1(mMemType);
230	resetLastError();
231	BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible);
232	if (!mLastError.empty())
233	{
234		LLImage::setLastError(mLastError);
235	}
236
237	LLImageCompressionTester* tester = (LLImageCompressionTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
238	if (tester)
239	{
240		// Compression stat gathering
241		// Note that we *do not* take into account the compression failures cases so we night overestimate the time spent processing
242
243		// Always add the compression time to the stat
244		tester->updateCompressionStats(elapsed.getElapsedTimeF32()) ;
245		if (res)
246		{
247			// The whole data stream is finally compressed when res is returned as TRUE
248			tester->updateCompressionStats(this->getDataSize(), raw_imagep->getDataSize()) ;
249		}
250	}
251
252	return res;
253}
254
255//static
256S32 LLImageJ2C::calcHeaderSizeJ2C()
257{
258	return FIRST_PACKET_SIZE; // Hack. just needs to be >= actual header size...
259}
260
261//static
262S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
263{
264	// Note: this only provides an *estimate* of the size in bytes of an image level
265	// *TODO: find a way to read the true size (when available) and convey the fact
266	// that the result is an estimate in the other cases
267	if (rate <= 0.f) rate = .125f;
268	while (discard_level > 0)
269	{
270		if (w < 1 || h < 1)
271			break;
272		w >>= 1;
273		h >>= 1;
274		discard_level--;
275	}
276	S32 bytes = (S32)((F32)(w*h*comp)*rate);
277	bytes = llmax(bytes, calcHeaderSizeJ2C());
278	return bytes;
279}
280
281S32 LLImageJ2C::calcHeaderSize()
282{
283	return calcHeaderSizeJ2C();
284}
285
286
287// calcDataSize() returns how many bytes to read 
288// to load discard_level (including header and higher discard levels)
289S32 LLImageJ2C::calcDataSize(S32 discard_level)
290{
291	discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL);
292
293	if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) 
294		|| mDataSizes[0] == 0)
295	{
296		mAreaUsedForDataSizeCalcs = getHeight() * getWidth();
297		
298		S32 level = MAX_DISCARD_LEVEL;	// Start at the highest discard
299		while ( level >= 0 )
300		{
301			mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
302			level--;
303		}
304
305		/* This is technically a more correct way to calculate the size required
306		   for each discard level, since they should include the size needed for
307		   lower levels.   Unfortunately, this doesn't work well and will lead to 
308		   download stalls.  The true correct way is to parse the header.  This will
309		   all go away with http textures at some point.
310
311		// Calculate the size for each discard level.   Lower levels (higher quality)
312		// contain the cumulative size of higher levels		
313		S32 total_size = calcHeaderSizeJ2C();
314
315		S32 level = MAX_DISCARD_LEVEL;	// Start at the highest discard
316		while ( level >= 0 )
317		{	// Add in this discard level and all before it
318			total_size += calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
319			mDataSizes[level] = total_size;
320			level--;
321		}
322		*/
323	}
324	return mDataSizes[discard_level];
325}
326
327S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
328{
329	llassert(bytes >= 0);
330	S32 discard_level = 0;
331	if (bytes == 0)
332	{
333		return MAX_DISCARD_LEVEL;
334	}
335	while (1)
336	{
337		S32 bytes_needed = calcDataSize(discard_level); // virtual
338		if (bytes >= bytes_needed - (bytes_needed>>2)) // For J2c, up the res at 75% of the optimal number of bytes
339		{
340			break;
341		}
342		discard_level++;
343		if (discard_level >= MAX_DISCARD_LEVEL)
344		{
345			break;
346		}
347	}
348	return discard_level;
349}
350
351void LLImageJ2C::setRate(F32 rate)
352{
353	mRate = rate;
354}
355
356void LLImageJ2C::setMaxBytes(S32 max_bytes)
357{
358	mMaxBytes = max_bytes;
359}
360
361void LLImageJ2C::setReversible(const BOOL reversible)
362{
363 	mReversible = reversible;
364}
365
366
367BOOL LLImageJ2C::loadAndValidate(const std::string &filename)
368{
369	BOOL res = TRUE;
370	
371	resetLastError();
372
373	S32 file_size = 0;
374	LLAPRFile infile ;
375	infile.open(filename, LL_APR_RB, NULL, &file_size);
376	apr_file_t* apr_file = infile.getFileHandle() ;
377	if (!apr_file)
378	{
379		setLastError("Unable to open file for reading", filename);
380		res = FALSE;
381	}
382	else if (file_size == 0)
383	{
384		setLastError("File is empty",filename);
385		res = FALSE;
386	}
387	else
388	{
389		U8 *data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), file_size);
390		apr_size_t bytes_read = file_size;
391		apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read	
392		infile.close() ;
393
394		if (s != APR_SUCCESS || (S32)bytes_read != file_size)
395		{
396			FREE_MEM(LLImageBase::getPrivatePool(), data);
397			setLastError("Unable to read entire file");
398			res = FALSE;
399		}
400		else
401		{
402			res = validate(data, file_size);
403		}
404	}
405	
406	if (!mLastError.empty())
407	{
408		LLImage::setLastError(mLastError);
409	}
410	
411	return res;
412}
413
414
415BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
416{
417	LLMemType mt1(mMemType);
418
419	resetLastError();
420	
421	setData(data, file_size);
422
423	BOOL res = updateData();
424	if ( res )
425	{
426		// Check to make sure that this instance has been initialized with data
427		if (!getData() || (0 == getDataSize()))
428		{
429			setLastError("LLImageJ2C uninitialized");
430			res = FALSE;
431		}
432		else
433		{
434			res = mImpl->getMetadata(*this);
435		}
436	}
437	
438	if (!mLastError.empty())
439	{
440		LLImage::setLastError(mLastError);
441	}
442	return res;
443}
444
445void LLImageJ2C::decodeFailed()
446{
447	mDecoding = FALSE;
448}
449
450void LLImageJ2C::updateRawDiscardLevel()
451{
452	mRawDiscardLevel = mMaxBytes ? calcDiscardLevelBytes(mMaxBytes) : mDiscardLevel;
453}
454
455LLImageJ2CImpl::~LLImageJ2CImpl()
456{
457}
458
459//----------------------------------------------------------------------------------------------
460// Start of LLImageCompressionTester
461//----------------------------------------------------------------------------------------------
462LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTesterBasic(sTesterName) 
463{
464	addMetric("Time Decompression (s)");
465	addMetric("Volume In Decompression (kB)");
466	addMetric("Volume Out Decompression (kB)");
467	addMetric("Decompression Ratio (x:1)");
468	addMetric("Perf Decompression (kB/s)");
469
470	addMetric("Time Compression (s)");
471	addMetric("Volume In Compression (kB)");
472	addMetric("Volume Out Compression (kB)");
473	addMetric("Compression Ratio (x:1)");
474	addMetric("Perf Compression (kB/s)");
475
476	mRunBytesInDecompression = 0;
477	mRunBytesInCompression = 0;
478
479	mTotalBytesInDecompression = 0;
480	mTotalBytesOutDecompression = 0;
481	mTotalBytesInCompression = 0;
482	mTotalBytesOutCompression = 0;
483
484	mTotalTimeDecompression = 0.0f;
485	mTotalTimeCompression = 0.0f;
486}
487
488LLImageCompressionTester::~LLImageCompressionTester()
489{
490	outputTestResults();
491	LLImageJ2C::sTesterp = NULL;
492}
493
494//virtual 
495void LLImageCompressionTester::outputTestRecord(LLSD *sd) 
496{	
497	std::string currentLabel = getCurrentLabelName();
498
499	F32 decompressionPerf = 0.0f;
500	F32 compressionPerf   = 0.0f;
501	F32 decompressionRate = 0.0f;
502	F32 compressionRate   = 0.0f;
503
504	F32 totalkBInDecompression  = (F32)(mTotalBytesInDecompression)  / 1000.0;
505	F32 totalkBOutDecompression = (F32)(mTotalBytesOutDecompression) / 1000.0;
506	F32 totalkBInCompression    = (F32)(mTotalBytesInCompression)    / 1000.0;
507	F32 totalkBOutCompression   = (F32)(mTotalBytesOutCompression)   / 1000.0;
508	
509	if (!is_approx_zero(mTotalTimeDecompression))
510	{
511		decompressionPerf = totalkBInDecompression / mTotalTimeDecompression;
512	}
513	if (!is_approx_zero(totalkBInDecompression))
514	{
515		decompressionRate = totalkBOutDecompression / totalkBInDecompression;
516	}
517	if (!is_approx_zero(mTotalTimeCompression))
518	{
519		compressionPerf = totalkBInCompression / mTotalTimeCompression;
520	}
521	if (!is_approx_zero(totalkBOutCompression))
522	{
523		compressionRate = totalkBInCompression / totalkBOutCompression;
524	}
525
526	(*sd)[currentLabel]["Time Decompression (s)"]		= (LLSD::Real)mTotalTimeDecompression;
527	(*sd)[currentLabel]["Volume In Decompression (kB)"]	= (LLSD::Real)totalkBInDecompression;
528	(*sd)[currentLabel]["Volume Out Decompression (kB)"]= (LLSD::Real)totalkBOutDecompression;
529	(*sd)[currentLabel]["Decompression Ratio (x:1)"]	= (LLSD::Real)decompressionRate;
530	(*sd)[currentLabel]["Perf Decompression (kB/s)"]	= (LLSD::Real)decompressionPerf;
531
532	(*sd)[currentLabel]["Time Compression (s)"]			= (LLSD::Real)mTotalTimeCompression;
533	(*sd)[currentLabel]["Volume In Compression (kB)"]	= (LLSD::Real)totalkBInCompression;
534	(*sd)[currentLabel]["Volume Out Compression (kB)"]	= (LLSD::Real)totalkBOutCompression;
535	(*sd)[currentLabel]["Compression Ratio (x:1)"]		= (LLSD::Real)compressionRate;
536	(*sd)[currentLabel]["Perf Compression (kB/s)"]		= (LLSD::Real)compressionPerf;
537}
538
539void LLImageCompressionTester::updateCompressionStats(const F32 deltaTime) 
540{
541	mTotalTimeCompression += deltaTime;
542}
543
544void LLImageCompressionTester::updateCompressionStats(const S32 bytesCompress, const S32 bytesRaw) 
545{
546	mTotalBytesInCompression += bytesRaw;
547	mRunBytesInCompression += bytesRaw;
548	mTotalBytesOutCompression += bytesCompress;
549	if (mRunBytesInCompression > (1000000))
550	{
551		// Output everything
552		outputTestResults();
553		// Reset the compression data of the run
554		mRunBytesInCompression = 0;
555	}
556}
557
558void LLImageCompressionTester::updateDecompressionStats(const F32 deltaTime) 
559{
560	mTotalTimeDecompression += deltaTime;
561}
562
563void LLImageCompressionTester::updateDecompressionStats(const S32 bytesIn, const S32 bytesOut) 
564{
565	mTotalBytesInDecompression += bytesIn;
566	mRunBytesInDecompression += bytesIn;
567	mTotalBytesOutDecompression += bytesOut;
568	if (mRunBytesInDecompression > (1000000))
569	{
570		// Output everything
571		outputTestResults();
572		// Reset the decompression data of the run
573		mRunBytesInDecompression = 0;
574	}
575}
576
577//----------------------------------------------------------------------------------------------
578// End of LLTexturePipelineTester
579//----------------------------------------------------------------------------------------------
580