From f6b2d09ec5fee8e101d6fb8f2560b075038301a6 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Thu, 20 Feb 2014 00:05:01 +0100 Subject: [PATCH] xbmc: add PR4143 Signed-off-by: Stephan Raue --- .../xbmc/patches/xbmc-999.80.004-PR4143.patch | 3300 +++++++++++++++++ 1 file changed, 3300 insertions(+) create mode 100644 packages/mediacenter/xbmc/patches/xbmc-999.80.004-PR4143.patch diff --git a/packages/mediacenter/xbmc/patches/xbmc-999.80.004-PR4143.patch b/packages/mediacenter/xbmc/patches/xbmc-999.80.004-PR4143.patch new file mode 100644 index 0000000000..49864eb779 --- /dev/null +++ b/packages/mediacenter/xbmc/patches/xbmc-999.80.004-PR4143.patch @@ -0,0 +1,3300 @@ +From 6b8130456cd24fa6b658df3d7c3066400cd132f1 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Wed, 11 Dec 2013 17:21:54 +0000 +Subject: [PATCH 01/17] Move the reference-counting of Begin and End calls from + DX and GL source files into GUIFontTTF.cpp. + +--- + xbmc/guilib/GUIFontTTF.cpp | 21 ++++++++ + xbmc/guilib/GUIFontTTF.h | 6 ++- + xbmc/guilib/GUIFontTTFDX.cpp | 79 +++++++++++++---------------- + xbmc/guilib/GUIFontTTFDX.h | 4 +- + xbmc/guilib/GUIFontTTFGL.cpp | 118 +++++++++++++++++++------------------------ + xbmc/guilib/GUIFontTTFGL.h | 4 +- + 6 files changed, 117 insertions(+), 115 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 9c8e516..90b9c4a 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -309,6 +309,27 @@ bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float as + return true; + } + ++void CGUIFontTTFBase::Begin() ++{ ++ if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin()) ++ { ++ m_vertex_count = 0; ++ } ++ // Keep track of the nested begin/end calls. ++ m_nestedBeginCount++; ++} ++ ++void CGUIFontTTFBase::End() ++{ ++ if (m_nestedBeginCount == 0) ++ return; ++ ++ if (--m_nestedBeginCount > 0) ++ return; ++ ++ LastEnd(); ++} ++ + void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors, const vecText &text, uint32_t alignment, float maxPixelWidth, bool scrolling) + { + Begin(); +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 9723a43..c1c4507 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -77,8 +77,8 @@ class CGUIFontTTFBase + + bool Load(const CStdString& strFilename, float height = 20.0f, float aspect = 1.0f, float lineSpacing = 1.0f, bool border = false); + +- virtual void Begin() = 0; +- virtual void End() = 0; ++ void Begin(); ++ void End(); + + const CStdString& GetFileName() const { return m_strFileName; }; + +@@ -169,6 +169,8 @@ class CGUIFontTTFBase + CStdString m_strFileName; + + private: ++ virtual bool FirstBegin() = 0; ++ virtual void LastEnd() = 0; + CGUIFontTTFBase(const CGUIFontTTFBase&); + CGUIFontTTFBase& operator=(const CGUIFontTTFBase&); + int m_referenceCount; +diff --git a/xbmc/guilib/GUIFontTTFDX.cpp b/xbmc/guilib/GUIFontTTFDX.cpp +index e3eba24..2f90668 100644 +--- a/xbmc/guilib/GUIFontTTFDX.cpp ++++ b/xbmc/guilib/GUIFontTTFDX.cpp +@@ -51,65 +51,56 @@ + free(m_index); + } + +-void CGUIFontTTFDX::Begin() ++bool CGUIFontTTFDX::FirstBegin() + { + LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice(); + + if (pD3DDevice == NULL) ++ { + CLog::Log(LOGERROR, __FUNCTION__" - failed to get Direct3D device"); ++ return false; ++ } + +- if (m_nestedBeginCount == 0 && pD3DDevice != NULL && m_texture != NULL) ++ int unit = 0; ++ // just have to blit from our texture. ++ m_texture->BindToUnit(unit); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_DIFFUSE); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); ++ unit++; ++ ++ if(g_Windowing.UseLimitedColor()) + { +- int unit = 0; +- // just have to blit from our texture. +- m_texture->BindToUnit(unit); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_DIFFUSE); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP , D3DTOP_ADD ); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_CURRENT) ; ++ pD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(16,16,16,0) ); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG2, D3DTA_TFACTOR ); + unit++; +- +- if(g_Windowing.UseLimitedColor()) +- { +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP , D3DTOP_ADD ); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_CURRENT) ; +- pD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(16,16,16,0) ); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG2, D3DTA_TFACTOR ); +- unit++; +- } +- +- // no other texture stages needed +- pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_DISABLE); +- pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_DISABLE); +- +- pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); +- pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); +- pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); +- pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); +- pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); +- pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); +- pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); +- pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE); +- +- pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1); +- m_vertex_count = 0; + } + +- // Keep track of the nested begin/end calls. +- m_nestedBeginCount++; ++ // no other texture stages needed ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_DISABLE); ++ pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_DISABLE); ++ ++ pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); ++ pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); ++ pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); ++ pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); ++ pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); ++ pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); ++ pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); ++ pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE); ++ ++ pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1); ++ return true; + } + +-void CGUIFontTTFDX::End() ++void CGUIFontTTFDX::LastEnd() + { + LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice(); + +- if (m_nestedBeginCount == 0) +- return; +- +- if (--m_nestedBeginCount > 0) +- return; +- + if (m_vertex_count == 0) + return; + +diff --git a/xbmc/guilib/GUIFontTTFDX.h b/xbmc/guilib/GUIFontTTFDX.h +index 0431085..17dfefe 100644 +--- a/xbmc/guilib/GUIFontTTFDX.h ++++ b/xbmc/guilib/GUIFontTTFDX.h +@@ -41,8 +41,8 @@ class CGUIFontTTFDX : public CGUIFontTTFBase + CGUIFontTTFDX(const CStdString& strFileName); + virtual ~CGUIFontTTFDX(void); + +- virtual void Begin(); +- virtual void End(); ++ virtual bool FirstBegin(); ++ virtual void LastEnd(); + + protected: + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight); +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index 3358a5a..93b7ea6 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -50,90 +50,78 @@ + { + } + +-void CGUIFontTTFGL::Begin() ++bool CGUIFontTTFGL::FirstBegin() + { +- if (m_nestedBeginCount == 0 && m_texture != NULL) ++ if (!m_bTextureLoaded) + { +- if (!m_bTextureLoaded) +- { +- // Have OpenGL generate a texture object handle for us +- glGenTextures(1, (GLuint*) &m_nTexture); ++ // Have OpenGL generate a texture object handle for us ++ glGenTextures(1, (GLuint*) &m_nTexture); + +- // Bind the texture object +- glBindTexture(GL_TEXTURE_2D, m_nTexture); ++ // Bind the texture object ++ glBindTexture(GL_TEXTURE_2D, m_nTexture); + #ifdef HAS_GL +- glEnable(GL_TEXTURE_2D); ++ glEnable(GL_TEXTURE_2D); + #endif +- // Set the texture's stretching properties +- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ++ // Set the texture's stretching properties ++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + +- // Set the texture image -- THIS WORKS, so the pixels must be wrong. +- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0, +- GL_ALPHA, GL_UNSIGNED_BYTE, m_texture->GetPixels()); ++ // Set the texture image -- THIS WORKS, so the pixels must be wrong. ++ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0, ++ GL_ALPHA, GL_UNSIGNED_BYTE, m_texture->GetPixels()); + +- VerifyGLState(); +- m_bTextureLoaded = true; +- } ++ VerifyGLState(); ++ m_bTextureLoaded = true; ++ } + +- // Turn Blending On +- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE); +- glEnable(GL_BLEND); ++ // Turn Blending On ++ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE); ++ glEnable(GL_BLEND); + #ifdef HAS_GL +- glEnable(GL_TEXTURE_2D); ++ glEnable(GL_TEXTURE_2D); + #endif +- glBindTexture(GL_TEXTURE_2D, m_nTexture); ++ glBindTexture(GL_TEXTURE_2D, m_nTexture); + + #ifdef HAS_GL +- glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); +- glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE); +- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); +- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); +- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); +- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0); +- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); +- glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); +- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); +- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +- VerifyGLState(); ++ glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); ++ glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE); ++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); ++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); ++ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); ++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0); ++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); ++ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); ++ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); ++ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); ++ VerifyGLState(); ++ ++ if(g_Windowing.UseLimitedColor()) ++ { ++ glActiveTexture(GL_TEXTURE1); ++ glBindTexture(GL_TEXTURE_2D, m_nTexture); // dummy bind ++ glEnable(GL_TEXTURE_2D); + +- if(g_Windowing.UseLimitedColor()) +- { +- glActiveTexture(GL_TEXTURE1); +- glBindTexture(GL_TEXTURE_2D, m_nTexture); // dummy bind +- glEnable(GL_TEXTURE_2D); +- +- const GLfloat rgba[4] = {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 0.0f}; +- glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE , GL_COMBINE); +- glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba); +- glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB , GL_ADD); +- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB , GL_PREVIOUS); +- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB , GL_CONSTANT); +- glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB , GL_SRC_COLOR); +- glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB , GL_SRC_COLOR); +- glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA , GL_REPLACE); +- glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA , GL_PREVIOUS); +- VerifyGLState(); +- } ++ const GLfloat rgba[4] = {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 0.0f}; ++ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE , GL_COMBINE); ++ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba); ++ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB , GL_ADD); ++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB , GL_PREVIOUS); ++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB , GL_CONSTANT); ++ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB , GL_SRC_COLOR); ++ glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB , GL_SRC_COLOR); ++ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA , GL_REPLACE); ++ glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA , GL_PREVIOUS); ++ VerifyGLState(); ++ } + + #else +- g_Windowing.EnableGUIShader(SM_FONTS); ++ g_Windowing.EnableGUIShader(SM_FONTS); + #endif +- +- m_vertex_count = 0; +- } +- // Keep track of the nested begin/end calls. +- m_nestedBeginCount++; ++ return true; + } + +-void CGUIFontTTFGL::End() ++void CGUIFontTTFGL::LastEnd() + { +- if (m_nestedBeginCount == 0) +- return; +- +- if (--m_nestedBeginCount > 0) +- return; +- + #ifdef HAS_GL + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); + +diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h +index a0dacba..6736cf7 100644 +--- a/xbmc/guilib/GUIFontTTFGL.h ++++ b/xbmc/guilib/GUIFontTTFGL.h +@@ -41,8 +41,8 @@ class CGUIFontTTFGL : public CGUIFontTTFBase + CGUIFontTTFGL(const CStdString& strFileName); + virtual ~CGUIFontTTFGL(void); + +- virtual void Begin(); +- virtual void End(); ++ virtual bool FirstBegin(); ++ virtual void LastEnd(); + + protected: + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight); +-- +1.8.5.1 + + +From 7f031c9813202a6cbdca46ef1d95c9a2476b6533 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Wed, 11 Dec 2013 18:47:54 +0000 +Subject: [PATCH 02/17] Convert CGUIFontTTFBase::m_vertex to be managed as a + std::vector. Also retired CGUIFontTTFBase::m_vertex_count and + CGUIFontTTFBase::m_vertex_size because these can be derived from vector + member functions. + +--- + xbmc/guilib/GUIFontTTF.cpp | 29 +++++------------------------ + xbmc/guilib/GUIFontTTF.h | 4 +--- + xbmc/guilib/GUIFontTTFDX.cpp | 12 ++++++------ + xbmc/guilib/GUIFontTTFGL.cpp | 12 ++++++------ + 4 files changed, 18 insertions(+), 39 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 90b9c4a..3f219d9 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -139,8 +139,7 @@ class CFreeTypeLibrary + m_nestedBeginCount = 0; + + m_bTextureLoaded = false; +- m_vertex_size = 4*1024; +- m_vertex = (SVertex*)malloc(m_vertex_size * sizeof(SVertex)); ++ m_vertex.reserve(4*1024); + + m_face = NULL; + m_stroker = NULL; +@@ -155,7 +154,6 @@ class CFreeTypeLibrary + m_textureScaleX = m_textureScaleY = 0.0; + m_ellipsesWidth = m_height = 0.0f; + m_color = 0; +- m_vertex_count = 0; + m_nTexture = 0; + } + +@@ -216,9 +214,7 @@ void CGUIFontTTFBase::Clear() + g_freeTypeLibrary.ReleaseStroker(m_stroker); + m_stroker = NULL; + +- free(m_vertex); +- m_vertex = NULL; +- m_vertex_count = 0; ++ m_vertex.clear(); + } + + bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float aspect, float lineSpacing, bool border) +@@ -313,7 +309,7 @@ void CGUIFontTTFBase::Begin() + { + if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin()) + { +- m_vertex_count = 0; ++ m_vertex.clear(); + } + // Keep track of the nested begin/end calls. + m_nestedBeginCount++; +@@ -746,22 +742,9 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c + float tt = texture.y1 * m_textureScaleY; + float tb = texture.y2 * m_textureScaleY; + +- // grow the vertex buffer if required +- if(m_vertex_count >= m_vertex_size) +- { +- m_vertex_size *= 2; +- void* old = m_vertex; +- m_vertex = (SVertex*)realloc(m_vertex, m_vertex_size * sizeof(SVertex)); +- if (!m_vertex) +- { +- free(old); +- CLog::Log(LOGSEVERE, "%s: can't allocate %"PRIdS" bytes for texture", __FUNCTION__ , m_vertex_size * sizeof(SVertex)); +- return; +- } +- } +- ++ m_vertex.resize(m_vertex.size() + 4); ++ SVertex* v = &m_vertex[m_vertex.size() - 4]; + m_color = color; +- SVertex* v = m_vertex + m_vertex_count; + + unsigned char r = GET_R(color) + , g = GET_G(color) +@@ -828,8 +811,6 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c + v[3].y = y[2]; + v[3].z = z[2]; + #endif +- +- m_vertex_count+=4; + } + + // Oblique code - original taken from freetype2 (ftsynth.c) +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index c1c4507..35e3cf9 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -157,9 +157,7 @@ class CGUIFontTTFBase + bool m_bTextureLoaded; + unsigned int m_nTexture; + +- SVertex* m_vertex; +- int m_vertex_count; +- int m_vertex_size; ++ std::vector m_vertex; + + float m_textureScaleX; + float m_textureScaleY; +diff --git a/xbmc/guilib/GUIFontTTFDX.cpp b/xbmc/guilib/GUIFontTTFDX.cpp +index 2f90668..6ef8984 100644 +--- a/xbmc/guilib/GUIFontTTFDX.cpp ++++ b/xbmc/guilib/GUIFontTTFDX.cpp +@@ -101,17 +101,17 @@ void CGUIFontTTFDX::LastEnd() + { + LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice(); + +- if (m_vertex_count == 0) ++ if (m_vertex.size() == 0) + return; + +- unsigned index_size = m_vertex_size * 6 / 4; ++ unsigned index_size = m_vertex.capacity() * 6 / 4; + if(m_index_size < index_size) + { + uint16_t* id = (uint16_t*)calloc(index_size, sizeof(uint16_t)); + if(id == NULL) + return; + +- for(int i = 0, b = 0; i < m_vertex_size; i += 4, b += 6) ++ for(int i = 0, b = 0; i < m_vertex.capacity(); i += 4, b += 6) + { + id[b+0] = i + 0; + id[b+1] = i + 1; +@@ -140,11 +140,11 @@ void CGUIFontTTFDX::LastEnd() + + pD3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST + , 0 +- , m_vertex_count +- , m_vertex_count / 2 ++ , m_vertex.size() ++ , m_vertex.size() / 2 + , m_index + , D3DFMT_INDEX16 +- , m_vertex ++ , &m_vertex[0] + , sizeof(SVertex)); + pD3DDevice->SetTransform(D3DTS_WORLD, &orig); + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index 93b7ea6..a4e8571 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -125,13 +125,13 @@ void CGUIFontTTFGL::LastEnd() + #ifdef HAS_GL + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); + +- glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, r)); +- glVertexPointer (3, GL_FLOAT , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, x)); +- glTexCoordPointer(2, GL_FLOAT , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, u)); ++ glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, r)); ++ glVertexPointer (3, GL_FLOAT , sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, x)); ++ glTexCoordPointer(2, GL_FLOAT , sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, u)); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); +- glDrawArrays(GL_QUADS, 0, m_vertex_count); ++ glDrawArrays(GL_QUADS, 0, m_vertex.size()); + glPopClientAttrib(); + + glActiveTexture(GL_TEXTURE1); +@@ -147,10 +147,10 @@ void CGUIFontTTFGL::LastEnd() + GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); + + // stack object until VBOs will be used +- std::vector vecVertices( 6 * (m_vertex_count / 4) ); ++ std::vector vecVertices( 6 * (m_vertex.size() / 4) ); + SVertex *vertices = &vecVertices[0]; + +- for (int i=0; i +Date: Mon, 16 Dec 2013 18:58:12 +0000 +Subject: [PATCH 03/17] CGUIFontTTFBase::RenderCharacter can now append to + arbitrary vectors of vertices rather than only CGUIFontTTFBase::m_vertex + +--- + xbmc/guilib/GUIFontTTF.cpp | 12 +++++++----- + xbmc/guilib/GUIFontTTF.h | 2 +- + 2 files changed, 8 insertions(+), 6 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 3f219d9..1aaf68b 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -330,6 +330,8 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + { + Begin(); + ++ std::vector &vertices = m_vertex; ++ + // save the origin, which is scaled separately + m_originX = x; + m_originY = y; +@@ -410,7 +412,7 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + + for (int i = 0; i < 3; i++) + { +- RenderCharacter(startX + cursorX, startY, period, color, !scrolling); ++ RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices); + cursorX += period->advance; + } + break; +@@ -419,7 +421,7 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) + break; // exceeded max allowed width - stop rendering + +- RenderCharacter(startX + cursorX, startY, ch, color, !scrolling); ++ RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices); + if ( alignment & XBFONT_JUSTIFIED ) + { + if ((*pos & 0xffff) == L' ') +@@ -676,7 +678,7 @@ bool CGUIFontTTFBase::CacheCharacter(wchar_t letter, uint32_t style, Character * + return true; + } + +-void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX) ++void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX, std::vector &vertices) + { + // actual image width isn't same as the character width as that is + // just baseline width and height should include the descent +@@ -742,8 +744,8 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c + float tt = texture.y1 * m_textureScaleY; + float tb = texture.y2 * m_textureScaleY; + +- m_vertex.resize(m_vertex.size() + 4); +- SVertex* v = &m_vertex[m_vertex.size() - 4]; ++ vertices.resize(vertices.size() + 4); ++ SVertex* v = &vertices[vertices.size() - 4]; + m_color = color; + + unsigned char r = GET_R(color) +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 35e3cf9..4a6a696 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -109,7 +109,7 @@ class CGUIFontTTFBase + // Stuff for pre-rendering for speed + inline Character *GetCharacter(character_t letter); + bool CacheCharacter(wchar_t letter, uint32_t style, Character *ch); +- void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX); ++ void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX, std::vector &vertices); + void ClearCharacterCache(); + + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight) = 0; +-- +1.8.5.1 + + +From a46ceb87785e051b670340feb30d15050b060bfd Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Wed, 15 Jan 2014 17:18:38 +0000 +Subject: [PATCH 04/17] Add a cache of font glyph bounding box vertices. This + is implemented as a template because ultimately we will key on different + parameters and store values of different types, depending upon whether we + have a GLES or non-GLES backend, and for GLES, whether or not the currently + applicable transformation matrices permit the use of hardware clipping. + +--- + xbmc/guilib/GUIFontCache.cpp | 105 ++++++++++++++++++++ + xbmc/guilib/GUIFontCache.h | 217 ++++++++++++++++++++++++++++++++++++++++++ + xbmc/guilib/GUIFontTTF.cpp | 181 +++++++++++++++++++---------------- + xbmc/guilib/GUIFontTTF.h | 5 + + xbmc/guilib/GUIFontTTFGL.cpp | 1 + + xbmc/guilib/GraphicContext.h | 1 + + xbmc/guilib/Makefile.in | 1 + + xbmc/guilib/TransformMatrix.h | 11 +++ + 8 files changed, 438 insertions(+), 84 deletions(-) + create mode 100644 xbmc/guilib/GUIFontCache.cpp + create mode 100644 xbmc/guilib/GUIFontCache.h + +diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp +new file mode 100644 +index 0000000..c029713 +--- /dev/null ++++ b/xbmc/guilib/GUIFontCache.cpp +@@ -0,0 +1,105 @@ ++/* ++ * Copyright (C) 2005-2013 Team XBMC ++ * http://xbmc.org ++ * ++ * This Program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This Program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with XBMC; see the file COPYING. If not, see ++ * . ++ * ++ */ ++ ++#include ++#include ++#include "utils/StdString.h" // required by GUIFontTTF.h ++#include "GUIFontTTF.h" ++#include "GraphicContext.h" ++ ++template ++void CGUIFontCacheEntry::Reassign::operator()(CGUIFontCacheEntry &entry) ++{ ++ entry.m_key.m_pos = m_key.m_pos; ++ entry.m_key.m_colors.assign(m_key.m_colors.begin(), m_key.m_colors.end()); ++ entry.m_key.m_text.assign(m_key.m_text.begin(), m_key.m_text.end()); ++ entry.m_key.m_alignment = m_key.m_alignment; ++ entry.m_key.m_maxPixelWidth = m_key.m_maxPixelWidth; ++ entry.m_key.m_scrolling = m_key.m_scrolling; ++ entry.m_matrix = m_key.m_matrix; ++ entry.m_key.m_scaleX = m_key.m_scaleX; ++ entry.m_key.m_scaleY = m_key.m_scaleY; ++ ++ entry.m_lastUsedMillis = m_nowMillis; ++ entry.m_value.clear(); ++} ++ ++template ++CGUIFontCacheEntry::~CGUIFontCacheEntry() ++{ ++ delete &m_key.m_colors; ++ delete &m_key.m_text; ++ m_value.clear(); ++} ++ ++template ++Value &CGUIFontCache::Lookup(Position &pos, ++ const vecColors &colors, const vecText &text, ++ uint32_t alignment, float maxPixelWidth, ++ bool scrolling, ++ unsigned int nowMillis, bool &dirtyCache) ++{ ++ const CGUIFontCacheKey key(pos, ++ const_cast(colors), const_cast(text), ++ alignment, maxPixelWidth, ++ scrolling, g_graphicsContext.GetGUIMatrix(), ++ g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY()); ++ EntryHashIterator i = m_list.get().find(key); ++ if (i == m_list.get().end()) ++ { ++ /* Cache miss */ ++ EntryAgeIterator oldest = m_list.get().begin(); ++ if (!m_list.get().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) ++ { ++ /* The oldest existing entry is old enough to expire and reuse */ ++ m_list.get().modify(m_list.project(oldest), typename CGUIFontCacheEntry::Reassign(key, nowMillis)); ++ m_list.get().relocate(m_list.get().end(), oldest); ++ } ++ else ++ { ++ /* We need a new entry instead */ ++ /* Yes, this causes the creation an destruction of a temporary entry, but ++ * this code ought to only be used infrequently, when the cache needs to grow */ ++ m_list.get().push_back(CGUIFontCacheEntry(*this, key, nowMillis)); ++ } ++ dirtyCache = true; ++ return (--m_list.get().end())->m_value; ++ } ++ else ++ { ++ /* Cache hit */ ++ /* Update time in entry and move to the back of the list */ ++ i->m_lastUsedMillis = nowMillis; ++ m_list.get().relocate(m_list.get().end(), m_list.project(i)); ++ dirtyCache = false; ++ return i->m_value; ++ } ++} ++ ++template ++void CGUIFontCache::Flush() ++{ ++ m_list.get().clear(); ++} ++ ++template void CGUIFontCacheEntry::Reassign::operator()(CGUIFontCacheEntry &entry); ++template CGUIFontCacheEntry::~CGUIFontCacheEntry(); ++template CGUIFontCacheStaticValue &CGUIFontCache::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); ++template void CGUIFontCache::Flush(); +diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h +new file mode 100644 +index 0000000..ef65845 +--- /dev/null ++++ b/xbmc/guilib/GUIFontCache.h +@@ -0,0 +1,217 @@ ++/*! ++\file GUIFontCache.h ++\brief ++*/ ++ ++#ifndef CGUILIB_GUIFONTCACHE_H ++#define CGUILIB_GUIFONTCACHE_H ++#pragma once ++ ++/* ++ * Copyright (C) 2005-2013 Team XBMC ++ * http://xbmc.org ++ * ++ * This Program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This Program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with XBMC; see the file COPYING. If not, see ++ * . ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "boost/multi_index_container.hpp" ++#include "boost/multi_index/sequenced_index.hpp" ++#include "boost/multi_index/hashed_index.hpp" ++#include "boost/multi_index/member.hpp" ++ ++#include "TransformMatrix.h" ++ ++using namespace boost::multi_index; ++ ++#define FONT_CACHE_TIME_LIMIT (1000) ++ ++template class CGUIFontCache; ++class CGUIFontTTFBase; ++ ++template ++struct CGUIFontCacheKey ++{ ++ Position m_pos; ++ vecColors &m_colors; ++ vecText &m_text; ++ uint32_t m_alignment; ++ float m_maxPixelWidth; ++ bool m_scrolling; ++ const TransformMatrix &m_matrix; ++ float m_scaleX; ++ float m_scaleY; ++ ++ CGUIFontCacheKey(Position pos, ++ vecColors &colors, vecText &text, ++ uint32_t alignment, float maxPixelWidth, ++ bool scrolling, const TransformMatrix &matrix, ++ float scaleX, float scaleY) : ++ m_pos(pos), ++ m_colors(colors), m_text(text), ++ m_alignment(alignment), m_maxPixelWidth(maxPixelWidth), ++ m_scrolling(scrolling), m_matrix(matrix), ++ m_scaleX(scaleX), m_scaleY(scaleY) ++ {} ++}; ++ ++template ++struct CGUIFontCacheEntry ++{ ++ const CGUIFontCache &m_cache; ++ CGUIFontCacheKey m_key; ++ TransformMatrix m_matrix; ++ ++ /* These need to be declared as mutable to get round the fact that only ++ * const iterators are available. These fields do not affect comparison or ++ * hash functors, so from the container's point of view, they are mutable. */ ++ mutable unsigned int m_lastUsedMillis; ++ mutable Value m_value; ++ ++ CGUIFontCacheEntry(const CGUIFontCache &cache, const CGUIFontCacheKey &key, unsigned int nowMillis) : ++ m_cache(cache), ++ m_key(key.m_pos, ++ *new vecColors, *new vecText, ++ key.m_alignment, key.m_maxPixelWidth, ++ key.m_scrolling, m_matrix, ++ key.m_scaleX, key.m_scaleY), ++ m_lastUsedMillis(nowMillis) ++ { ++ m_key.m_colors.assign(key.m_colors.begin(), key.m_colors.end()); ++ m_key.m_text.assign(key.m_text.begin(), key.m_text.end()); ++ m_matrix = key.m_matrix; ++ } ++ ++ CGUIFontCacheEntry(const CGUIFontCacheEntry &other) : ++ m_cache(other.m_cache), ++ m_key(other.m_key.m_pos, ++ *new vecColors, *new vecText, ++ other.m_key.m_alignment, other.m_key.m_maxPixelWidth, ++ other.m_key.m_scrolling, m_matrix, ++ other.m_key.m_scaleX, other.m_key.m_scaleY), ++ m_lastUsedMillis(other.m_lastUsedMillis), ++ m_value(other.m_value) ++ { ++ m_key.m_colors.assign(other.m_key.m_colors.begin(), other.m_key.m_colors.end()); ++ m_key.m_text.assign(other.m_key.m_text.begin(), other.m_key.m_text.end()); ++ m_matrix = other.m_key.m_matrix; ++ } ++ ++ struct Reassign ++ { ++ Reassign(const CGUIFontCacheKey &key, unsigned int nowMillis) : m_key(key), m_nowMillis(nowMillis) {} ++ void operator()(CGUIFontCacheEntry &entry); ++ private: ++ const CGUIFontCacheKey &m_key; ++ unsigned int m_nowMillis; ++ }; ++ ++ ~CGUIFontCacheEntry(); ++}; ++ ++template ++struct CGUIFontCacheHash ++{ ++ size_t operator()(const CGUIFontCacheKey &key) const ++ { ++ /* Not much effort has gone into choosing this hash function */ ++ size_t hash = 0, i; ++ for (i = 0; i < 3 && i < key.m_text.size(); ++i) ++ hash += key.m_text[i]; ++ if (key.m_colors.size()) ++ hash += key.m_colors[0]; ++ hash += MatrixHashContribution(key); ++ return hash; ++ } ++}; ++ ++template ++struct CGUIFontCacheKeysMatch ++{ ++ bool operator()(const CGUIFontCacheKey &a, const CGUIFontCacheKey &b) const ++ { ++ return a.m_text == b.m_text && ++ a.m_colors == b.m_colors && ++ a.m_alignment == b.m_alignment && ++ a.m_scrolling == b.m_scrolling && ++ a.m_maxPixelWidth == b.m_maxPixelWidth && ++ Match(a.m_pos, a.m_matrix, b.m_pos, b.m_matrix, a.m_scrolling) && ++ a.m_scaleX == b.m_scaleX && ++ a.m_scaleY == b.m_scaleY; ++ } ++}; ++ ++template ++class CGUIFontCache ++{ ++ /* Empty structs used as tags to identify indexes */ ++ struct Age {}; ++ struct Hash {}; ++ ++ typedef multi_index_container< ++ CGUIFontCacheEntry, ++ indexed_by< ++ sequenced >, ++ hashed_unique, member, CGUIFontCacheKey, &CGUIFontCacheEntry::m_key>, CGUIFontCacheHash, CGUIFontCacheKeysMatch > ++ > ++ > EntryList; ++ ++ typedef typename EntryList::template index::type::iterator EntryAgeIterator; ++ typedef typename EntryList::template index::type::iterator EntryHashIterator; ++ ++ EntryList m_list; ++ ++public: ++ const CGUIFontTTFBase &m_font; ++ ++ CGUIFontCache(CGUIFontTTFBase &font) : m_font(font) {} ++ Value &Lookup(Position &pos, ++ const vecColors &colors, const vecText &text, ++ uint32_t alignment, float maxPixelWidth, ++ bool scrolling, ++ unsigned int nowMillis, bool &dirtyCache); ++ void Flush(); ++}; ++ ++struct CGUIFontCacheStaticPosition ++{ ++ float m_x; ++ float m_y; ++ CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {} ++}; ++ ++typedef std::vector CGUIFontCacheStaticValue; ++ ++inline bool Match(const CGUIFontCacheStaticPosition &a, const TransformMatrix &a_m, ++ const CGUIFontCacheStaticPosition &b, const TransformMatrix &b_m, ++ bool scrolling) ++{ ++ return a.m_x == b.m_x && a.m_y == b.m_y && a_m == b_m; ++} ++ ++inline float MatrixHashContribution(const CGUIFontCacheKey &a) ++{ ++ /* Ensure horizontally translated versions end up in different buckets */ ++ return a.m_matrix.m[0][3]; ++} ++ ++#endif +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 1aaf68b..288e61a 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -27,6 +27,7 @@ + #include "utils/MathUtils.h" + #include "utils/log.h" + #include "windowing/WindowingFactory.h" ++#include "threads/SystemClock.h" + + #include + +@@ -131,7 +132,7 @@ class CFreeTypeLibrary + XBMC_GLOBAL_REF(CFreeTypeLibrary, g_freeTypeLibrary); // our freetype library + #define g_freeTypeLibrary XBMC_GLOBAL_USE(CFreeTypeLibrary) + +-CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) ++CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this) + { + m_texture = NULL; + m_char = NULL; +@@ -330,108 +331,120 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + { + Begin(); + +- std::vector &vertices = m_vertex; +- +- // save the origin, which is scaled separately +- m_originX = x; +- m_originY = y; +- +- // Check if we will really need to truncate or justify the text +- if ( alignment & XBFONT_TRUNCATED ) ++ bool dirtyCache; ++ CGUIFontCacheStaticPosition staticPos(x, y); ++ std::vector &vertices = m_staticCache.Lookup(staticPos, ++ colors, text, ++ alignment, maxPixelWidth, ++ scrolling, ++ XbmcThreads::SystemClockMillis(), ++ dirtyCache); ++ if (dirtyCache) + { +- if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth) +- alignment &= ~XBFONT_TRUNCATED; +- } +- else if ( alignment & XBFONT_JUSTIFIED ) +- { +- if ( maxPixelWidth <= 0.0f ) +- alignment &= ~XBFONT_JUSTIFIED; +- } ++ // save the origin, which is scaled separately ++ m_originX = x; ++ m_originY = y; + +- // calculate sizing information +- float startX = 0; +- float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0; // vertical centering ++ // Check if we will really need to truncate or justify the text ++ if ( alignment & XBFONT_TRUNCATED ) ++ { ++ if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth) ++ alignment &= ~XBFONT_TRUNCATED; ++ } ++ else if ( alignment & XBFONT_JUSTIFIED ) ++ { ++ if ( maxPixelWidth <= 0.0f ) ++ alignment &= ~XBFONT_JUSTIFIED; ++ } + +- if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) ) +- { +- // Get the extent of this line +- float w = GetTextWidthInternal( text.begin(), text.end() ); ++ // calculate sizing information ++ float startX = 0; ++ float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0; // vertical centering + +- if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues +- w = maxPixelWidth; ++ if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) ) ++ { ++ // Get the extent of this line ++ float w = GetTextWidthInternal( text.begin(), text.end() ); + +- if ( alignment & XBFONT_CENTER_X) +- w *= 0.5f; +- // Offset this line's starting position +- startX -= w; +- } ++ if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues ++ w = maxPixelWidth; + +- float spacePerLetter = 0; // for justification effects +- if ( alignment & XBFONT_JUSTIFIED ) +- { +- // first compute the size of the text to render in both characters and pixels +- unsigned int lineChars = 0; +- float linePixels = 0; +- for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) ++ if ( alignment & XBFONT_CENTER_X) ++ w *= 0.5f; ++ // Offset this line's starting position ++ startX -= w; ++ } ++ ++ float spacePerLetter = 0; // for justification effects ++ if ( alignment & XBFONT_JUSTIFIED ) + { +- Character *ch = GetCharacter(*pos); +- if (ch) +- { // spaces have multiple times the justification spacing of normal letters +- lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1; +- linePixels += ch->advance; ++ // first compute the size of the text to render in both characters and pixels ++ unsigned int lineChars = 0; ++ float linePixels = 0; ++ for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) ++ { ++ Character *ch = GetCharacter(*pos); ++ if (ch) ++ { // spaces have multiple times the justification spacing of normal letters ++ lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1; ++ linePixels += ch->advance; ++ } + } ++ if (lineChars > 1) ++ spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1); + } +- if (lineChars > 1) +- spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1); +- } +- float cursorX = 0; // current position along the line +- +- for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) +- { +- // If starting text on a new line, determine justification effects +- // Get the current letter in the CStdString +- color_t color = (*pos & 0xff0000) >> 16; +- if (color >= colors.size()) +- color = 0; +- color = colors[color]; ++ float cursorX = 0; // current position along the line + +- // grab the next character +- Character *ch = GetCharacter(*pos); +- if (!ch) continue; +- +- if ( alignment & XBFONT_TRUNCATED ) ++ for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) + { +- // Check if we will be exceeded the max allowed width +- if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth ) +- { +- // Yup. Let's draw the ellipses, then bail +- // Perhaps we should really bail to the next line in this case?? +- Character *period = GetCharacter(L'.'); +- if (!period) +- break; ++ // If starting text on a new line, determine justification effects ++ // Get the current letter in the CStdString ++ color_t color = (*pos & 0xff0000) >> 16; ++ if (color >= colors.size()) ++ color = 0; ++ color = colors[color]; ++ ++ // grab the next character ++ Character *ch = GetCharacter(*pos); ++ if (!ch) continue; + +- for (int i = 0; i < 3; i++) ++ if ( alignment & XBFONT_TRUNCATED ) ++ { ++ // Check if we will be exceeded the max allowed width ++ if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth ) + { +- RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices); +- cursorX += period->advance; ++ // Yup. Let's draw the ellipses, then bail ++ // Perhaps we should really bail to the next line in this case?? ++ Character *period = GetCharacter(L'.'); ++ if (!period) ++ break; ++ ++ for (int i = 0; i < 3; i++) ++ { ++ RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices); ++ cursorX += period->advance; ++ } ++ break; + } +- break; + } +- } +- else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) +- break; // exceeded max allowed width - stop rendering ++ else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) ++ break; // exceeded max allowed width - stop rendering + +- RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices); +- if ( alignment & XBFONT_JUSTIFIED ) +- { +- if ((*pos & 0xffff) == L' ') +- cursorX += ch->advance + spacePerLetter * justification_word_weight; ++ RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices); ++ if ( alignment & XBFONT_JUSTIFIED ) ++ { ++ if ((*pos & 0xffff) == L' ') ++ cursorX += ch->advance + spacePerLetter * justification_word_weight; ++ else ++ cursorX += ch->advance + spacePerLetter; ++ } + else +- cursorX += ch->advance + spacePerLetter; ++ cursorX += ch->advance; + } +- else +- cursorX += ch->advance; + } ++ /* Append the new vertices (from the cache or otherwise) to the set collected ++ * since the first Begin() call */ ++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); + + End(); + } +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 4a6a696..7cb4669 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -64,6 +64,9 @@ struct SVertex + }; + + ++#include "GUIFontCache.h" ++ ++ + class CGUIFontTTFBase + { + friend class CGUIFont; +@@ -166,6 +169,8 @@ class CGUIFontTTFBase + + CStdString m_strFileName; + ++ CGUIFontCache m_staticCache; ++ + private: + virtual bool FirstBegin() = 0; + virtual void LastEnd() = 0; +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index a4e8571..cb56987 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -200,6 +200,7 @@ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight) + m_textureScaleX = 1.0f / m_textureWidth; + if (m_textureHeight < newHeight) + CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight); ++ m_staticCache.Flush(); + + memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch()); + if (m_texture) +diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h +index 6c2dcd4..bab2457 100644 +--- a/xbmc/guilib/GraphicContext.h ++++ b/xbmc/guilib/GraphicContext.h +@@ -146,6 +146,7 @@ class CGraphicContext : public CCriticalSection, + inline void ScaleFinalCoords(float &x, float &y, float &z) const XBMC_FORCE_INLINE { m_finalTransform.matrix.TransformPosition(x, y, z); } + bool RectIsAngled(float x1, float y1, float x2, float y2) const; + ++ inline const TransformMatrix &GetGUIMatrix() const XBMC_FORCE_INLINE { return m_finalTransform.matrix; } + inline float GetGUIScaleX() const XBMC_FORCE_INLINE { return m_finalTransform.scaleX; } + inline float GetGUIScaleY() const XBMC_FORCE_INLINE { return m_finalTransform.scaleY; } + inline color_t MergeAlpha(color_t color) const XBMC_FORCE_INLINE +diff --git a/xbmc/guilib/Makefile.in b/xbmc/guilib/Makefile.in +index 086fb0d..af82979 100644 +--- a/xbmc/guilib/Makefile.in ++++ b/xbmc/guilib/Makefile.in +@@ -23,6 +23,7 @@ SRCS += GUIEditControl.cpp + SRCS += GUIFadeLabelControl.cpp + SRCS += GUIFixedListContainer.cpp + SRCS += GUIFont.cpp ++SRCS += GUIFontCache.cpp + SRCS += GUIFontManager.cpp + SRCS += GUIFontTTF.cpp + SRCS += GUIImage.cpp +diff --git a/xbmc/guilib/TransformMatrix.h b/xbmc/guilib/TransformMatrix.h +index f351c99..9036ba9 100644 +--- a/xbmc/guilib/TransformMatrix.h ++++ b/xbmc/guilib/TransformMatrix.h +@@ -245,3 +245,14 @@ class TransformMatrix + float alpha; + bool identity; + }; ++ ++inline bool operator==(const TransformMatrix &a, const TransformMatrix &b) ++{ ++ return a.alpha == b.alpha && ((a.identity && b.identity) || ++ (!a.identity && !b.identity && std::equal(&a.m[0][0], &a.m[0][0] + sizeof a.m / sizeof a.m[0][0], &b.m[0][0]))); ++} ++ ++inline bool operator!=(const TransformMatrix &a, const TransformMatrix &b) ++{ ++ return !operator==(a, b); ++} +-- +1.8.5.1 + + +From 577707ca22fa5a7f9f1dc33120941386aacd8801 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Thu, 23 Jan 2014 22:24:17 +0000 +Subject: [PATCH 05/17] Lay the groundwork for hardware clipping. + +For glScissor() to replace CGraphicContext::ClipRect, a necessary condition +is that no shear or rotation is introduced between the coordinate systems +they use; this depends upon the settings of the GUI matrix m_finalTransform +as well as the OpenGL model-view and projection matrices. These all remain +unchanged between paired calls of CGUIShader::OnEnabled and +CGUIShader::OnDisabled, so we scan the matrices in CGUIShader::OnEnabled to +see whether hardware clipping is possible. + +Then, in CGUIFontTTFBase::RenderCharacter, we don't apply software clipping +in such cases. However, because vertices arising from multiple +CGUIFontTTFBase::DrawTextInternal calls (each of which often uses a different +clip rectangle) get lumped into the same vector, which only at the end is +passed to OpenGL for rendering, we need to wait a few commits before we can +actually apply hardware clipping. In the meantime, expect to see rendering +errors. +--- + xbmc/guilib/GUIFontTTF.cpp | 3 +- + xbmc/guilib/GUIShader.cpp | 80 +++++++++++++++++++++++++++++++- + xbmc/guilib/GUIShader.h | 11 +++++ + xbmc/guilib/GraphicContext.cpp | 10 ++++ + xbmc/guilib/GraphicContext.h | 1 + + xbmc/rendering/RenderSystem.h | 2 + + xbmc/rendering/gles/RenderSystemGLES.cpp | 22 +++++++++ + xbmc/rendering/gles/RenderSystemGLES.h | 2 + + 8 files changed, 128 insertions(+), 3 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 288e61a..19c7ff4 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -710,7 +710,8 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c + (posY + ch->offsetY + height) * g_graphicsContext.GetGUIScaleY()); + vertex += CPoint(m_originX, m_originY); + CRect texture(ch->left, ch->top, ch->right, ch->bottom); +- g_graphicsContext.ClipRect(vertex, texture); ++ if (!g_Windowing.ScissorsCanEffectClipping()) ++ g_graphicsContext.ClipRect(vertex, texture); + + // transform our positions - note, no scaling due to GUI calibration/resolution occurs + float x[4], y[4], z[4]; +diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp +index 11089b8..53bce09 100644 +--- a/xbmc/guilib/GUIShader.cpp ++++ b/xbmc/guilib/GUIShader.cpp +@@ -26,6 +26,8 @@ + #include "GUIShader.h" + #include "MatrixGLES.h" + #include "utils/log.h" ++#include "windowing/egl/WinSystemEGL.h" ++#include "guilib/GraphicContext.h" + + CGUIShader::CGUIShader( const char *shader ) : CGLSLShaderProgram("guishader_vert.glsl", shader) + { +@@ -86,8 +88,82 @@ bool CGUIShader::OnEnabled() + { + // This is called after glUseProgram() + +- glUniformMatrix4fv(m_hProj, 1, GL_FALSE, g_matrices.GetMatrix(MM_PROJECTION)); +- glUniformMatrix4fv(m_hModel, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); ++ GLfloat *projMatrix = g_matrices.GetMatrix(MM_PROJECTION); ++ GLfloat *modelMatrix = g_matrices.GetMatrix(MM_MODELVIEW); ++ glUniformMatrix4fv(m_hProj, 1, GL_FALSE, projMatrix); ++ glUniformMatrix4fv(m_hModel, 1, GL_FALSE, modelMatrix); ++ ++ const TransformMatrix &guiMatrix = g_graphicsContext.GetGUIMatrix(); ++ CRect viewPort; // absolute positions of corners ++ g_Windowing.GetViewPort(viewPort); ++ ++ /* glScissor operates in window coordinates. In order that we can use it to ++ * perform clipping, we must ensure that there is an independent linear ++ * transformation from the coordinate system used by CGraphicContext::ClipRect ++ * to window coordinates, separately for X and Y (in other words, no ++ * rotation or shear is introduced at any stage). To do, this, we need to ++ * check that zeros are present in the following locations: ++ * ++ * GUI matrix: ++ * / * 0 * * \ ++ * | 0 * * * | ++ * \ 0 0 * * / ++ * ^ TransformMatrix::TransformX/Y/ZCoord are only ever called with ++ * input z = 0, so this column doesn't matter ++ * Model-view matrix: ++ * / * 0 0 * \ ++ * | 0 * 0 * | ++ * | 0 0 * * | ++ * \ * * * * / <- eye w has no influence on window x/y (last column below ++ * is either 0 or ignored) ++ * Projection matrix: ++ * / * 0 0 0 \ ++ * | 0 * 0 0 | ++ * | * * * * | <- normalised device coordinate z has no influence on window x/y ++ * \ 0 0 * 0 / ++ * ++ * Some of these zeros are not strictly required to ensure this, but they tend ++ * to be zeroed in the common case, so by checking for zeros here, we simplify ++ * the calculation of the window x/y coordinates further down the line. ++ * ++ * (Minor detail: we don't quite deal in window coordinates as defined by ++ * OpenGL, because CRenderSystemGLES::SetScissors flips the Y axis. But all ++ * that's needed to handle that is an effective negation at the stage where ++ * Y is in normalised device coordinates.) ++ */ ++ m_clipPossible = guiMatrix.m[0][1] == 0 && ++ guiMatrix.m[1][0] == 0 && ++ guiMatrix.m[2][0] == 0 && ++ guiMatrix.m[2][1] == 0 && ++ modelMatrix[0+1*4] == 0 && ++ modelMatrix[0+2*4] == 0 && ++ modelMatrix[1+0*4] == 0 && ++ modelMatrix[1+2*4] == 0 && ++ modelMatrix[2+0*4] == 0 && ++ modelMatrix[2+1*4] == 0 && ++ projMatrix[0+1*4] == 0 && ++ projMatrix[0+2*4] == 0 && ++ projMatrix[0+3*4] == 0 && ++ projMatrix[1+0*4] == 0 && ++ projMatrix[1+2*4] == 0 && ++ projMatrix[1+3*4] == 0 && ++ projMatrix[3+0*4] == 0 && ++ projMatrix[3+1*4] == 0 && ++ projMatrix[3+3*4] == 0; ++ if (m_clipPossible) ++ { ++ m_clipXFactor = guiMatrix.m[0][0] * modelMatrix[0+0*4] * projMatrix[0+0*4]; ++ m_clipXOffset = (guiMatrix.m[0][3] * modelMatrix[0+0*4] + modelMatrix[0+3*4]) * projMatrix[0+0*4]; ++ m_clipYFactor = guiMatrix.m[1][1] * modelMatrix[1+1*4] * projMatrix[1+1*4]; ++ m_clipYOffset = (guiMatrix.m[1][3] * modelMatrix[1+1*4] + modelMatrix[1+3*4]) * projMatrix[1+1*4]; ++ float clipW = (guiMatrix.m[2][3] * modelMatrix[2+2*4] + modelMatrix[2+3*4]) * projMatrix[3+2*4]; ++ float xMult = (viewPort.x2 - viewPort.x1) / (2 * clipW); ++ float yMult = (viewPort.y1 - viewPort.y2) / (2 * clipW); // correct for inverted window coordinate scheme ++ m_clipXFactor = m_clipXFactor * xMult; ++ m_clipXOffset = m_clipXOffset * xMult + (viewPort.x2 + viewPort.x1) / 2; ++ m_clipYFactor = m_clipYFactor * yMult; ++ m_clipYOffset = m_clipYOffset * yMult + (viewPort.y2 + viewPort.y1) / 2; ++ } + + return true; + } +diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h +index c7e95aa..86ce4cc 100644 +--- a/xbmc/guilib/GUIShader.h ++++ b/xbmc/guilib/GUIShader.h +@@ -41,6 +41,11 @@ class CGUIShader : public CGLSLShaderProgram + GLint GetCord1Loc() { return m_hCord1; } + GLint GetUniColLoc() { return m_hUniCol; } + GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; } ++ bool HardwareClipIsPossible() { return m_clipPossible; } ++ GLfloat GetClipXFactor() { return m_clipXFactor; } ++ GLfloat GetClipXOffset() { return m_clipXOffset; } ++ GLfloat GetClipYFactor() { return m_clipYFactor; } ++ GLfloat GetClipYOffset() { return m_clipYOffset; } + + protected: + GLint m_hTex0; +@@ -56,6 +61,12 @@ class CGUIShader : public CGLSLShaderProgram + + GLfloat *m_proj; + GLfloat *m_model; ++ ++ bool m_clipPossible; ++ GLfloat m_clipXFactor; ++ GLfloat m_clipXOffset; ++ GLfloat m_clipYFactor; ++ GLfloat m_clipYOffset; + }; + + #endif // GUI_SHADER_H +diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp +index ff29532..1621d17 100644 +--- a/xbmc/guilib/GraphicContext.cpp ++++ b/xbmc/guilib/GraphicContext.cpp +@@ -167,6 +167,16 @@ void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2) + } + } + ++CRect CGraphicContext::GetClipRegion() ++{ ++ if (m_clipRegions.empty()) ++ return CRect(0, 0, m_iScreenWidth, m_iScreenHeight); ++ CRect clipRegion(m_clipRegions.top()); ++ if (!m_origins.empty()) ++ clipRegion -= m_origins.top(); ++ return clipRegion; ++} ++ + bool CGraphicContext::SetViewPort(float fx, float fy, float fwidth, float fheight, bool intersectPrevious /* = false */) + { + // transform coordinates - we may have a rotation which changes the positioning of the +diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h +index bab2457..0a27643 100644 +--- a/xbmc/guilib/GraphicContext.h ++++ b/xbmc/guilib/GraphicContext.h +@@ -199,6 +199,7 @@ class CGraphicContext : public CCriticalSection, + void ApplyHardwareTransform(); + void RestoreHardwareTransform(); + void ClipRect(CRect &vertex, CRect &texture, CRect *diffuse = NULL); ++ CRect GetClipRegion(); + inline void AddGUITransform() + { + m_transforms.push(m_finalTransform); +diff --git a/xbmc/rendering/RenderSystem.h b/xbmc/rendering/RenderSystem.h +index 2bceb67..12cc784 100644 +--- a/xbmc/rendering/RenderSystem.h ++++ b/xbmc/rendering/RenderSystem.h +@@ -109,6 +109,8 @@ class CRenderSystemBase + virtual void SetViewPort(CRect& viewPort) = 0; + virtual void GetViewPort(CRect& viewPort) = 0; + ++ virtual bool ScissorsCanEffectClipping() { return false; } ++ virtual CRect ClipRectToScissorRect(const CRect &rect) { return CRect(); } + virtual void SetScissors(const CRect &rect) = 0; + virtual void ResetScissors() = 0; + +diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp +index ad531f8..87b2cb6 100644 +--- a/xbmc/rendering/gles/RenderSystemGLES.cpp ++++ b/xbmc/rendering/gles/RenderSystemGLES.cpp +@@ -533,6 +533,28 @@ void CRenderSystemGLES::SetViewPort(CRect& viewPort) + m_viewPort[3] = viewPort.Height(); + } + ++bool CRenderSystemGLES::ScissorsCanEffectClipping() ++{ ++ if (m_pGUIshader[m_method]) ++ return m_pGUIshader[m_method]->HardwareClipIsPossible(); ++ ++ return false; ++} ++ ++CRect CRenderSystemGLES::ClipRectToScissorRect(const CRect &rect) ++{ ++ if (!m_pGUIshader[m_method]) ++ return CRect(); ++ float xFactor = m_pGUIshader[m_method]->GetClipXFactor(); ++ float xOffset = m_pGUIshader[m_method]->GetClipXOffset(); ++ float yFactor = m_pGUIshader[m_method]->GetClipYFactor(); ++ float yOffset = m_pGUIshader[m_method]->GetClipYOffset(); ++ return CRect(rect.x1 * xFactor + xOffset, ++ rect.y1 * yFactor + yOffset, ++ rect.x2 * xFactor + xOffset, ++ rect.y2 * yFactor + yOffset); ++} ++ + void CRenderSystemGLES::SetScissors(const CRect &rect) + { + if (!m_bRenderCreated) +diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h +index b0e4a19..af2dbbb 100644 +--- a/xbmc/rendering/gles/RenderSystemGLES.h ++++ b/xbmc/rendering/gles/RenderSystemGLES.h +@@ -63,6 +63,8 @@ class CRenderSystemGLES : public CRenderSystemBase + virtual void SetViewPort(CRect& viewPort); + virtual void GetViewPort(CRect& viewPort); + ++ virtual bool ScissorsCanEffectClipping(); ++ virtual CRect ClipRectToScissorRect(const CRect &rect); + virtual void SetScissors(const CRect& rect); + virtual void ResetScissors(); + +-- +1.8.5.1 + + +From 0faf86c9908a625fd4d93201966eda785aff5bc0 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Thu, 23 Jan 2014 16:42:22 +0000 +Subject: [PATCH 06/17] Increase font cache hit rate by keying on the + fractional part of m_originX and m_originY *after* they have been through the + graphics context's transformation matrix, plus the scale/rotation elements of + the matrix, rather than the origin in the original frame of reference plus + the complete transformation matrix. All vertices of individual glyph bounding + boxes are a constant offset from this position, and when the fractional part + of the translation is a match, the rounding of each vertex will be in the + same direction; this permits us to calculate the desired vertices from the + cached ones simply by adding the integer parts of the translations with no + additional rounding steps. + +Note that this requires that software clipping is *not* performed. +--- + xbmc/guilib/GUIFontCache.cpp | 8 +++++++ + xbmc/guilib/GUIFontCache.h | 43 +++++++++++++++++++++++++++++++++++ + xbmc/guilib/GUIFontTTF.cpp | 53 +++++++++++++++++++++++++++++++++++--------- + xbmc/guilib/GUIFontTTF.h | 1 + + 4 files changed, 95 insertions(+), 10 deletions(-) + +diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp +index c029713..b66c00b 100644 +--- a/xbmc/guilib/GUIFontCache.cpp ++++ b/xbmc/guilib/GUIFontCache.cpp +@@ -85,6 +85,9 @@ + else + { + /* Cache hit */ ++ /* Update the translation arguments so that they hold the offset to apply ++ * to the cached values (but only in the dynamic case) */ ++ pos.UpdateWithOffsets(i->m_key.m_pos, scrolling); + /* Update time in entry and move to the back of the list */ + i->m_lastUsedMillis = nowMillis; + m_list.get().relocate(m_list.get().end(), m_list.project(i)); +@@ -103,3 +106,8 @@ + template CGUIFontCacheEntry::~CGUIFontCacheEntry(); + template CGUIFontCacheStaticValue &CGUIFontCache::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); + template void CGUIFontCache::Flush(); ++ ++template void CGUIFontCacheEntry::Reassign::operator()(CGUIFontCacheEntry &entry); ++template CGUIFontCacheEntry::~CGUIFontCacheEntry(); ++template CGUIFontCacheDynamicValue &CGUIFontCache::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); ++template void CGUIFontCache::Flush(); +diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h +index ef65845..d913dee 100644 +--- a/xbmc/guilib/GUIFontCache.h ++++ b/xbmc/guilib/GUIFontCache.h +@@ -44,6 +44,7 @@ + using namespace boost::multi_index; + + #define FONT_CACHE_TIME_LIMIT (1000) ++#define FONT_CACHE_DIST_LIMIT (0.01) + + template class CGUIFontCache; + class CGUIFontTTFBase; +@@ -197,6 +198,7 @@ struct CGUIFontCacheStaticPosition + float m_x; + float m_y; + CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {} ++ void UpdateWithOffsets(const CGUIFontCacheStaticPosition &cached, bool scrolling) {} + }; + + typedef std::vector CGUIFontCacheStaticValue; +@@ -214,4 +216,45 @@ inline float MatrixHashContribution(const CGUIFontCacheKey CGUIFontCacheDynamicValue; ++ ++inline bool Match(const CGUIFontCacheDynamicPosition &a, const TransformMatrix &a_m, ++ const CGUIFontCacheDynamicPosition &b, const TransformMatrix &b_m, ++ bool scrolling) ++{ ++ float diffX = a.m_x - b.m_x + FONT_CACHE_DIST_LIMIT; ++ float diffY = a.m_y - b.m_y + FONT_CACHE_DIST_LIMIT; ++ float diffZ = a.m_z - b.m_z + FONT_CACHE_DIST_LIMIT; ++ return (scrolling || diffX - floorf(diffX) < 2 * FONT_CACHE_DIST_LIMIT) && ++ diffY - floorf(diffY) < 2 * FONT_CACHE_DIST_LIMIT && ++ diffZ - floorf(diffZ) < 2 * FONT_CACHE_DIST_LIMIT && ++ a_m.m[0][0] == b_m.m[0][0] && ++ a_m.m[1][1] == b_m.m[1][1] && ++ a_m.m[2][2] == b_m.m[2][2]; ++ // We already know the first 3 columns of both matrices are diagonal, so no need to check the other elements ++} ++ ++inline float MatrixHashContribution(const CGUIFontCacheKey &a) ++{ ++ return 0; ++} ++ + #endif +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 19c7ff4..73f0e50 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -132,7 +132,7 @@ class CFreeTypeLibrary + XBMC_GLOBAL_REF(CFreeTypeLibrary, g_freeTypeLibrary); // our freetype library + #define g_freeTypeLibrary XBMC_GLOBAL_USE(CFreeTypeLibrary) + +-CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this) ++CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this), m_dynamicCache(*this) + { + m_texture = NULL; + m_char = NULL; +@@ -332,13 +332,28 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + Begin(); + + bool dirtyCache; ++ bool hardwareClipping = g_Windowing.ScissorsCanEffectClipping(); + CGUIFontCacheStaticPosition staticPos(x, y); +- std::vector &vertices = m_staticCache.Lookup(staticPos, +- colors, text, +- alignment, maxPixelWidth, +- scrolling, +- XbmcThreads::SystemClockMillis(), +- dirtyCache); ++ CGUIFontCacheDynamicPosition dynamicPos; ++ if (hardwareClipping) ++ { ++ dynamicPos = CGUIFontCacheDynamicPosition(g_graphicsContext.ScaleFinalXCoord(x, y), ++ g_graphicsContext.ScaleFinalYCoord(x, y), ++ g_graphicsContext.ScaleFinalZCoord(x, y)); ++ } ++ std::vector &vertices = hardwareClipping ? ++ m_dynamicCache.Lookup(dynamicPos, ++ colors, text, ++ alignment, maxPixelWidth, ++ scrolling, ++ XbmcThreads::SystemClockMillis(), ++ dirtyCache) : ++ m_staticCache.Lookup(staticPos, ++ colors, text, ++ alignment, maxPixelWidth, ++ scrolling, ++ XbmcThreads::SystemClockMillis(), ++ dirtyCache); + if (dirtyCache) + { + // save the origin, which is scaled separately +@@ -441,10 +456,28 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + else + cursorX += ch->advance; + } ++ if (hardwareClipping) ++ /* Append the new vertices (which we have just constructed in the cache) ++ * to the set collected since the first Begin() call */ ++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); ++ } ++ else if (hardwareClipping) ++ { ++ /* Apply the translation offset to the vertices from the cache after ++ * appending them to the set collected since the first Begin() call */ ++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); ++ SVertex *v; ++ for (v = &*m_vertex.end() - vertices.size(); v != &*m_vertex.end(); v++) ++ { ++ v->x += dynamicPos.m_x; ++ v->y += dynamicPos.m_y; ++ v->z += dynamicPos.m_z; ++ } + } +- /* Append the new vertices (from the cache or otherwise) to the set collected +- * since the first Begin() call */ +- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); ++ if (!hardwareClipping) ++ /* Append the new vertices (from the cache or otherwise) to the set collected ++ * since the first Begin() call */ ++ m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); + + End(); + } +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 7cb4669..78445ab 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -170,6 +170,7 @@ class CGUIFontTTFBase + CStdString m_strFileName; + + CGUIFontCache m_staticCache; ++ CGUIFontCache m_dynamicCache; + + private: + virtual bool FirstBegin() = 0; +-- +1.8.5.1 + + +From 1cc1bebef45c0a75786fa3df0e11816107ba4da1 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Wed, 8 Jan 2014 12:16:33 +0000 +Subject: [PATCH 07/17] Rewrite of scrolling text code. + +No longer shuffles the string round to minimise the number of characters +before the clipping rectangle; this doesn't save much on rendering time but +does harm the effectiveness of the cache. Now uses a pixel offset into the +string rather than a character + pixel offset, and plots the entire string +every time (execpt when the wrap point is visible, in which case the entire +string is plotted twice). + +It also makes motion smoother, because (possibly unintentionally) the +previous code preferred to align the scroll offset with character boundaries. +This would lead to uneven changes of position, especially when the width of +the character currently being scrolled off the edge was only slightly more +than an integral multiple of the scroll increment. +--- + xbmc/guilib/GUIFadeLabelControl.cpp | 8 +-- + xbmc/guilib/GUIFont.cpp | 123 +++++++++++++----------------------- + xbmc/guilib/GUIFont.h | 17 ++--- + xbmc/guilib/GUIRSSControl.cpp | 6 +- + xbmc/utils/RssReader.cpp | 2 +- + xbmc/utils/RssReader.h | 2 +- + 6 files changed, 58 insertions(+), 100 deletions(-) + +diff --git a/xbmc/guilib/GUIFadeLabelControl.cpp b/xbmc/guilib/GUIFadeLabelControl.cpp +index d594c04..86ee73a 100644 +--- a/xbmc/guilib/GUIFadeLabelControl.cpp ++++ b/xbmc/guilib/GUIFadeLabelControl.cpp +@@ -109,18 +109,14 @@ void CGUIFadeLabelControl::Process(unsigned int currentTime, CDirtyRegionList &d + bool moveToNextLabel = false; + if (!m_scrollOut) + { +- vecText text; +- m_textLayout.GetFirstText(text); +- if (m_scrollInfo.characterPos && m_scrollInfo.characterPos < text.size()) +- text.erase(text.begin(), text.begin() + min((int)m_scrollInfo.characterPos - 1, (int)text.size())); +- if (m_label.font->GetTextWidth(text) < m_width) ++ if (m_scrollInfo.pixelPos + m_width > m_scrollInfo.m_textWidth) + { + if (m_fadeAnim.GetProcess() != ANIM_PROCESS_NORMAL) + m_fadeAnim.QueueAnimation(ANIM_PROCESS_NORMAL); + moveToNextLabel = true; + } + } +- else if (m_scrollInfo.characterPos > m_textLayout.GetTextLength()) ++ else if (m_scrollInfo.pixelPos > m_scrollInfo.m_textWidth) + moveToNextLabel = true; + + // apply the fading animation +diff --git a/xbmc/guilib/GUIFont.cpp b/xbmc/guilib/GUIFont.cpp +index 63ecf85..eb8efdb 100644 +--- a/xbmc/guilib/GUIFont.cpp ++++ b/xbmc/guilib/GUIFont.cpp +@@ -36,7 +36,12 @@ + initialWait = wait; + initialPos = pos; + SetSpeed(speed ? speed : defaultSpeed); +- g_charsetConverter.utf8ToW(scrollSuffix, suffix); ++ CStdStringW wsuffix; ++ g_charsetConverter.utf8ToW(scrollSuffix, wsuffix); ++ suffix.clear(); ++ suffix.reserve(wsuffix.size()); ++ for (vecText::size_type i = 0; i < wsuffix.size(); i++) ++ suffix.push_back(wsuffix[i]); + Reset(); + } + +@@ -115,11 +120,12 @@ bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo) + { + // draw at our scroll position + // we handle the scrolling as follows: +- // We scroll on a per-pixel basis up until we have scrolled the first character outside +- // of our viewport, whereby we cycle the string around, and reset the scroll position. +- // +- // pixelPos is the amount in pixels to move the string by. +- // characterPos is the amount in characters to rotate the string by. ++ // We scroll on a per-pixel basis (eschewing the use of character indices ++ // which were also in use previously). The complete string, including suffix, ++ // is plotted to achieve the desired effect - normally just the one time, but ++ // if there is a wrap point within the viewport then it will be plotted twice. ++ // If the string is smaller than the viewport, then it may be plotted even ++ // more times than that. + // + if (scrollInfo.waitTime) + { +@@ -135,54 +141,19 @@ bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo) + // move along by the appropriate scroll amount + float scrollAmount = fabs(scrollInfo.GetPixelsPerFrame() * g_graphicsContext.GetGUIScaleX()); + +- if (scrollInfo.pixelSpeed > 0) ++ if (!scrollInfo.m_widthValid) + { +- // we want to move scrollAmount, grab the next character +- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); +- if (scrollInfo.pixelPos + scrollAmount < charWidth) +- scrollInfo.pixelPos += scrollAmount; // within the current character +- else +- { // past the current character, decrement scrollAmount by the charWidth and move to the next character +- while (scrollInfo.pixelPos + scrollAmount >= charWidth) +- { +- scrollAmount -= (charWidth - scrollInfo.pixelPos); +- scrollInfo.pixelPos = 0; +- scrollInfo.characterPos++; +- if (scrollInfo.characterPos >= text.size() + scrollInfo.suffix.size()) +- { +- scrollInfo.Reset(); +- break; +- } +- charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); +- } +- } +- } +- else if (scrollInfo.pixelSpeed < 0) +- { // scrolling backwards +- // we want to move scrollAmount, grab the next character +- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); +- if (scrollInfo.pixelPos + scrollAmount < charWidth) +- scrollInfo.pixelPos += scrollAmount; // within the current character +- else +- { // past the current character, decrement scrollAmount by the charWidth and move to the next character +- while (scrollInfo.pixelPos + scrollAmount >= charWidth) +- { +- scrollAmount -= (charWidth - scrollInfo.pixelPos); +- scrollInfo.pixelPos = 0; +- if (scrollInfo.characterPos == 0) +- { +- scrollInfo.Reset(); +- scrollInfo.characterPos = text.size() + scrollInfo.suffix.size() - 1; +- break; +- } +- scrollInfo.characterPos--; +- charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); +- } +- } ++ /* Calculate the pixel width of the complete string */ ++ scrollInfo.m_textWidth = GetTextWidth(text); ++ scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix); ++ scrollInfo.m_widthValid = true; + } ++ scrollInfo.pixelPos += scrollAmount; ++ assert(scrollInfo.m_totalWidth != 0); ++ while (scrollInfo.pixelPos >= scrollInfo.m_totalWidth) ++ scrollInfo.pixelPos -= scrollInfo.m_totalWidth; + +- if(scrollInfo.characterPos != old.characterPos +- || scrollInfo.pixelPos != old.pixelPos) ++ if (scrollInfo.pixelPos != old.pixelPos) + return true; + else + return false; +@@ -194,39 +165,27 @@ void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, colo + if (!m_font) return; + if (!shadowColor) shadowColor = m_shadowColor; + +- float spaceWidth = GetCharWidth(L' '); +- // max chars on screen + extra margin chars +- vecText::size_type maxChars = +- std::min( +- (text.size() + (vecText::size_type)scrollInfo.suffix.size()), +- (vecText::size_type)((maxWidth * 1.05f) / spaceWidth)); +- + if (!text.size() || ClippedRegionIsEmpty(x, y, maxWidth, alignment)) + return; // nothing to render + +- maxWidth = ROUND(maxWidth / g_graphicsContext.GetGUIScaleX()); ++ if (!scrollInfo.m_widthValid) ++ { ++ /* Calculate the pixel width of the complete string */ ++ scrollInfo.m_textWidth = GetTextWidth(text); ++ scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix); ++ scrollInfo.m_widthValid = true; ++ } ++ ++ assert(scrollInfo.m_totalWidth != 0); ++ ++ float textPixelWidth = ROUND(scrollInfo.m_textWidth / g_graphicsContext.GetGUIScaleX()); ++ float suffixPixelWidth = ROUND((scrollInfo.m_totalWidth - scrollInfo.m_textWidth) / g_graphicsContext.GetGUIScaleX()); + +- float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); + float offset; + if(scrollInfo.pixelSpeed >= 0) + offset = scrollInfo.pixelPos; + else +- offset = charWidth - scrollInfo.pixelPos; +- +- // Now rotate our string as needed, only take a slightly larger then visible part of the text. +- unsigned int pos = scrollInfo.characterPos; +- vecText renderText; +- renderText.reserve(maxChars); +- for (vecText::size_type i = 0; i < maxChars; i++) +- { +- if (pos >= text.size() + scrollInfo.suffix.size()) +- pos = 0; +- if (pos < text.size()) +- renderText.push_back(text[pos]); +- else +- renderText.push_back(scrollInfo.suffix[pos - text.size()]); +- pos++; +- } ++ offset = scrollInfo.m_totalWidth - scrollInfo.pixelPos; + + vecColors renderColors; + for (unsigned int i = 0; i < colors.size(); i++) +@@ -239,9 +198,17 @@ void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, colo + vecColors shadowColors; + for (unsigned int i = 0; i < renderColors.size(); i++) + shadowColors.push_back((renderColors[i] & 0xff000000) != 0 ? shadowColor : 0); +- m_font->DrawTextInternal(x - offset + 1, y + 1, shadowColors, renderText, alignment, maxWidth + scrollInfo.pixelPos + m_font->GetLineHeight(2.0f), scroll); ++ for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth) ++ { ++ m_font->DrawTextInternal(x + dx + 1, y + 1, shadowColors, text, alignment, textPixelWidth, scroll); ++ m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth + 1, y + 1, shadowColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll); ++ } ++ } ++ for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth) ++ { ++ m_font->DrawTextInternal(x + dx, y, renderColors, text, alignment, textPixelWidth, scroll); ++ m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth, y, renderColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll); + } +- m_font->DrawTextInternal(x - offset, y, renderColors, renderText, alignment, maxWidth + scrollInfo.pixelPos + m_font->GetLineHeight(2.0f), scroll); + + g_graphicsContext.RestoreClipRegion(); + } +diff --git a/xbmc/guilib/GUIFont.h b/xbmc/guilib/GUIFont.h +index c55db48..09cf9b3 100644 +--- a/xbmc/guilib/GUIFont.h ++++ b/xbmc/guilib/GUIFont.h +@@ -64,7 +64,6 @@ class CScrollInfo + void Reset() + { + waitTime = initialWait; +- characterPos = 0; + // pixelPos is where we start the current letter, so is measured + // to the left of the text rendering's left edge. Thus, a negative + // value will mean the text starts to the right +@@ -72,25 +71,19 @@ class CScrollInfo + // privates: + m_averageFrameTime = 1000.f / abs(defaultSpeed); + m_lastFrameTime = 0; +- } +- uint32_t GetCurrentChar(const vecText &text) const +- { +- assert(text.size()); +- if (characterPos < text.size()) +- return text[characterPos]; +- else if (characterPos < text.size() + suffix.size()) +- return suffix[characterPos - text.size()]; +- return text[0]; ++ m_widthValid = false; + } + float GetPixelsPerFrame(); + + float pixelPos; + float pixelSpeed; + unsigned int waitTime; +- unsigned int characterPos; + unsigned int initialWait; + float initialPos; +- CStdStringW suffix; ++ vecText suffix; ++ mutable float m_textWidth; ++ mutable float m_totalWidth; ++ mutable bool m_widthValid; + + static const int defaultSpeed = 60; + private: +diff --git a/xbmc/guilib/GUIRSSControl.cpp b/xbmc/guilib/GUIRSSControl.cpp +index b68e08b..6bd37fd 100644 +--- a/xbmc/guilib/GUIRSSControl.cpp ++++ b/xbmc/guilib/GUIRSSControl.cpp +@@ -122,7 +122,9 @@ void CGUIRSSControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyre + dirty = true; + + if (CRssManager::Get().GetReader(GetID(), GetParentID(), this, m_pReader)) +- m_scrollInfo.characterPos = m_pReader->m_SavedScrollPos; ++ { ++ m_scrollInfo.pixelPos = m_pReader->m_savedScrollPixelPos; ++ } + else + { + if (m_strRSSTags != "") +@@ -180,7 +182,7 @@ void CGUIRSSControl::Render() + if (m_pReader) + { + m_pReader->CheckForUpdates(); +- m_pReader->m_SavedScrollPos = m_scrollInfo.characterPos; ++ m_pReader->m_savedScrollPixelPos = m_scrollInfo.pixelPos; + } + } + CGUIControl::Render(); +diff --git a/xbmc/utils/RssReader.cpp b/xbmc/utils/RssReader.cpp +index b1e53b7..f68597a 100644 +--- a/xbmc/utils/RssReader.cpp ++++ b/xbmc/utils/RssReader.cpp +@@ -54,7 +54,7 @@ + m_pObserver = NULL; + m_spacesBetweenFeeds = 0; + m_bIsRunning = false; +- m_SavedScrollPos = 0; ++ m_savedScrollPixelPos = 0; + m_rtlText = false; + m_requestRefresh = false; + } +diff --git a/xbmc/utils/RssReader.h b/xbmc/utils/RssReader.h +index 2c6f366..b74faf2 100644 +--- a/xbmc/utils/RssReader.h ++++ b/xbmc/utils/RssReader.h +@@ -43,7 +43,7 @@ class CRssReader : public CThread + void SetObserver(IRssObserver* observer); + void CheckForUpdates(); + void requestRefresh(); +- unsigned int m_SavedScrollPos; ++ float m_savedScrollPixelPos; + + private: + void Process(); +-- +1.8.5.1 + + +From 38f0baa539e8ded3affdf13fa6640caa7dd8bebe Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Mon, 27 Jan 2014 23:21:10 +0000 +Subject: [PATCH 08/17] Move the application of the translation offsets into + the GLES code. Still all pure software at this stage. Main change is in the + data types at the interface between CGUIFontTTFBase and CGUIFontTTFGL. The + old way (array of vertices in m_vertex) are retained in addition, for the + sake`of cases that need to use software clipping on GLES, as well as for DX + and GL support where the new scheme is not (yet?) used. + +--- + xbmc/guilib/GUIFontTTF.cpp | 19 +++--------- + xbmc/guilib/GUIFontTTF.h | 17 +++++++++++ + xbmc/guilib/GUIFontTTFGL.cpp | 72 ++++++++++++++++++++++++++++++++------------ + 3 files changed, 73 insertions(+), 35 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 73f0e50..ad0a53b 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -215,6 +215,7 @@ void CGUIFontTTFBase::Clear() + g_freeTypeLibrary.ReleaseStroker(m_stroker); + m_stroker = NULL; + ++ m_vertexTrans.clear(); + m_vertex.clear(); + } + +@@ -310,6 +311,7 @@ void CGUIFontTTFBase::Begin() + { + if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin()) + { ++ m_vertexTrans.clear(); + m_vertex.clear(); + } + // Keep track of the nested begin/end calls. +@@ -457,23 +459,10 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + cursorX += ch->advance; + } + if (hardwareClipping) +- /* Append the new vertices (which we have just constructed in the cache) +- * to the set collected since the first Begin() call */ +- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); ++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices)); + } + else if (hardwareClipping) +- { +- /* Apply the translation offset to the vertices from the cache after +- * appending them to the set collected since the first Begin() call */ +- m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end()); +- SVertex *v; +- for (v = &*m_vertex.end() - vertices.size(); v != &*m_vertex.end(); v++) +- { +- v->x += dynamicPos.m_x; +- v->y += dynamicPos.m_y; +- v->z += dynamicPos.m_z; +- } +- } ++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices)); + if (!hardwareClipping) + /* Append the new vertices (from the cache or otherwise) to the set collected + * since the first Begin() call */ +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 78445ab..c71f90d 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -61,6 +61,14 @@ struct SVertex + unsigned char r, g, b, a; + #endif + float u, v; ++ struct SVertex Offset(float translate[3]) const ++ { ++ SVertex out = *this; ++ out.x += translate[0]; ++ out.y += translate[1]; ++ out.z += translate[2]; ++ return out; ++ } + }; + + +@@ -160,6 +168,15 @@ class CGUIFontTTFBase + bool m_bTextureLoaded; + unsigned int m_nTexture; + ++ struct CTranslatedVertices ++ { ++ float translateX; ++ float translateY; ++ float translateZ; ++ const std::vector *vertexBuffer; ++ CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {} ++ }; ++ std::vector m_vertexTrans; + std::vector m_vertex; + + float m_textureScaleX; +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index cb56987..f6aa081 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -146,34 +146,65 @@ void CGUIFontTTFGL::LastEnd() + GLint colLoc = g_Windowing.GUIShaderGetCol(); + GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); + +- // stack object until VBOs will be used +- std::vector vecVertices( 6 * (m_vertex.size() / 4) ); +- SVertex *vertices = &vecVertices[0]; ++ // Enable the attributes used by this shader ++ glEnableVertexAttribArray(posLoc); ++ glEnableVertexAttribArray(colLoc); ++ glEnableVertexAttribArray(tex0Loc); + +- for (size_t i=0; i 0) + { +- *vertices++ = m_vertex[i]; +- *vertices++ = m_vertex[i+1]; +- *vertices++ = m_vertex[i+2]; ++ // Deal with vertices that had to use software clipping ++ std::vector vecVertices( 6 * (m_vertex.size() / 4) ); ++ SVertex *vertices = &vecVertices[0]; + +- *vertices++ = m_vertex[i+1]; +- *vertices++ = m_vertex[i+3]; +- *vertices++ = m_vertex[i+2]; +- } ++ for (size_t i=0; i 0) ++ { ++ // Deal with the vertices that can be hardware clipped and therefore translated ++ std::vector vecVertices; ++ for (size_t i = 0; i < m_vertexTrans.size(); i++) ++ { ++ float translate[3] = { m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ }; ++ for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4) ++ { ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate)); ++ } ++ } ++ SVertex *vertices = &vecVertices[0]; + +- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); ++ // Normalize color values. Does not affect Performance at all. ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); ++ ++ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ } + ++ // Disable the attributes used by this shader + glDisableVertexAttribArray(posLoc); + glDisableVertexAttribArray(colLoc); + glDisableVertexAttribArray(tex0Loc); +@@ -201,6 +232,7 @@ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight) + if (m_textureHeight < newHeight) + CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight); + m_staticCache.Flush(); ++ m_dynamicCache.Flush(); + + memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch()); + if (m_texture) +-- +1.8.5.1 + + +From 66d47fad537cd3fc7dbbe23c2aad897a2b085c72 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Wed, 15 Jan 2014 15:28:06 +0000 +Subject: [PATCH 09/17] Rather than applying the translation offsets to the + vertices, now applies them to the model view matrix from the top of the + matrix stack and pushes it over to OpenGL. The vertices themselves are still + all held client-side. + +--- + xbmc/guilib/GUIFontTTF.h | 8 ------- + xbmc/guilib/GUIFontTTFGL.cpp | 40 +++++++++++++++++++++----------- + xbmc/guilib/GUIShader.h | 1 + + xbmc/rendering/gles/RenderSystemGLES.cpp | 8 +++++++ + xbmc/rendering/gles/RenderSystemGLES.h | 1 + + 5 files changed, 36 insertions(+), 22 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index c71f90d..fde2085 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -61,14 +61,6 @@ struct SVertex + unsigned char r, g, b, a; + #endif + float u, v; +- struct SVertex Offset(float translate[3]) const +- { +- SVertex out = *this; +- out.x += translate[0]; +- out.y += translate[1]; +- out.z += translate[2]; +- return out; +- } + }; + + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index f6aa081..fbffaa0 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -29,6 +29,7 @@ + #include "utils/log.h" + #include "utils/GLUtils.h" + #include "windowing/WindowingFactory.h" ++#include "guilib/MatrixGLES.h" + + // stuff for freetype + #include +@@ -145,6 +146,7 @@ void CGUIFontTTFGL::LastEnd() + GLint posLoc = g_Windowing.GUIShaderGetPos(); + GLint colLoc = g_Windowing.GUIShaderGetCol(); + GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); ++ GLint modelLoc = g_Windowing.GUIShaderGetModel(); + + // Enable the attributes used by this shader + glEnableVertexAttribArray(posLoc); +@@ -183,25 +185,35 @@ void CGUIFontTTFGL::LastEnd() + std::vector vecVertices; + for (size_t i = 0; i < m_vertexTrans.size(); i++) + { +- float translate[3] = { m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ }; ++ // Apply the translation to the currently active (top-of-stack) model view matrix ++ g_matrices.MatrixMode(MM_MODELVIEW); ++ g_matrices.PushMatrix(); ++ g_matrices.Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ); ++ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); ++ ++ vecVertices.clear(); + for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4) + { +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3].Offset(translate)); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate)); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3]); ++ vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]); + } +- } +- SVertex *vertices = &vecVertices[0]; ++ SVertex *vertices = &vecVertices[0]; + +- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); +- // Normalize color values. Does not affect Performance at all. +- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); +- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); ++ // Normalize color values. Does not affect Performance at all. ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); + +- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ ++ g_matrices.PopMatrix(); ++ } ++ // Restore the original model view matrix ++ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); + } + + // Disable the attributes used by this shader +diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h +index 86ce4cc..ba01956 100644 +--- a/xbmc/guilib/GUIShader.h ++++ b/xbmc/guilib/GUIShader.h +@@ -41,6 +41,7 @@ class CGUIShader : public CGLSLShaderProgram + GLint GetCord1Loc() { return m_hCord1; } + GLint GetUniColLoc() { return m_hUniCol; } + GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; } ++ GLint GetModelLoc() { return m_hModel; } + bool HardwareClipIsPossible() { return m_clipPossible; } + GLfloat GetClipXFactor() { return m_clipXFactor; } + GLfloat GetClipXOffset() { return m_clipXOffset; } +diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp +index 87b2cb6..3069ed8 100644 +--- a/xbmc/rendering/gles/RenderSystemGLES.cpp ++++ b/xbmc/rendering/gles/RenderSystemGLES.cpp +@@ -678,4 +678,12 @@ GLint CRenderSystemGLES::GUIShaderGetCoord0Matrix() + return -1; + } + ++GLint CRenderSystemGLES::GUIShaderGetModel() ++{ ++ if (m_pGUIshader[m_method]) ++ return m_pGUIshader[m_method]->GetModelLoc(); ++ ++ return -1; ++} ++ + #endif +diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h +index af2dbbb..ac1780b 100644 +--- a/xbmc/rendering/gles/RenderSystemGLES.h ++++ b/xbmc/rendering/gles/RenderSystemGLES.h +@@ -90,6 +90,7 @@ class CRenderSystemGLES : public CRenderSystemBase + GLint GUIShaderGetCoord1(); + GLint GUIShaderGetUniCol(); + GLint GUIShaderGetCoord0Matrix(); ++ GLint GUIShaderGetModel(); + + protected: + virtual void SetVSyncImpl(bool enable) = 0; +-- +1.8.5.1 + + +From 8cb5497b9d572d6761edb7778d4ced562d32141d Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Wed, 29 Jan 2014 13:21:19 +0000 +Subject: [PATCH 10/17] Enable hardware clipping. + +--- + xbmc/guilib/GUIFontTTF.cpp | 4 ++-- + xbmc/guilib/GUIFontTTF.h | 5 ++++- + xbmc/guilib/GUIFontTTFGL.cpp | 6 ++++++ + 3 files changed, 12 insertions(+), 3 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index ad0a53b..4dc4c8e 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -459,10 +459,10 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + cursorX += ch->advance; + } + if (hardwareClipping) +- m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices)); ++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices, g_graphicsContext.GetClipRegion())); + } + else if (hardwareClipping) +- m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices)); ++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices, g_graphicsContext.GetClipRegion())); + if (!hardwareClipping) + /* Append the new vertices (from the cache or otherwise) to the set collected + * since the first Begin() call */ +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index fde2085..5e7c31f 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -27,6 +27,8 @@ + * + */ + ++#include "Geometry.h" ++ + // forward definition + class CBaseTexture; + +@@ -166,7 +168,8 @@ class CGUIFontTTFBase + float translateY; + float translateZ; + const std::vector *vertexBuffer; +- CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {} ++ CRect clip; ++ CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {} + }; + std::vector m_vertexTrans; + std::vector m_vertex; +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index fbffaa0..b7618e1 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -185,6 +185,10 @@ void CGUIFontTTFGL::LastEnd() + std::vector vecVertices; + for (size_t i = 0; i < m_vertexTrans.size(); i++) + { ++ // Apply the clip rectangle ++ CRect clip = g_Windowing.ClipRectToScissorRect(m_vertexTrans[i].clip); ++ g_graphicsContext.SetScissors(clip); ++ + // Apply the translation to the currently active (top-of-stack) model view matrix + g_matrices.MatrixMode(MM_MODELVIEW); + g_matrices.PushMatrix(); +@@ -212,6 +216,8 @@ void CGUIFontTTFGL::LastEnd() + + g_matrices.PopMatrix(); + } ++ // Restore the original scissor rectangle ++ g_graphicsContext.ResetScissors(); + // Restore the original model view matrix + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); + } +-- +1.8.5.1 + + +From 7a9076fcb30b45d7071cc64def09b0745974c988 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Wed, 15 Jan 2014 15:32:51 +0000 +Subject: [PATCH 11/17] Move the vertex data across to a vertex buffer object + just prior to drawing. + +--- + xbmc/guilib/GUIFontTTFGL.cpp | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index b7618e1..0df3749 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -207,12 +207,24 @@ void CGUIFontTTFGL::LastEnd() + } + SVertex *vertices = &vecVertices[0]; + +- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x)); +- // Normalize color values. Does not affect Performance at all. +- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r)); +- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u)); +- ++ // Generate a unique buffer object name and put it in vertexBuffer ++ GLuint vertexBuffer; ++ glGenBuffers(1, &vertexBuffer); ++ // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point ++ glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); ++ // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER ++ // binding point (i.e. our buffer object) and initialise it from the ++ // specified client-side pointer ++ glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof *vertices, vertices, GL_STATIC_DRAW); ++ // Set up the offsets of the various vertex attributes within the buffer ++ // object bound to GL_ARRAY_BUFFER ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x)); ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r)); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u)); ++ // Do the actual drawing operation, using the full set of vertices in the buffer + glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); ++ // Release the buffer name for reuse ++ glDeleteBuffers(1, &vertexBuffer); + + g_matrices.PopMatrix(); + } +@@ -220,6 +232,8 @@ void CGUIFontTTFGL::LastEnd() + g_graphicsContext.ResetScissors(); + // Restore the original model view matrix + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); ++ // Unbind GL_ARRAY_BUFFER ++ glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + // Disable the attributes used by this shader +-- +1.8.5.1 + + +From 0751ac01d3cb02b36c21828c94c33c6d090a63b5 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Wed, 15 Jan 2014 16:04:04 +0000 +Subject: [PATCH 12/17] Move vertex data into an OpenGL VBO when the font cache + entry is populated. The font cache now stores the "name" (handle) of the VBO, + rather than a vector of vertices. + +--- + xbmc/guilib/GUIFontCache.cpp | 6 ++++ + xbmc/guilib/GUIFontCache.h | 30 +++++++++++++++++- + xbmc/guilib/GUIFontTTF.cpp | 15 +++++++-- + xbmc/guilib/GUIFontTTF.h | 7 +++-- + xbmc/guilib/GUIFontTTFGL.cpp | 74 ++++++++++++++++++++++++++++++-------------- + xbmc/guilib/GUIFontTTFGL.h | 5 +++ + 6 files changed, 107 insertions(+), 30 deletions(-) + +diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp +index b66c00b..895fa72 100644 +--- a/xbmc/guilib/GUIFontCache.cpp ++++ b/xbmc/guilib/GUIFontCache.cpp +@@ -111,3 +111,9 @@ + template CGUIFontCacheEntry::~CGUIFontCacheEntry(); + template CGUIFontCacheDynamicValue &CGUIFontCache::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &); + template void CGUIFontCache::Flush(); ++ ++void CVertexBuffer::clear() ++{ ++ if (m_font != NULL) ++ m_font->DestroyVertexBuffer(*this); ++} +diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h +index d913dee..ff766bf 100644 +--- a/xbmc/guilib/GUIFontCache.h ++++ b/xbmc/guilib/GUIFontCache.h +@@ -234,7 +234,35 @@ struct CGUIFontCacheDynamicPosition + } + }; + +-typedef std::vector CGUIFontCacheDynamicValue; ++struct CVertexBuffer ++{ ++ void *bufferHandle; ++ size_t size; ++ CVertexBuffer() : bufferHandle(NULL), size(0), m_font(NULL) {} ++ CVertexBuffer(void *bufferHandle, size_t size, const CGUIFontTTFBase *font) : bufferHandle(bufferHandle), size(size), m_font(font) {} ++ CVertexBuffer(const CVertexBuffer &other) : bufferHandle(other.bufferHandle), size(other.size), m_font(other.m_font) ++ { ++ /* In practice, the copy constructor is only called before a vertex buffer ++ * has been attached. If this should ever change, we'll need another support ++ * function in GUIFontTTFGL/DX to duplicate a buffer, given its handle. */ ++ assert(other.bufferHandle == 0); ++ } ++ CVertexBuffer &operator=(CVertexBuffer &other) ++ { ++ /* This is used with move-assignment semantics for initialising the object in the font cache */ ++ assert(bufferHandle == 0); ++ bufferHandle = other.bufferHandle; ++ other.bufferHandle = 0; ++ size = other.size; ++ m_font = other.m_font; ++ return *this; ++ } ++ void clear(); ++private: ++ const CGUIFontTTFBase *m_font; ++}; ++ ++typedef CVertexBuffer CGUIFontCacheDynamicValue; + + inline bool Match(const CGUIFontCacheDynamicPosition &a, const TransformMatrix &a_m, + const CGUIFontCacheDynamicPosition &b, const TransformMatrix &b_m, +diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp +index 4dc4c8e..8b25306 100644 +--- a/xbmc/guilib/GUIFontTTF.cpp ++++ b/xbmc/guilib/GUIFontTTF.cpp +@@ -343,13 +343,18 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + g_graphicsContext.ScaleFinalYCoord(x, y), + g_graphicsContext.ScaleFinalZCoord(x, y)); + } +- std::vector &vertices = hardwareClipping ? ++ CVertexBuffer unusedVertexBuffer; ++ CVertexBuffer &vertexBuffer = hardwareClipping ? + m_dynamicCache.Lookup(dynamicPos, + colors, text, + alignment, maxPixelWidth, + scrolling, + XbmcThreads::SystemClockMillis(), + dirtyCache) : ++ unusedVertexBuffer; ++ std::vector tempVertices; ++ std::vector &vertices = hardwareClipping ? ++ tempVertices : + m_staticCache.Lookup(staticPos, + colors, text, + alignment, maxPixelWidth, +@@ -459,10 +464,14 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors + cursorX += ch->advance; + } + if (hardwareClipping) +- m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices, g_graphicsContext.GetClipRegion())); ++ { ++ CVertexBuffer newVertexBuffer = CreateVertexBuffer(tempVertices); ++ vertexBuffer = newVertexBuffer; ++ m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertexBuffer, g_graphicsContext.GetClipRegion())); ++ } + } + else if (hardwareClipping) +- m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices, g_graphicsContext.GetClipRegion())); ++ m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertexBuffer, g_graphicsContext.GetClipRegion())); + if (!hardwareClipping) + /* Append the new vertices (from the cache or otherwise) to the set collected + * since the first Begin() call */ +diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h +index 5e7c31f..b1cd525 100644 +--- a/xbmc/guilib/GUIFontTTF.h ++++ b/xbmc/guilib/GUIFontTTF.h +@@ -84,6 +84,9 @@ class CGUIFontTTFBase + + void Begin(); + void End(); ++ /* The next two should only be called if we've declared we can do hardware clipping */ ++ virtual CVertexBuffer CreateVertexBuffer(const std::vector &vertices) const { assert(false); return CVertexBuffer(); } ++ virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const {} + + const CStdString& GetFileName() const { return m_strFileName; }; + +@@ -167,9 +170,9 @@ class CGUIFontTTFBase + float translateX; + float translateY; + float translateZ; +- const std::vector *vertexBuffer; ++ const CVertexBuffer *vertexBuffer; + CRect clip; +- CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {} ++ CTranslatedVertices(float translateX, float translateY, float translateZ, const CVertexBuffer *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {} + }; + std::vector m_vertexTrans; + std::vector m_vertex; +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index 0df3749..1cd684b 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -49,6 +49,10 @@ + + CGUIFontTTFGL::~CGUIFontTTFGL(void) + { ++ // It's important that all the CGUIFontCacheEntry objects are ++ // destructed before the CGUIFontTTFGL goes out of scope, because ++ // our virtual methods won't be accessible after this point ++ m_dynamicCache.Flush(); + } + + bool CGUIFontTTFGL::FirstBegin() +@@ -182,7 +186,6 @@ void CGUIFontTTFGL::LastEnd() + if (m_vertexTrans.size() > 0) + { + // Deal with the vertices that can be hardware clipped and therefore translated +- std::vector vecVertices; + for (size_t i = 0; i < m_vertexTrans.size(); i++) + { + // Apply the clip rectangle +@@ -195,36 +198,17 @@ void CGUIFontTTFGL::LastEnd() + g_matrices.Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ); + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); + +- vecVertices.clear(); +- for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4) +- { +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3]); +- vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]); +- } +- SVertex *vertices = &vecVertices[0]; +- +- // Generate a unique buffer object name and put it in vertexBuffer +- GLuint vertexBuffer; +- glGenBuffers(1, &vertexBuffer); + // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point +- glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); +- // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER +- // binding point (i.e. our buffer object) and initialise it from the +- // specified client-side pointer +- glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof *vertices, vertices, GL_STATIC_DRAW); ++ glBindBuffer(GL_ARRAY_BUFFER, (GLuint) m_vertexTrans[i].vertexBuffer->bufferHandle); ++ + // Set up the offsets of the various vertex attributes within the buffer + // object bound to GL_ARRAY_BUFFER + glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x)); + glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r)); + glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u)); ++ + // Do the actual drawing operation, using the full set of vertices in the buffer +- glDrawArrays(GL_TRIANGLES, 0, vecVertices.size()); +- // Release the buffer name for reuse +- glDeleteBuffers(1, &vertexBuffer); ++ glDrawArrays(GL_TRIANGLES, 0, 6 * m_vertexTrans[i].vertexBuffer->size); + + g_matrices.PopMatrix(); + } +@@ -245,6 +229,48 @@ void CGUIFontTTFGL::LastEnd() + #endif + } + ++#if HAS_GLES ++CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector &vertices) const ++{ ++ // Rearrange the vertices to describe triangles ++ std::vector triangleVertices; ++ triangleVertices.reserve(vertices.size() * 6 / 4); ++ for (size_t i = 0; i < vertices.size(); i += 4) ++ { ++ triangleVertices.push_back(vertices[i]); ++ triangleVertices.push_back(vertices[i+1]); ++ triangleVertices.push_back(vertices[i+2]); ++ triangleVertices.push_back(vertices[i+1]); ++ triangleVertices.push_back(vertices[i+3]); ++ triangleVertices.push_back(vertices[i+2]); ++ } ++ ++ // Generate a unique buffer object name and put it in bufferHandle ++ GLuint bufferHandle; ++ glGenBuffers(1, &bufferHandle); ++ // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point ++ glBindBuffer(GL_ARRAY_BUFFER, bufferHandle); ++ // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER ++ // binding point (i.e. our buffer object) and initialise it from the ++ // specified client-side pointer ++ glBufferData(GL_ARRAY_BUFFER, triangleVertices.size() * sizeof (SVertex), &triangleVertices[0], GL_STATIC_DRAW); ++ // Unbind GL_ARRAY_BUFFER ++ glBindBuffer(GL_ARRAY_BUFFER, 0); ++ ++ return CVertexBuffer((void *) bufferHandle, vertices.size() / 4, this); ++} ++ ++void CGUIFontTTFGL::DestroyVertexBuffer(CVertexBuffer &buffer) const ++{ ++ if (buffer.bufferHandle != 0) ++ { ++ // Release the buffer name for reuse ++ glDeleteBuffers(1, (GLuint *) &buffer.bufferHandle); ++ buffer.bufferHandle = 0; ++ } ++} ++#endif ++ + CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight) + { + newHeight = CBaseTexture::PadPow2(newHeight); +diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h +index 6736cf7..168fb21 100644 +--- a/xbmc/guilib/GUIFontTTFGL.h ++++ b/xbmc/guilib/GUIFontTTFGL.h +@@ -29,6 +29,7 @@ + + + #include "GUIFontTTF.h" ++#include "system.h" + + + /*! +@@ -43,6 +44,10 @@ class CGUIFontTTFGL : public CGUIFontTTFBase + + virtual bool FirstBegin(); + virtual void LastEnd(); ++#if HAS_GLES ++ virtual CVertexBuffer CreateVertexBuffer(const std::vector &vertices) const; ++ virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const; ++#endif + + protected: + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight); +-- +1.8.5.1 + + +From 694ebf8bb53c9d664c2750bc4d590bc84f7dac2b Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Thu, 16 Jan 2014 16:29:42 +0000 +Subject: [PATCH 13/17] Switch from glDrawArrays() to glDrawElements(). This + involves setting up a static VBO containing the indexes necessary to convert + from quads to triangles on the fly in the GPU. + +--- + xbmc/guilib/GUIFontTTFGL.cpp | 72 +++++++++++++++++++++++++------------ + xbmc/guilib/GUIFontTTFGL.h | 9 +++++ + xbmc/windowing/egl/WinSystemEGL.cpp | 17 +++++++++ + 3 files changed, 76 insertions(+), 22 deletions(-) + +diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp +index 1cd684b..d476409 100644 +--- a/xbmc/guilib/GUIFontTTFGL.cpp ++++ b/xbmc/guilib/GUIFontTTFGL.cpp +@@ -186,6 +186,10 @@ void CGUIFontTTFGL::LastEnd() + if (m_vertexTrans.size() > 0) + { + // Deal with the vertices that can be hardware clipped and therefore translated ++ ++ // Bind our pre-calculated array to GL_ELEMENT_ARRAY_BUFFER ++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle); ++ + for (size_t i = 0; i < m_vertexTrans.size(); i++) + { + // Apply the clip rectangle +@@ -201,14 +205,21 @@ void CGUIFontTTFGL::LastEnd() + // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point + glBindBuffer(GL_ARRAY_BUFFER, (GLuint) m_vertexTrans[i].vertexBuffer->bufferHandle); + +- // Set up the offsets of the various vertex attributes within the buffer +- // object bound to GL_ARRAY_BUFFER +- glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x)); +- glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, r)); +- glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u)); ++ // Do the actual drawing operation, split into groups of characters no ++ // larger than the pre-determined size of the element array ++ for (size_t character = 0; m_vertexTrans[i].vertexBuffer->size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX) ++ { ++ size_t count = m_vertexTrans[i].vertexBuffer->size - character; ++ count = std::min(count, ELEMENT_ARRAY_MAX_CHAR_INDEX); ++ ++ // Set up the offsets of the various vertex attributes within the buffer ++ // object bound to GL_ARRAY_BUFFER ++ glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, x))); ++ glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, r))); ++ glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, u))); + +- // Do the actual drawing operation, using the full set of vertices in the buffer +- glDrawArrays(GL_TRIANGLES, 0, 6 * m_vertexTrans[i].vertexBuffer->size); ++ glDrawElements(GL_TRIANGLES, 6 * count, GL_UNSIGNED_SHORT, 0); ++ } + + g_matrices.PopMatrix(); + } +@@ -216,8 +227,9 @@ void CGUIFontTTFGL::LastEnd() + g_graphicsContext.ResetScissors(); + // Restore the original model view matrix + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW)); +- // Unbind GL_ARRAY_BUFFER ++ // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER + glBindBuffer(GL_ARRAY_BUFFER, 0); ++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + // Disable the attributes used by this shader +@@ -232,19 +244,6 @@ void CGUIFontTTFGL::LastEnd() + #if HAS_GLES + CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector &vertices) const + { +- // Rearrange the vertices to describe triangles +- std::vector triangleVertices; +- triangleVertices.reserve(vertices.size() * 6 / 4); +- for (size_t i = 0; i < vertices.size(); i += 4) +- { +- triangleVertices.push_back(vertices[i]); +- triangleVertices.push_back(vertices[i+1]); +- triangleVertices.push_back(vertices[i+2]); +- triangleVertices.push_back(vertices[i+1]); +- triangleVertices.push_back(vertices[i+3]); +- triangleVertices.push_back(vertices[i+2]); +- } +- + // Generate a unique buffer object name and put it in bufferHandle + GLuint bufferHandle; + glGenBuffers(1, &bufferHandle); +@@ -253,7 +252,7 @@ CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector &vert + // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER + // binding point (i.e. our buffer object) and initialise it from the + // specified client-side pointer +- glBufferData(GL_ARRAY_BUFFER, triangleVertices.size() * sizeof (SVertex), &triangleVertices[0], GL_STATIC_DRAW); ++ glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof (SVertex), &vertices[0], GL_STATIC_DRAW); + // Unbind GL_ARRAY_BUFFER + glBindBuffer(GL_ARRAY_BUFFER, 0); + +@@ -348,4 +347,33 @@ void CGUIFontTTFGL::DeleteHardwareTexture() + } + } + ++#if HAS_GLES ++void CGUIFontTTFGL::CreateStaticVertexBuffers(void) ++{ ++ // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point ++ glGenBuffers(1, &m_elementArrayHandle); ++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle); ++ // Create an array holding the mesh indices to convert quads to triangles ++ GLushort index[ELEMENT_ARRAY_MAX_CHAR_INDEX][6]; ++ for (size_t i = 0; i < ELEMENT_ARRAY_MAX_CHAR_INDEX; i++) ++ { ++ index[i][0] = 4*i; ++ index[i][1] = 4*i+1; ++ index[i][2] = 4*i+2; ++ index[i][3] = 4*i+1; ++ index[i][4] = 4*i+3; ++ index[i][5] = 4*i+2; ++ } ++ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof index, index, GL_STATIC_DRAW); ++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); ++} ++ ++void CGUIFontTTFGL::DestroyStaticVertexBuffers(void) ++{ ++ glDeleteBuffers(1, &m_elementArrayHandle); ++} ++ ++GLuint CGUIFontTTFGL::m_elementArrayHandle; ++#endif ++ + #endif +diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h +index 168fb21..a14ab7a 100644 +--- a/xbmc/guilib/GUIFontTTFGL.h ++++ b/xbmc/guilib/GUIFontTTFGL.h +@@ -30,6 +30,7 @@ + + #include "GUIFontTTF.h" + #include "system.h" ++#include "system_gl.h" + + + /*! +@@ -47,6 +48,8 @@ class CGUIFontTTFGL : public CGUIFontTTFBase + #if HAS_GLES + virtual CVertexBuffer CreateVertexBuffer(const std::vector &vertices) const; + virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const; ++ static void CreateStaticVertexBuffers(void); ++ static void DestroyStaticVertexBuffers(void); + #endif + + protected: +@@ -54,6 +57,12 @@ class CGUIFontTTFGL : public CGUIFontTTFBase + virtual bool CopyCharToTexture(FT_BitmapGlyph bitGlyph, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2); + virtual void DeleteHardwareTexture(); + ++#if HAS_GLES ++#define ELEMENT_ARRAY_MAX_CHAR_INDEX (1000) ++ ++ static GLuint m_elementArrayHandle; ++#endif ++ + }; + + #endif +diff --git a/xbmc/windowing/egl/WinSystemEGL.cpp b/xbmc/windowing/egl/WinSystemEGL.cpp +index 2f063e6..5a437c2 100644 +--- a/xbmc/windowing/egl/WinSystemEGL.cpp ++++ b/xbmc/windowing/egl/WinSystemEGL.cpp +@@ -29,6 +29,7 @@ + #include "settings/AdvancedSettings.h" + #include "settings/Settings.h" + #include "settings/DisplaySettings.h" ++#include "guilib/GUIFontTTFGL.h" + #include "utils/log.h" + #include "EGLWrapper.h" + #include "EGLQuirks.h" +@@ -192,6 +193,9 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res) + return false; + } + ++#if HAS_GLES ++ bool newContext = false; ++#endif + if (m_context == EGL_NO_CONTEXT) + { + if (!m_egl->CreateContext(m_display, m_config, contextAttrs, &m_context)) +@@ -199,6 +203,9 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res) + CLog::Log(LOGERROR, "%s: Could not create context",__FUNCTION__); + return false; + } ++#if HAS_GLES ++ newContext = true; ++#endif + } + + if (!m_egl->BindContext(m_display, m_surface, m_context)) +@@ -207,6 +214,11 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res) + return false; + } + ++#if HAS_GLES ++ if (newContext) ++ CGUIFontTTFGL::CreateStaticVertexBuffers(); ++#endif ++ + // for the non-trivial dirty region modes, we need the EGL buffer to be preserved across updates + if (g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_COST_REDUCTION || + g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_UNION) +@@ -228,7 +240,12 @@ bool CWinSystemEGL::DestroyWindowSystem() + DestroyWindow(); + + if (m_context != EGL_NO_CONTEXT) ++ { ++#if HAS_GLES ++ CGUIFontTTFGL::DestroyStaticVertexBuffers(); ++#endif + m_egl->DestroyContext(m_display, m_context); ++ } + m_context = EGL_NO_CONTEXT; + + if (m_display != EGL_NO_DISPLAY) +-- +1.8.5.1 + + +From 973ea1a1b04d7fb1a43f7486a039855102c2abcf Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Tue, 4 Feb 2014 16:17:57 +0000 +Subject: [PATCH 14/17] Update Windows project files + +--- + project/VS2010Express/XBMC.vcxproj | 2 ++ + project/VS2010Express/XBMC.vcxproj.filters | 6 ++++++ + 2 files changed, 8 insertions(+) + +diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj +index 3d9875c..413206f 100644 +--- a/project/VS2010Express/XBMC.vcxproj ++++ b/project/VS2010Express/XBMC.vcxproj +@@ -539,6 +539,7 @@ + + + ++ + + + +@@ -2060,6 +2061,7 @@ + + + ++ + + + +diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters +index b5e72d9..190e598 100644 +--- a/project/VS2010Express/XBMC.vcxproj.filters ++++ b/project/VS2010Express/XBMC.vcxproj.filters +@@ -1036,6 +1036,9 @@ + + guilib + ++ ++ guilib ++ + + guilib + +@@ -4002,6 +4005,9 @@ + + guilib + ++ ++ guilib ++ + + guilib + +-- +1.8.5.1 + + +From 4a1c8ecbaf4a129100a6af4c5cd5031449bfe218 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Tue, 4 Feb 2014 16:49:45 +0000 +Subject: [PATCH 15/17] Update XCode project file + +--- + XBMC.xcodeproj/project.pbxproj | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj +index ae59e25..8cec946 100644 +--- a/XBMC.xcodeproj/project.pbxproj ++++ b/XBMC.xcodeproj/project.pbxproj +@@ -168,6 +168,9 @@ + 1D638128161E211E003603ED /* PeripheralImon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D638126161E211E003603ED /* PeripheralImon.cpp */; }; + 1DAFDB7C16DFDCA7007F8C68 /* PeripheralBusCEC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DAFDB7A16DFDCA7007F8C68 /* PeripheralBusCEC.cpp */; }; + 1DE0443515828F4B005DDB4D /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DE0443315828F4B005DDB4D /* Exception.cpp */; }; ++ 2FD7EC5F18A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; }; ++ 2FD7EC6018A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; }; ++ 2FD7EC6118A14FE50047F86C /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */; }; + 32C631281423A90F00F18420 /* JpegIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32C631261423A90F00F18420 /* JpegIO.cpp */; }; + 36A9443D15821E2800727135 /* DatabaseUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9443B15821E2800727135 /* DatabaseUtils.cpp */; }; + 36A9444115821E7C00727135 /* SortUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9443F15821E7C00727135 /* SortUtils.cpp */; }; +@@ -3523,6 +3526,8 @@ + 1DAFDB7B16DFDCA7007F8C68 /* PeripheralBusCEC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeripheralBusCEC.h; sourceTree = ""; }; + 1DE0443315828F4B005DDB4D /* Exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Exception.cpp; path = commons/Exception.cpp; sourceTree = ""; }; + 1DE0443415828F4B005DDB4D /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exception.h; path = commons/Exception.h; sourceTree = ""; }; ++ 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIFontCache.cpp; sourceTree = ""; }; ++ 2FD7EC5E18A14FE50047F86C /* GUIFontCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIFontCache.h; sourceTree = ""; }; + 32C631261423A90F00F18420 /* JpegIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JpegIO.cpp; sourceTree = ""; }; + 32C631271423A90F00F18420 /* JpegIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JpegIO.h; sourceTree = ""; }; + 36A9443B15821E2800727135 /* DatabaseUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseUtils.cpp; sourceTree = ""; }; +@@ -5865,6 +5870,8 @@ + 18B7C76A1294222E009E7A26 /* GUIFixedListContainer.cpp */, + 18B7C7101294222D009E7A26 /* GUIFixedListContainer.h */, + 18B7C76B1294222E009E7A26 /* GUIFont.cpp */, ++ 2FD7EC5D18A14FE50047F86C /* GUIFontCache.cpp */, ++ 2FD7EC5E18A14FE50047F86C /* GUIFontCache.h */, + 18B7C7111294222D009E7A26 /* GUIFont.h */, + 18B7C76C1294222E009E7A26 /* GUIFontManager.cpp */, + 18B7C7121294222D009E7A26 /* GUIFontManager.h */, +@@ -10749,6 +10756,7 @@ + 7C1409A9184015C9009F9411 /* InfoExpression.cpp in Sources */, + AE32174218313ADF0003FAFC /* XSLTUtils.cpp in Sources */, + 7C15DCBC1892481400FCE564 /* InfoBool.cpp in Sources */, ++ 2FD7EC5F18A14FE50047F86C /* GUIFontCache.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +@@ -11792,6 +11800,7 @@ + 7C1409AB184015C9009F9411 /* InfoExpression.cpp in Sources */, + AE32174318313AE10003FAFC /* XSLTUtils.cpp in Sources */, + 7C15DCBE1892481400FCE564 /* InfoBool.cpp in Sources */, ++ 2FD7EC6118A14FE50047F86C /* GUIFontCache.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +@@ -12837,6 +12846,7 @@ + 7C1409AA184015C9009F9411 /* InfoExpression.cpp in Sources */, + AE4E87A717354C4A00D15206 /* XSLTUtils.cpp in Sources */, + 7C15DCBD1892481400FCE564 /* InfoBool.cpp in Sources */, ++ 2FD7EC6018A14FE50047F86C /* GUIFontCache.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +-- +1.8.5.1 + + +From 661e76a40031c17e726b87f13005de6d17c4e2da Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Tue, 4 Feb 2014 17:44:34 +0000 +Subject: [PATCH 16/17] Clang seems to be more picky than gcc about some C++ + template syntax + +--- + xbmc/guilib/GUIFontCache.cpp | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp +index 895fa72..bd84b9a 100644 +--- a/xbmc/guilib/GUIFontCache.cpp ++++ b/xbmc/guilib/GUIFontCache.cpp +@@ -61,26 +61,26 @@ + alignment, maxPixelWidth, + scrolling, g_graphicsContext.GetGUIMatrix(), + g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY()); +- EntryHashIterator i = m_list.get().find(key); +- if (i == m_list.get().end()) ++ EntryHashIterator i = m_list.template get().find(key); ++ if (i == m_list.template get().end()) + { + /* Cache miss */ +- EntryAgeIterator oldest = m_list.get().begin(); +- if (!m_list.get().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) ++ EntryAgeIterator oldest = m_list.template get().begin(); ++ if (!m_list.template get().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT) + { + /* The oldest existing entry is old enough to expire and reuse */ +- m_list.get().modify(m_list.project(oldest), typename CGUIFontCacheEntry::Reassign(key, nowMillis)); +- m_list.get().relocate(m_list.get().end(), oldest); ++ m_list.template get().modify(m_list.template project(oldest), typename CGUIFontCacheEntry::Reassign(key, nowMillis)); ++ m_list.template get().relocate(m_list.template get().end(), oldest); + } + else + { + /* We need a new entry instead */ + /* Yes, this causes the creation an destruction of a temporary entry, but + * this code ought to only be used infrequently, when the cache needs to grow */ +- m_list.get().push_back(CGUIFontCacheEntry(*this, key, nowMillis)); ++ m_list.template get().push_back(CGUIFontCacheEntry(*this, key, nowMillis)); + } + dirtyCache = true; +- return (--m_list.get().end())->m_value; ++ return (--m_list.template get().end())->m_value; + } + else + { +@@ -90,7 +90,7 @@ + pos.UpdateWithOffsets(i->m_key.m_pos, scrolling); + /* Update time in entry and move to the back of the list */ + i->m_lastUsedMillis = nowMillis; +- m_list.get().relocate(m_list.get().end(), m_list.project(i)); ++ m_list.template get().relocate(m_list.template get().end(), m_list.template project(i)); + dirtyCache = false; + return i->m_value; + } +@@ -99,7 +99,7 @@ + template + void CGUIFontCache::Flush() + { +- m_list.get().clear(); ++ m_list.template get().clear(); + } + + template void CGUIFontCacheEntry::Reassign::operator()(CGUIFontCacheEntry &entry); +-- +1.8.5.1 + + +From c4db53861a2bf05b0e4f914a4a1e388ff2426f04 Mon Sep 17 00:00:00 2001 +From: Ben Avison +Date: Tue, 4 Feb 2014 18:52:14 +0000 +Subject: [PATCH 17/17] Fix header to hopefully permit iOS builds to work + again. GUIShader.cpp added #include windowing/egl/WinSystemEGL.h inside a but + also need the header windowing/osx/WinSystemIOS.h instead. The only thing + GUIShader.cpp needed was g_windowing.GetViewPort, which is provided by the + common base class CRenderSystemGLES of g_windowing in both cases, so I think + it should be sufficient to use windowing/WindowingFactory.h instead, which is + abstracted away from the other header files. + +--- + xbmc/guilib/GUIShader.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp +index 53bce09..86330cc 100644 +--- a/xbmc/guilib/GUIShader.cpp ++++ b/xbmc/guilib/GUIShader.cpp +@@ -26,7 +26,7 @@ + #include "GUIShader.h" + #include "MatrixGLES.h" + #include "utils/log.h" +-#include "windowing/egl/WinSystemEGL.h" ++#include "windowing/WindowingFactory.h" + #include "guilib/GraphicContext.h" + + CGUIShader::CGUIShader( const char *shader ) : CGLSLShaderProgram("guishader_vert.glsl", shader) +-- +1.8.5.1 +