xbmc: add RPi backports patch

Signed-off-by: Stephan Raue <stephan@openelec.tv>
This commit is contained in:
Stephan Raue 2014-04-12 22:42:48 +02:00
parent bee7e1edac
commit 35c991c604
4 changed files with 16127 additions and 958 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,208 +0,0 @@
From 7388e8be7cd5e78100532ebf0dba15dccb7b03f8 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Tue, 3 Dec 2013 15:51:39 +0000
Subject: [PATCH] Faster and simpler portable implementation of
MathUtils::round_int().
Much as I like a bit of inline assembler, I have also removed the ARM versions
of MathUtils::truncate_int() and MathUtils::round_int(). The former was just
how any sane compiler should have assembled a cast from double to signed int
anyway. The latter was a much too complicated way to achieve the desired
effect, and was switched out in most ARM builds anyway in favour of the old
portable implementation that used floor().
Verified that MathUtils::test() still passes, and that GCC is now able to
inline MathUtils::round_int(), where it didn't previously.
I tested on a Raspberry Pi with the default theme, displaying the front page
with the RSS ticker enabled. This saturates the CPU, so I'm measuring the
improvement using the debug window's FPS figure. This patch improves this from
~50.8 FPS to ~52.6 FPS.
---
xbmc/utils/MathUtils.h | 129 +++++++++++++++++++++++--------------------------
1 file changed, 61 insertions(+), 68 deletions(-)
diff --git a/xbmc/utils/MathUtils.h b/xbmc/utils/MathUtils.h
index 96af9f4..0dae77d 100644
--- a/xbmc/utils/MathUtils.h
+++ b/xbmc/utils/MathUtils.h
@@ -34,17 +34,13 @@
#if defined(__ppc__) || \
defined(__powerpc__) || \
- (defined(TARGET_DARWIN_IOS) && defined(__llvm__)) || \
- (defined(TARGET_ANDROID) && defined(__arm__)) || \
- defined(TARGET_RASPBERRY_PI)
+ defined(__arm__)
#define DISABLE_MATHUTILS_ASM_ROUND_INT
#endif
#if defined(__ppc__) || \
defined(__powerpc__) || \
- (defined(TARGET_DARWIN) && defined(__llvm__)) || \
- (defined(TARGET_ANDROID) && defined(__arm__)) || \
- defined(TARGET_RASPBERRY_PI)
+ defined(__arm__)
#define DISABLE_MATHUTILS_ASM_TRUNCATE_INT
#endif
@@ -73,60 +69,63 @@
{
assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
- const float round_to_nearest = 0.5f;
- int i;
#if defined(DISABLE_MATHUTILS_ASM_ROUND_INT)
- i = floor(x + round_to_nearest);
-
-#elif defined(__arm__)
- // From 'ARM-v7-M Architecture Reference Manual' page A7-569:
- // "The floating-point to integer operation (vcvt) [normally] uses the Round towards Zero rounding mode"
- // Because of this...we must use some less-than-straightforward logic to perform this operation without
- // changing the rounding mode flags
-
- /* The assembly below implements the following logic:
- if (x < 0)
- inc = -0.5f
- else
- inc = 0.5f
- int_val = trunc(x+inc);
- err = x - int_val;
- if (err == 0.5f)
- int_val++;
- return int_val;
- */
+ /* This implementation warrants some further explanation.
+ *
+ * First, a couple of notes on rounding:
+ * 1) C casts from float/double to integer round towards zero.
+ * 2) Float/double additions are rounded according to the normal rules,
+ * in other words: on some architectures, it's fixed at compile-time,
+ * and on others it can be set using fesetround()). The following
+ * analysis assumes round-to-nearest with ties rounding to even. This
+ * is a fairly sensible choice, and is the default with ARM VFP.
+ *
+ * What this function wants is round-to-nearest with ties rounding to
+ * +infinity. This isn't an IEEE rounding mode, even if we could guarantee
+ * that all architectures supported fesetround(), which they don't. Instead,
+ * this adds an offset of 2147483648.5 (= 0x80000000.8p0), then casts to
+ * an unsigned int (crucially, all possible inputs are now in a range where
+ * round to zero acts the same as round to -infinity) and then subtracts
+ * 0x80000000 in the integer domain. The 0.5 component of the offset
+ * converts what is effectively a round down into a round to nearest, with
+ * ties rounding up, as desired.
+ *
+ * There is a catch, that because there is a double rounding, there is a
+ * small region where the input falls just *below* a tie, where the addition
+ * of the offset causes a round *up* to an exact integer, due to the finite
+ * level of precision available in floating point. You need to be aware of
+ * this when calling this function, although at present it is not believed
+ * that XBMC ever attempts to round numbers in this window.
+ *
+ * It is worth proving the size of the affected window. Recall that double
+ * precision employs a mantissa of 52 bits.
+ * 1) For all inputs -0.5 <= x <= INT_MAX
+ * Once the offset is applied, the most significant binary digit in the
+ * floating-point representation is +2^31.
+ * At this magnitude, the smallest step representable in double precision
+ * is 2^31 / 2^52 = 0.000000476837158203125
+ * So the size of the range which is rounded up due to the addition is
+ * half the size of this step, or 0.0000002384185791015625
+ *
+ * 2) For all inputs INT_MIN/2 < x < -0.5
+ * Once the offset is applied, the most significant binary digit in the
+ * floating-point representation is +2^30.
+ * At this magnitude, the smallest step representable in double precision
+ * is 2^30 / 2^52 = 0.0000002384185791015625
+ * So the size of the range which is rounded up due to the addition is
+ * half the size of this step, or 0.00000011920928955078125
+ *
+ * 3) For all inputs INT_MIN <= x <= INT_MIN/2
+ * The representation once the offset is applied has equal or greater
+ * precision than the input, so the addition does not cause rounding.
+ */
+ return ((unsigned int) (x + 0x80000000.8p0)) - 0x80000000;
- __asm__ __volatile__ (
-#if defined(__ARM_PCS_VFP)
- "fconstd d1,#%G[rnd_val] \n\t" // Copy round_to_nearest into a working register (d1 = 0.5)
#else
- "vmov.F64 d1,%[rnd_val] \n\t"
-#endif
- "fcmpezd %P[value] \n\t" // Check value against zero (value == 0?)
- "fmstat \n\t" // Copy the floating-point status flags into the general-purpose status flags
- "it mi \n\t"
- "vnegmi.F64 d1, d1 \n\t" // if N-flag is set, negate round_to_nearest (if (value < 0) d1 = -1 * d1)
- "vadd.F64 d1,%P[value],d1 \n\t" // Add round_to_nearest to value, store result in working register (d1 += value)
- "vcvt.S32.F64 s3,d1 \n\t" // Truncate(round towards zero) (s3 = (int)d1)
- "vmov %[result],s3 \n\t" // Store the integer result in a general-purpose register (result = s3)
- "vcvt.F64.S32 d1,s3 \n\t" // Convert back to floating-point (d1 = (double)s3)
- "vsub.F64 d1,%P[value],d1 \n\t" // Calculate the error (d1 = value - d1)
-#if defined(__ARM_PCS_VFP)
- "fconstd d2,#%G[rnd_val] \n\t" // d2 = 0.5;
-#else
- "vmov.F64 d2,%[rnd_val] \n\t"
-#endif
- "fcmped d1, d2 \n\t" // (d1 == 0.5?)
- "fmstat \n\t" // Copy the floating-point status flags into the general-purpose status flags
- "it eq \n\t"
- "addeq %[result],#1 \n\t" // (if (d1 == d2) result++;)
- : [result] "=r"(i) // Outputs
- : [rnd_val] "Dv" (round_to_nearest), [value] "w"(x) // Inputs
- : "d1", "d2", "s3" // Clobbers
- );
-
-#elif defined(__SSE2__)
+ const float round_to_nearest = 0.5f;
+ int i;
+#if defined(__SSE2__)
const float round_dn_to_nearest = 0.4999999f;
i = (x > 0) ? _mm_cvttsd_si32(_mm_set_sd(x + round_to_nearest)) : _mm_cvttsd_si32(_mm_set_sd(x - round_dn_to_nearest));
@@ -150,8 +149,8 @@
);
#endif
-
return i;
+#endif
}
/*! \brief Truncate to nearest integer.
@@ -165,20 +164,13 @@
{
assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
- int i;
#if defined(DISABLE_MATHUTILS_ASM_TRUNCATE_INT)
- return i = (int)x;
-
-#elif defined(__arm__)
- __asm__ __volatile__ (
- "vcvt.S32.F64 %[result],%P[value] \n\t" // Truncate(round towards zero) and store the result
- : [result] "=w"(i) // Outputs
- : [value] "w"(x) // Inputs
- );
- return i;
+ return x;
-#elif defined(TARGET_WINDOWS)
+#else
+ int i;
+#if defined(TARGET_WINDOWS)
const float round_towards_m_i = -0.5f;
__asm
{
@@ -204,6 +196,7 @@
if (x < 0)
i = -i;
return (i);
+#endif
}
inline int64_t abs(int64_t a)
--
1.8.5.1

View File

@ -1,392 +0,0 @@
From b039984e34db62d1b0d9ac2f673b4658fe0f64a5 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 12 Feb 2014 18:43:14 +0000
Subject: [PATCH] Improved file buffering in CArchive.
CArchive already did some file buffering, but only on writes. Added the
equivalent code for reads. Also improved the write buffer case so that it
only ever issues sector-aligned writes (the read code does this from the
start). Shuffled various bits of code into the header file to squeeze a bit
more performance out of it.
Profiled the effect on CFileItemList::Archive(), which is one of the slow
parts of the process of reopening a media library, on a non-overclocked
Raspberry Pi. Times are in seconds:
TV shows (253 items)
Before After
Mean StdDev Mean StdDev Confidence Change
0.394 0.005 0.151 0.005 100.0% +159.8%
Songs (4115 items)
Before After
Mean StdDev Mean StdDev Confidence Change
2.931 0.045 0.690 0.019 100.0% +324.4%
---
xbmc/utils/Archive.cpp | 158 ++++++++++++++++++-------------------------------
xbmc/utils/Archive.h | 130 ++++++++++++++++++++++++++++++++++------
2 files changed, 172 insertions(+), 116 deletions(-)
diff --git a/xbmc/utils/Archive.cpp b/xbmc/utils/Archive.cpp
index 4519e19..b2ad273 100644
--- a/xbmc/utils/Archive.cpp
+++ b/xbmc/utils/Archive.cpp
@@ -30,24 +30,29 @@
using namespace XFILE;
-#define BUFFER_MAX 4096
-
CArchive::CArchive(CFile* pFile, int mode)
{
m_pFile = pFile;
m_iMode = mode;
- m_pBuffer = new uint8_t[BUFFER_MAX];
- memset(m_pBuffer, 0, BUFFER_MAX);
-
- m_BufferPos = 0;
+ m_pBuffer = new uint8_t[CARCHIVE_BUFFER_MAX];
+ memset(m_pBuffer, 0, CARCHIVE_BUFFER_MAX);
+ if (mode == load)
+ {
+ m_BufferPos = m_pBuffer + CARCHIVE_BUFFER_MAX;
+ m_BufferRemain = 0;
+ }
+ else
+ {
+ m_BufferPos = m_pBuffer;
+ m_BufferRemain = CARCHIVE_BUFFER_MAX;
+ }
}
CArchive::~CArchive()
{
FlushBuffer();
delete[] m_pBuffer;
- m_BufferPos = 0;
}
void CArchive::Close()
@@ -214,89 +219,6 @@ bool CArchive::IsStoring()
return *this;
}
-inline CArchive& CArchive::streamout(const void* dataPtr, size_t size)
-{
- const uint8_t* ptr = (const uint8_t*)dataPtr;
-
- if (size + m_BufferPos >= BUFFER_MAX)
- {
- FlushBuffer();
- while (size >= BUFFER_MAX)
- {
- memcpy(m_pBuffer, ptr, BUFFER_MAX);
- m_BufferPos = BUFFER_MAX;
- ptr += BUFFER_MAX;
- size -= BUFFER_MAX;
- FlushBuffer();
- }
- }
-
- memcpy(m_pBuffer + m_BufferPos, ptr, size);
- m_BufferPos += size;
-
- return *this;
-}
-
-CArchive& CArchive::operator>>(float& f)
-{
- return streamin(&f, sizeof(f));
-}
-
-CArchive& CArchive::operator>>(double& d)
-{
- return streamin(&d, sizeof(d));
-}
-
-CArchive& CArchive::operator>>(short int& s)
-{
- return streamin(&s, sizeof(s));
-}
-
-CArchive& CArchive::operator>>(unsigned short int& us)
-{
- return streamin(&us, sizeof(us));
-}
-
-CArchive& CArchive::operator>>(int& i)
-{
- return streamin(&i, sizeof(i));
-}
-
-CArchive& CArchive::operator>>(unsigned int& ui)
-{
- return streamin(&ui, sizeof(ui));
-}
-
-CArchive& CArchive::operator>>(long int& l)
-{
- return streamin(&l, sizeof(l));
-}
-
-CArchive& CArchive::operator>>(unsigned long int& ul)
-{
- return streamin(&ul, sizeof(ul));
-}
-
-CArchive& CArchive::operator>>(long long int& ll)
-{
- return streamin(&ll, sizeof(ll));
-}
-
-CArchive& CArchive::operator>>(unsigned long long int& ull)
-{
- return streamin(&ull, sizeof(ull));
-}
-
-CArchive& CArchive::operator>>(bool& b)
-{
- return streamin(&b, sizeof(b));
-}
-
-CArchive& CArchive::operator>>(char& c)
-{
- return streamin(&c, sizeof(c));
-}
-
CArchive& CArchive::operator>>(std::string& str)
{
size_t iLength = 0;
@@ -450,23 +372,61 @@ bool CArchive::IsStoring()
return *this;
}
-inline CArchive& CArchive::streamin(void* dataPtr, const size_t size)
+void CArchive::FlushBuffer()
{
- size_t read = m_pFile->Read(dataPtr, size);
- if (read < size)
+ if (m_iMode == store && m_BufferPos != m_pBuffer)
{
- CLog::Log(LOGERROR, "%s: can't stream out: requested %lu bytes, was read %lu bytes", __FUNCTION__, (unsigned long)size, (unsigned long)read);
- memset(dataPtr, 0, size);
+ m_pFile->Write(m_pBuffer, m_BufferPos - m_pBuffer);
+ m_BufferPos = m_pBuffer;
+ m_BufferRemain = CARCHIVE_BUFFER_MAX;
}
+}
+CArchive &CArchive::streamout_bufferwrap(const uint8_t *ptr, size_t size)
+{
+ do
+ {
+ size_t chunkSize = std::min(size, m_BufferRemain);
+ m_BufferPos = std::copy(ptr, ptr + chunkSize, m_BufferPos);
+ ptr += chunkSize;
+ size -= chunkSize;
+ m_BufferRemain -= chunkSize;
+ if (m_BufferRemain == 0)
+ FlushBuffer();
+ } while (size > 0);
return *this;
}
-void CArchive::FlushBuffer()
+void CArchive::FillBuffer()
{
- if (m_BufferPos > 0)
+ if (m_iMode == load && m_BufferRemain == 0)
{
- m_pFile->Write(m_pBuffer, m_BufferPos);
- m_BufferPos = 0;
+ m_BufferRemain = m_pFile->Read(m_pBuffer, CARCHIVE_BUFFER_MAX);
+ m_BufferPos = m_pBuffer;
}
}
+
+CArchive &CArchive::streamin_bufferwrap(uint8_t *ptr, size_t size)
+{
+ uint8_t *orig_ptr = ptr;
+ size_t orig_size = size;
+ do
+ {
+ if (m_BufferRemain == 0)
+ {
+ FillBuffer();
+ if (m_BufferRemain < CARCHIVE_BUFFER_MAX && m_BufferRemain < size)
+ {
+ CLog::Log(LOGERROR, "%s: can't stream in: requested %lu bytes, was read %lu bytes", __FUNCTION__, (unsigned long) orig_size, (unsigned long) (ptr - orig_ptr + m_BufferRemain));
+ memset(orig_ptr, 0, orig_size);
+ return *this;
+ }
+ }
+ size_t chunkSize = std::min(size, m_BufferRemain);
+ ptr = std::copy(m_BufferPos, m_BufferPos + chunkSize, ptr);
+ m_BufferPos += chunkSize;
+ m_BufferRemain -= chunkSize;
+ size -= chunkSize;
+ } while (size > 0);
+ return *this;
+}
diff --git a/xbmc/utils/Archive.h b/xbmc/utils/Archive.h
index 0148fcb..5b25be5 100644
--- a/xbmc/utils/Archive.h
+++ b/xbmc/utils/Archive.h
@@ -24,6 +24,8 @@
#include <vector>
#include "PlatformDefs.h" // for SYSTEMTIME
+#define CARCHIVE_BUFFER_MAX 4096
+
namespace XFILE
{
class CFile;
@@ -77,18 +79,66 @@ class CArchive
CArchive& operator<<(const std::vector<int>& iArray);
// loading
- CArchive& operator>>(float& f);
- CArchive& operator>>(double& d);
- CArchive& operator>>(short int& s);
- CArchive& operator>>(unsigned short int& us);
- CArchive& operator>>(int& i);
- CArchive& operator>>(unsigned int& ui);
- CArchive& operator>>(long int& l);
- CArchive& operator>>(unsigned long int& ul);
- CArchive& operator>>(long long int& ll);
- CArchive& operator>>(unsigned long long int& ull);
- CArchive& operator>>(bool& b);
- CArchive& operator>>(char& c);
+ inline CArchive& operator>>(float& f)
+ {
+ return streamin(&f, sizeof(f));
+ }
+
+ inline CArchive& operator>>(double& d)
+ {
+ return streamin(&d, sizeof(d));
+ }
+
+ inline CArchive& operator>>(short int& s)
+ {
+ return streamin(&s, sizeof(s));
+ }
+
+ inline CArchive& operator>>(unsigned short int& us)
+ {
+ return streamin(&us, sizeof(us));
+ }
+
+ inline CArchive& operator>>(int& i)
+ {
+ return streamin(&i, sizeof(i));
+ }
+
+ inline CArchive& operator>>(unsigned int& ui)
+ {
+ return streamin(&ui, sizeof(ui));
+ }
+
+ inline CArchive& operator>>(long int& l)
+ {
+ return streamin(&l, sizeof(l));
+ }
+
+ inline CArchive& operator>>(unsigned long int& ul)
+ {
+ return streamin(&ul, sizeof(ul));
+ }
+
+ inline CArchive& operator>>(long long int& ll)
+ {
+ return streamin(&ll, sizeof(ll));
+ }
+
+ inline CArchive& operator>>(unsigned long long int& ull)
+ {
+ return streamin(&ull, sizeof(ull));
+ }
+
+ inline CArchive& operator>>(bool& b)
+ {
+ return streamin(&b, sizeof(b));
+ }
+
+ inline CArchive& operator>>(char& c)
+ {
+ return streamin(&c, sizeof(c));
+ }
+
CArchive& operator>>(std::string &str);
CArchive& operator>>(std::wstring& wstr);
CArchive& operator>>(SYSTEMTIME& time);
@@ -105,12 +155,58 @@ class CArchive
enum Mode {load = 0, store};
protected:
- CArchive& streamout(const void* dataPtr, size_t size);
- CArchive& streamin(void* dataPtr, const size_t size);
- void FlushBuffer();
+ inline CArchive &streamout(const void *dataPtr, size_t size)
+ {
+ const uint8_t *ptr = (const uint8_t *) dataPtr;
+ /* Note, the buffer is flushed as soon as it is full (m_BufferRemain == size) rather
+ * than waiting until we attempt to put more data into an already full buffer */
+ if (m_BufferRemain > size)
+ {
+ switch (size)
+ {
+ case 1: *m_BufferPos++ = *ptr; m_BufferRemain--; break;
+ case 2: *(uint16_t *) m_BufferPos = *(const uint16_t *) ptr; m_BufferPos += 2; m_BufferRemain -= 2; break;
+ case 4: *(uint32_t *) m_BufferPos = *(const uint32_t *) ptr; m_BufferPos += 4; m_BufferRemain -= 4; break;
+ default: m_BufferPos = std::copy(ptr, ptr + size, m_BufferPos); m_BufferRemain -= size; break;
+ }
+ return *this;
+ }
+ else
+ {
+ return streamout_bufferwrap(ptr, size);
+ }
+ }
+
+ inline CArchive &streamin(void *dataPtr, size_t size)
+ {
+ uint8_t *ptr = (uint8_t *) dataPtr;
+ /* Note, refilling the buffer is deferred until we know we need to read more from it */
+ if (m_BufferRemain >= size)
+ {
+ switch (size)
+ {
+ case 1: *ptr = *m_BufferPos++; m_BufferRemain--; break;
+ case 2: *(uint16_t *) ptr = *(const uint16_t *) m_BufferPos; m_BufferPos += 2; m_BufferRemain -= 2; break;
+ case 4: *(uint32_t *) ptr = *(const uint32_t *) m_BufferPos; m_BufferPos += 4; m_BufferRemain -= 4; break;
+ default: std::copy(m_BufferPos, m_BufferPos + size, ptr); m_BufferPos += size; m_BufferRemain -= size; break;
+ }
+ return *this;
+ }
+ else
+ {
+ return streamin_bufferwrap(ptr, size);
+ }
+ }
+
XFILE::CFile* m_pFile;
int m_iMode;
uint8_t *m_pBuffer;
- int m_BufferPos;
-};
+ uint8_t *m_BufferPos;
+ size_t m_BufferRemain;
+private:
+ void FlushBuffer();
+ CArchive &streamout_bufferwrap(const uint8_t *ptr, size_t size);
+ void FillBuffer();
+ CArchive &streamin_bufferwrap(uint8_t *ptr, size_t size);
+};
--
1.8.5.1

