mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-24 11:16:51 +00:00
xbmc: add RPi backports patch
Signed-off-by: Stephan Raue <stephan@openelec.tv>
This commit is contained in:
parent
bee7e1edac
commit
35c991c604
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user