View File

@ -1,358 +0,0 @@
From c38d57f0580372afa46602f8635d6bb284f0a2af Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Tue, 26 Nov 2013 20:09:48 +0000
Subject: [PATCH 1/2] Add caching of infolabels
The functions CGUIInfoLabel::GetLabel and CGUIInfoLabel::GetItemLabel take
a number of strings returned from CGUIInfoManager::GetImage or
CGUIInfoManager::GetLabel, and combine them with various constant strings
which were determined during CGUIInfoLabel::Parse.
Rather than perform all the string operations on every call, this patch
changes to use a two-pass process: first it queries all the GetImage/GetLabel
strings, and then only if at least one of them has changed does it bother
rebuilding the resultant string - otherwise it re-uses the copy built on a
preceding call.
CGUIInfoLabel::GetLabel/GetItemLabel are also changed to return string
references, rather than forcing an additional string copy.
I have measured the effect while the Videos window of the default skin was
open (but idle) on a Raspberry Pi, and this reduced the CPU usage by 0.8%
from 36.2% to 35.4%:
Before After
Mean StdDev Mean StdDev Confidence Change
IdleCPU% 36.2 0.5 35.4 0.5 99.9% +2.2%
---
xbmc/guilib/GUIInfoTypes.cpp | 102 +++++++++++++++++++++++++++++++++----------
xbmc/guilib/GUIInfoTypes.h | 11 ++++-
2 files changed, 87 insertions(+), 26 deletions(-)
diff --git a/xbmc/guilib/GUIInfoTypes.cpp b/xbmc/guilib/GUIInfoTypes.cpp
index 5648a77..6bb0eed 100644
--- a/xbmc/guilib/GUIInfoTypes.cpp
+++ b/xbmc/guilib/GUIInfoTypes.cpp
@@ -136,37 +136,64 @@ void CGUIInfoLabel::SetLabel(const CStdString &label, const CStdString &fallback
Parse(label, context);
}
-CStdString CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage, CStdString *fallback /*= NULL*/) const
+const std::string &CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage, CStdString *fallback /*= NULL*/) const
{
- CStdString label;
- for (unsigned int i = 0; i < m_info.size(); i++)
+ for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
{
const CInfoPortion &portion = m_info[i];
if (portion.m_info)
{
- CStdString infoLabel;
+ std::string infoLabel;
if (preferImage)
infoLabel = g_infoManager.GetImage(portion.m_info, contextWindow, fallback);
if (infoLabel.empty())
infoLabel = g_infoManager.GetLabel(portion.m_info, contextWindow, fallback);
- if (!infoLabel.empty())
- label += portion.GetLabel(infoLabel);
+ if (j == m_labelPortions.size())
+ m_labelPortions.push_back(infoLabel);
+ else if (infoLabel != m_labelPortions[j])
+ {
+ m_labelPortions[j] = infoLabel;
+ m_labelDirty = true;
+ }
+ j++;
}
- else
- { // no info, so just append the prefix
- label += portion.m_prefix;
+ }
+ if (m_labelDirty)
+ {
+ m_label.clear();
+ for (unsigned int i = 0, j= 0; i < m_info.size(); i++)
+ {
+ const CInfoPortion &portion = m_info[i];
+ if (portion.m_info)
+ {
+ if (!m_labelPortions[j].empty())
+ m_label += portion.GetLabel(m_labelPortions[j]);
+ j++;
+ }
+ else
+ { // no info, so just append the prefix
+ m_label += portion.m_prefix;
+ }
}
+ if (m_label.empty()) // empty label, use the fallback
+ m_label = m_fallback;
+ m_labelDirty = false;
}
- if (label.empty()) // empty label, use the fallback
- return m_fallback;
- return label;
+ return m_label;
}
-CStdString CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImages, CStdString *fallback /*= NULL*/) const
+const std::string &CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImages, CStdString *fallback /*= NULL*/) const
{
- if (!item->IsFileItem()) return "";
- CStdString label;
- for (unsigned int i = 0; i < m_info.size(); i++)
+ if (!item->IsFileItem())
+ {
+ if (m_itemLabelDirty)
+ {
+ m_itemLabel = "";
+ m_itemLabelDirty = false;
+ }
+ return m_itemLabel;
+ }
+ for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
{
const CInfoPortion &portion = m_info[i];
if (portion.m_info)
@@ -176,17 +203,38 @@ CStdString CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImag
infoLabel = g_infoManager.GetItemImage((const CFileItem *)item, portion.m_info, fallback);
else
infoLabel = g_infoManager.GetItemLabel((const CFileItem *)item, portion.m_info, fallback);
- if (!infoLabel.empty())
- label += portion.GetLabel(infoLabel);
+ if (j == m_itemLabelPortions.size())
+ m_itemLabelPortions.push_back(infoLabel);
+ else if (infoLabel != m_itemLabelPortions[j])
+ {
+ m_itemLabelPortions[j] = infoLabel;
+ m_itemLabelDirty = true;
+ }
+ j++;
}
- else
- { // no info, so just append the prefix
- label += portion.m_prefix;
+ }
+ if (m_itemLabelDirty)
+ {
+ m_itemLabel.clear();
+ for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
+ {
+ const CInfoPortion &portion = m_info[i];
+ if (portion.m_info)
+ {
+ if (!m_itemLabelPortions[j].empty())
+ m_itemLabel += portion.GetLabel(m_itemLabelPortions[j]);
+ j++;
+ }
+ else
+ { // no info, so just append the prefix
+ m_itemLabel += portion.m_prefix;
+ }
}
+ if (m_itemLabel.empty())
+ m_itemLabel = m_fallback;
+ m_itemLabelDirty = false;
}
- if (label.empty())
- return m_fallback;
- return label;
+ return m_itemLabel;
}
bool CGUIInfoLabel::IsEmpty() const
@@ -270,6 +318,12 @@ CStdString CGUIInfoLabel::ReplaceAddonStrings(const CStdString &label)
void CGUIInfoLabel::Parse(const CStdString &label, int context)
{
m_info.clear();
+ m_labelDirty = true;
+ m_label.clear();
+ m_labelPortions.clear();
+ m_itemLabelDirty = true;
+ m_itemLabel.clear();
+ m_itemLabelPortions.clear();
// Step 1: Replace all $LOCALIZE[number] with the real string
CStdString work = ReplaceLocalize(label);
// Step 2: Replace all $ADDON[id number] with the real string
diff --git a/xbmc/guilib/GUIInfoTypes.h b/xbmc/guilib/GUIInfoTypes.h
index 8c1c1dc..418b2c4 100644
--- a/xbmc/guilib/GUIInfoTypes.h
+++ b/xbmc/guilib/GUIInfoTypes.h
@@ -83,7 +83,7 @@ class CGUIInfoLabel
\param fallback if non-NULL, is set to an alternate value to use should the actual value be not appropriate. Defaults to NULL.
\return label (or image).
*/
- CStdString GetLabel(int contextWindow, bool preferImage = false, CStdString *fallback = NULL) const;
+ const std::string &GetLabel(int contextWindow, bool preferImage = false, CStdString *fallback = NULL) const;
/*!
\brief Gets a label (or image) for a given listitem from the info manager.
@@ -92,7 +92,7 @@ class CGUIInfoLabel
\param fallback if non-NULL, is set to an alternate value to use should the actual value be not appropriate. Defaults to NULL.
\return label (or image).
*/
- CStdString GetItemLabel(const CGUIListItem *item, bool preferImage = false, CStdString *fallback = NULL) const;
+ const std::string &GetItemLabel(const CGUIListItem *item, bool preferImage = false, CStdString *fallback = NULL) const;
bool IsConstant() const;
bool IsEmpty() const;
@@ -132,6 +132,13 @@ class CGUIInfoLabel
CStdString m_fallback;
std::vector<CInfoPortion> m_info;
+
+ mutable bool m_labelDirty;
+ mutable std::string m_label;
+ mutable std::vector<std::string> m_labelPortions;
+ mutable bool m_itemLabelDirty;
+ mutable std::string m_itemLabel;
+ mutable std::vector<std::string> m_itemLabelPortions;
};
#endif
--
1.8.5.1
From c84740a80a94c6ed36f57f5fd48744a8fd5fc5b2 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Tue, 10 Dec 2013 01:12:31 +0000
Subject: [PATCH 2/2] De-duplication of string cache for non-item and item
labels
---
xbmc/guilib/GUIInfoTypes.cpp | 50 +++++++++++++++++++++++++-------------------
xbmc/guilib/GUIInfoTypes.h | 4 +---
2 files changed, 29 insertions(+), 25 deletions(-)
diff --git a/xbmc/guilib/GUIInfoTypes.cpp b/xbmc/guilib/GUIInfoTypes.cpp
index 6bb0eed..d096d8e 100644
--- a/xbmc/guilib/GUIInfoTypes.cpp
+++ b/xbmc/guilib/GUIInfoTypes.cpp
@@ -121,7 +121,7 @@ void CGUIInfoColor::Parse(const CStdString &label, int context)
m_color = g_colorManager.GetColor(label);
}
-CGUIInfoLabel::CGUIInfoLabel()
+CGUIInfoLabel::CGUIInfoLabel() : m_labelDirty(true)
{
}
@@ -178,7 +178,10 @@ void CGUIInfoLabel::SetLabel(const CStdString &label, const CStdString &fallback
if (m_label.empty()) // empty label, use the fallback
m_label = m_fallback;
m_labelDirty = false;
+ m_isLabelOfListItem = false;
}
+ else
+ assert(m_isLabelOfListItem == false);
return m_label;
}
@@ -186,12 +189,15 @@ void CGUIInfoLabel::SetLabel(const CStdString &label, const CStdString &fallback
{
if (!item->IsFileItem())
{
- if (m_itemLabelDirty)
+ if (m_labelDirty)
{
- m_itemLabel = "";
- m_itemLabelDirty = false;
+ m_label = "";
+ m_labelDirty = false;
+ m_isLabelOfListItem = true;
}
- return m_itemLabel;
+ else
+ assert(m_isLabelOfListItem == true);
+ return m_label;
}
for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
{
@@ -203,38 +209,41 @@ void CGUIInfoLabel::SetLabel(const CStdString &label, const CStdString &fallback
infoLabel = g_infoManager.GetItemImage((const CFileItem *)item, portion.m_info, fallback);
else
infoLabel = g_infoManager.GetItemLabel((const CFileItem *)item, portion.m_info, fallback);
- if (j == m_itemLabelPortions.size())
- m_itemLabelPortions.push_back(infoLabel);
- else if (infoLabel != m_itemLabelPortions[j])
+ if (j == m_labelPortions.size())
+ m_labelPortions.push_back(infoLabel);
+ else if (infoLabel != m_labelPortions[j])
{
- m_itemLabelPortions[j] = infoLabel;
- m_itemLabelDirty = true;
+ m_labelPortions[j] = infoLabel;
+ m_labelDirty = true;
}
j++;
}
}
- if (m_itemLabelDirty)
+ if (m_labelDirty)
{
- m_itemLabel.clear();
+ m_label.clear();
for (unsigned int i = 0, j = 0; i < m_info.size(); i++)
{
const CInfoPortion &portion = m_info[i];
if (portion.m_info)
{
- if (!m_itemLabelPortions[j].empty())
- m_itemLabel += portion.GetLabel(m_itemLabelPortions[j]);
+ if (!m_labelPortions[j].empty())
+ m_label += portion.GetLabel(m_labelPortions[j]);
j++;
}
else
{ // no info, so just append the prefix
- m_itemLabel += portion.m_prefix;
+ m_label += portion.m_prefix;
}
}
- if (m_itemLabel.empty())
- m_itemLabel = m_fallback;
- m_itemLabelDirty = false;
+ if (m_label.empty())
+ m_label = m_fallback;
+ m_labelDirty = false;
+ m_isLabelOfListItem = true;
}
- return m_itemLabel;
+ else
+ assert(m_isLabelOfListItem == true);
+ return m_label;
}
bool CGUIInfoLabel::IsEmpty() const
@@ -321,9 +330,6 @@ void CGUIInfoLabel::Parse(const CStdString &label, int context)
m_labelDirty = true;
m_label.clear();
m_labelPortions.clear();
- m_itemLabelDirty = true;
- m_itemLabel.clear();
- m_itemLabelPortions.clear();
// Step 1: Replace all $LOCALIZE[number] with the real string
CStdString work = ReplaceLocalize(label);
// Step 2: Replace all $ADDON[id number] with the real string
diff --git a/xbmc/guilib/GUIInfoTypes.h b/xbmc/guilib/GUIInfoTypes.h
index 418b2c4..6d9ebf7 100644
--- a/xbmc/guilib/GUIInfoTypes.h
+++ b/xbmc/guilib/GUIInfoTypes.h
@@ -133,12 +133,10 @@ class CGUIInfoLabel
CStdString m_fallback;
std::vector<CInfoPortion> m_info;
+ mutable bool m_isLabelOfListItem;
mutable bool m_labelDirty;
mutable std::string m_label;
mutable std::vector<std::string> m_labelPortions;
- mutable bool m_itemLabelDirty;
- mutable std::string m_itemLabel;
- mutable std::vector<std::string> m_itemLabelPortions;
};
#endif
--
1.8.5.1