[Libreoffice-commits] core.git: 6 commits - vcl/inc vcl/Library_vcl.mk vcl/opengl vcl/win

Tomaž Vajngerl tomaz.vajngerl at collabora.co.uk
Fri Apr 8 10:11:34 UTC 2016


 vcl/Library_vcl.mk                     |    1 
 vcl/inc/opengl/AccumulatedTextures.hxx |  113 +++++++++
 vcl/inc/opengl/FixedTextureAtlas.hxx   |    4 
 vcl/inc/opengl/PackedTextureAtlas.hxx  |   46 +++
 vcl/inc/opengl/texture.hxx             |   45 +--
 vcl/inc/openglgdiimpl.hxx              |    8 
 vcl/inc/win/salgdi.h                   |    5 
 vcl/opengl/FixedTextureAtlas.cxx       |   94 ++++++-
 vcl/opengl/PackedTextureAtlas.cxx      |  164 +++++++++++++
 vcl/opengl/gdiimpl.cxx                 |  117 +++++++++
 vcl/opengl/texture.cxx                 |  136 +++++++++--
 vcl/win/gdi/salgdi.cxx                 |    8 
 vcl/win/gdi/winlayout.cxx              |  390 +++++++++++----------------------
 13 files changed, 793 insertions(+), 338 deletions(-)

New commits:
commit cd089979607566bc26259ff9edb079842aabdd4e
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Fri Apr 8 17:08:04 2016 +0900

    opengl: refactor GL texture slot mechanism to be more general
    
    Slot mechanism in ImplOpenGLTexture was written to support needs
    for FixedTextureAtlas. This commit makes the slot mechanism more
    general so it can be used in other kinds of texture atlases like
    PackedTextureAtlas.
    
    The ImplOpenGLTexture still tracks slots, but it is not needed to
    define beforehand how many slots there are. The deallocation has
    been factored out, ImplOpenGLTexture instead calls a callback
    function that a slot with a specific "slot id" has been
    deallocated.
    
    Change-Id: I23950d325b803969f958d03ebf34805687c4e620

diff --git a/vcl/inc/opengl/FixedTextureAtlas.hxx b/vcl/inc/opengl/FixedTextureAtlas.hxx
index 8d104a7..bf9ee04 100644
--- a/vcl/inc/opengl/FixedTextureAtlas.hxx
+++ b/vcl/inc/opengl/FixedTextureAtlas.hxx
@@ -13,10 +13,11 @@
 
 #include "opengl/texture.hxx"
 
+struct FixedTexture;
 
 class VCL_PLUGIN_PUBLIC FixedTextureAtlasManager
 {
-    std::vector<ImplOpenGLTexture *> mpTextures;
+    std::vector<std::unique_ptr<FixedTexture>> maFixedTextures;
 
     int mWidthFactor;
     int mHeightFactor;
diff --git a/vcl/inc/opengl/texture.hxx b/vcl/inc/opengl/texture.hxx
index f54604a..5bbf26c 100644
--- a/vcl/inc/opengl/texture.hxx
+++ b/vcl/inc/opengl/texture.hxx
@@ -40,7 +40,7 @@ public:
     GLuint mnOptStencil;
 
     std::unique_ptr<std::vector<int>> mpSlotReferences;
-    int mnFreeSlots;
+    std::function<void(int)> mFunctSlotDeallocateCallback;
 
     ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate );
     ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int nType, void const * pData );
@@ -58,8 +58,13 @@ public:
         return mnRefCount == 1;
     }
 
-    bool InitializeSlots(int nSlotSize);
-    int  FindFreeSlot();
+    bool InitializeSlotMechanism(int nInitialSlotSize = 0);
+
+    void SetSlotDeallocateCallback(std::function<void(int)> aCallback)
+    {
+        mFunctSlotDeallocateCallback = aCallback;
+    }
+
     GLuint AddStencil();
 };
 
diff --git a/vcl/opengl/FixedTextureAtlas.cxx b/vcl/opengl/FixedTextureAtlas.cxx
index 1ed8311..87c5bb1 100644
--- a/vcl/opengl/FixedTextureAtlas.cxx
+++ b/vcl/opengl/FixedTextureAtlas.cxx
@@ -17,6 +17,52 @@
 
 #include "opengl/FixedTextureAtlas.hxx"
 
+struct FixedTexture
+{
+    ImplOpenGLTexture* mpTexture;
+    int mnFreeSlots;
+    std::vector<bool> maAllocatedSlots;
+
+    FixedTexture(ImplOpenGLTexture* pTexture, int nNumberOfSlots)
+        : mpTexture(pTexture)
+        , mnFreeSlots(nNumberOfSlots)
+        , maAllocatedSlots(nNumberOfSlots, false)
+    {
+        auto aDeallocateFunction = [this] (int nSlotNumber)
+        {
+            deallocateSlot(nSlotNumber);
+        };
+
+        mpTexture->SetSlotDeallocateCallback(aDeallocateFunction);
+        mpTexture->InitializeSlotMechanism(nNumberOfSlots);
+    }
+
+    void allocateSlot(int nSlot)
+    {
+        maAllocatedSlots[nSlot] = true;
+        mnFreeSlots--;
+    }
+
+    void deallocateSlot(int nSlot)
+    {
+        maAllocatedSlots[nSlot] = false;
+        mnFreeSlots++;
+    }
+
+    int findAndAllocateFreeSlot()
+    {
+        for (size_t i = 0; i < maAllocatedSlots.size(); ++i)
+        {
+            if (!maAllocatedSlots[i])
+            {
+                allocateSlot(i);
+                return i;
+            }
+        }
+        return -1;
+    }
+};
+
 FixedTextureAtlasManager::FixedTextureAtlasManager(int nWidthFactor, int nHeightFactor, int nSubTextureSize)
     : mWidthFactor(nWidthFactor)
     , mHeightFactor(nHeightFactor)
@@ -26,11 +72,10 @@ FixedTextureAtlasManager::FixedTextureAtlasManager(int nWidthFactor, int nHeight
 
 FixedTextureAtlasManager::~FixedTextureAtlasManager()
 {
-    for (auto i = mpTextures.begin(); i != mpTextures.end(); ++i)
+    for (std::unique_ptr<FixedTexture>& pFixedTexture : maFixedTextures)
     {
         // Free texture early in VCL shutdown while we have a context.
-        (*i)->Dispose();
-        (*i)->DecreaseRefCount(0);
+        delete pFixedTexture->mpTexture;
     }
 }
 
@@ -38,32 +83,35 @@ void FixedTextureAtlasManager::CreateNewTexture()
 {
     int nTextureWidth = mWidthFactor  * mSubTextureSize;
     int nTextureHeight = mHeightFactor * mSubTextureSize;
-    mpTextures.push_back(new ImplOpenGLTexture(nTextureWidth, nTextureHeight, true));
-    mpTextures.back()->InitializeSlots(mWidthFactor * mHeightFactor);
+    std::unique_ptr<FixedTexture> pFixedTexture(
+                                    new FixedTexture(new ImplOpenGLTexture(nTextureWidth, nTextureHeight, true),
+                                    mWidthFactor * mHeightFactor));
+
+    maFixedTextures.push_back(std::move(pFixedTexture));
 }
 
 OpenGLTexture FixedTextureAtlasManager::Reserve(int nWidth, int nHeight)
 {
-    ImplOpenGLTexture* pTexture = nullptr;
+    FixedTexture* pFixedTexture = nullptr;
 
-    auto funFreeSlot = [] (ImplOpenGLTexture *mpTexture)
+    auto funFreeSlot = [] (std::unique_ptr<FixedTexture>& inFixedTexture)
     {
-        return mpTexture->mnFreeSlots > 0;
+        return inFixedTexture->mnFreeSlots > 0;
     };
 
-    auto it = std::find_if(mpTextures.begin(), mpTextures.end(), funFreeSlot);
+    auto it = std::find_if(maFixedTextures.begin(), maFixedTextures.end(), funFreeSlot);
 
-    if (it != mpTextures.end())
+    if (it != maFixedTextures.end())
     {
-        pTexture = *it;
+        pFixedTexture = (*it).get();
     }
     else
     {
         CreateNewTexture();
-        pTexture = mpTextures.back();
+        pFixedTexture = maFixedTextures.back().get();
     }
 
-    int nSlot = pTexture->FindFreeSlot();
+    int nSlot = pFixedTexture->findAndAllocateFreeSlot();
 
     // Calculate coordinates in texture
     int nX = (nSlot % mWidthFactor) * mSubTextureSize;
@@ -71,7 +119,7 @@ OpenGLTexture FixedTextureAtlasManager::Reserve(int nWidth, int nHeight)
 
     Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
 
-    return OpenGLTexture(pTexture, aRectangle, nSlot);
+    return OpenGLTexture(pFixedTexture->mpTexture, aRectangle, nSlot);
 }
 
 OpenGLTexture FixedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
diff --git a/vcl/opengl/texture.cxx b/vcl/opengl/texture.cxx
index 6388a92..b3a3626 100644
--- a/vcl/opengl/texture.cxx
+++ b/vcl/opengl/texture.cxx
@@ -43,8 +43,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate )
     mnWidth( nWidth ),
     mnHeight( nHeight ),
     mnFilter( GL_NEAREST ),
-    mnOptStencil( 0 ),
-    mnFreeSlots(-1)
+    mnOptStencil( 0 )
 {
     OpenGLVCLContextZone aContextZone;
 
@@ -78,8 +77,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nX, int nY, int nWidth, int nHeight )
     mnWidth( nWidth ),
     mnHeight( nHeight ),
     mnFilter( GL_NEAREST ),
-    mnOptStencil( 0 ),
-    mnFreeSlots(-1)
+    mnOptStencil( 0 )
 {
     OpenGLVCLContextZone aContextZone;
 
@@ -113,8 +111,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int
     mnWidth( nWidth ),
     mnHeight( nHeight ),
     mnFilter( GL_NEAREST ),
-    mnOptStencil( 0 ),
-    mnFreeSlots(-1)
+    mnOptStencil( 0 )
 {
     OpenGLVCLContextZone aContextZone;
 
@@ -221,53 +218,43 @@ bool ImplOpenGLTexture::InsertBuffer(int nX, int nY, int nWidth, int nHeight, in
     return true;
 }
 
-bool ImplOpenGLTexture::InitializeSlots(int nSlotSize)
+bool ImplOpenGLTexture::InitializeSlotMechanism(int nInitialSlotSize)
 {
     if (mpSlotReferences)
         return false;
 
-    mpSlotReferences.reset(new std::vector<int>(nSlotSize, 0));
-    mnFreeSlots = nSlotSize;
-
+    mpSlotReferences.reset(new std::vector<int>(nInitialSlotSize, 0));
     return true;
 }
 
-int ImplOpenGLTexture::FindFreeSlot()
-{
-    if (mnFreeSlots > 0 && mpSlotReferences)
-    {
-        for (size_t i = 0; i < mpSlotReferences->size(); i++)
-        {
-            if (mpSlotReferences->at(i) <= 0)
-            {
-                return i;
-            }
-        }
-    }
-    return -1;
-}
-
 void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber)
 {
     mnRefCount++;
     if (mpSlotReferences && nSlotNumber >= 0)
     {
-        if (mpSlotReferences->at(nSlotNumber) == 0)
-            mnFreeSlots--;
+        if (nSlotNumber >= int(mpSlotReferences->size()))
+            mpSlotReferences->resize(nSlotNumber + 1, 0);
+
         mpSlotReferences->at(nSlotNumber)++;
     }
 }
 
 void ImplOpenGLTexture::DecreaseRefCount(int nSlotNumber)
 {
-    mnRefCount--;
     if (mpSlotReferences && nSlotNumber >= 0)
     {
+        if (nSlotNumber >= int(mpSlotReferences->size()))
+            mpSlotReferences->resize(nSlotNumber, 0);
+
         mpSlotReferences->at(nSlotNumber)--;
-        if (mpSlotReferences->at(nSlotNumber) == 0)
-            mnFreeSlots++;
+
+        if (mpSlotReferences->at(nSlotNumber) == 0 && mFunctSlotDeallocateCallback)
+        {
+            mFunctSlotDeallocateCallback(nSlotNumber);
+        }
     }
 
+    mnRefCount--;
     if (mnRefCount <= 0)
         delete this;
 }
commit f65e77c965bb47d53c994d90b7fd0bf5009b343b
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Fri Apr 8 17:43:04 2016 +0900

    opengl: cleanup texture, const internal format
    
    - Move IncreaseRefCount and DecreaseRefCount to texture.cxx
    - Add a constInternalFormat constant for the internal texture
      format that we want to use.
    
    Change-Id: I1c1e1f784075dc0d20b033d3bd2e52d2ffbd8252

diff --git a/vcl/inc/opengl/texture.hxx b/vcl/inc/opengl/texture.hxx
index a220875..f54604a 100644
--- a/vcl/inc/opengl/texture.hxx
+++ b/vcl/inc/opengl/texture.hxx
@@ -50,30 +50,8 @@ public:
 
     bool InsertBuffer(int nX, int nY, int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData);
 
-    void IncreaseRefCount(int nSlotNumber)
-    {
-        mnRefCount++;
-        if (mpSlotReferences && nSlotNumber >= 0)
-        {
-            if (mpSlotReferences->at(nSlotNumber) == 0)
-                mnFreeSlots--;
-            mpSlotReferences->at(nSlotNumber)++;
-        }
-    }
-
-    void DecreaseRefCount(int nSlotNumber)
-    {
-        mnRefCount--;
-        if (mpSlotReferences && nSlotNumber >= 0)
-        {
-            mpSlotReferences->at(nSlotNumber)--;
-            if (mpSlotReferences->at(nSlotNumber) == 0)
-                mnFreeSlots++;
-        }
-
-        if (mnRefCount <= 0)
-            delete this;
-    }
+    void IncreaseRefCount(int nSlotNumber);
+    void DecreaseRefCount(int nSlotNumber);
 
     bool IsUnique()
     {
diff --git a/vcl/opengl/texture.cxx b/vcl/opengl/texture.cxx
index b9b12ad..6388a92 100644
--- a/vcl/opengl/texture.cxx
+++ b/vcl/opengl/texture.cxx
@@ -29,10 +29,17 @@
 #include "opengl/framebuffer.hxx"
 #include "opengl/texture.hxx"
 #include "opengl/zone.hxx"
+namespace
+{
+
+SAL_CONSTEXPR GLenum constInternalFormat = GL_RGBA8;
+
+} // end anonymous namespace
 
 // texture with allocated size
 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate ) :
     mnRefCount( 1 ),
+    mnTexture( 0 ),
     mnWidth( nWidth ),
     mnHeight( nHeight ),
     mnFilter( GL_NEAREST ),
@@ -55,7 +62,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate )
     CHECK_GL_ERROR();
     if( bAllocate )
     {
-        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
+        glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
         CHECK_GL_ERROR();
     }
     glBindTexture( GL_TEXTURE_2D, 0 );
@@ -91,7 +98,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nX, int nY, int nWidth, int nHeight )
     CHECK_GL_ERROR();
     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
     CHECK_GL_ERROR();
-    glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, nX, nY, nWidth, nHeight, 0 );
+    glCopyTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nX, nY, nWidth, nHeight, 0 );
     CHECK_GL_ERROR();
     glBindTexture( GL_TEXTURE_2D, 0 );
     CHECK_GL_ERROR();
@@ -111,11 +118,8 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int
 {
     OpenGLVCLContextZone aContextZone;
 
-    if( !mnTexture )
-    {
-        glGenTextures( 1, &mnTexture );
-        CHECK_GL_ERROR();
-    }
+    glGenTextures( 1, &mnTexture );
+    CHECK_GL_ERROR();
     glBindTexture( GL_TEXTURE_2D, mnTexture );
     CHECK_GL_ERROR();
     glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
@@ -128,7 +132,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int
     CHECK_GL_ERROR();
     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
     CHECK_GL_ERROR();
-    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, mnWidth, mnHeight, 0, nFormat, nType, pData );
+    glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, mnWidth, mnHeight, 0, nFormat, nType, pData );
     CHECK_GL_ERROR();
     glBindTexture( GL_TEXTURE_2D, 0 );
     CHECK_GL_ERROR();
@@ -243,6 +247,32 @@ int ImplOpenGLTexture::FindFreeSlot()
     return -1;
 }
 
+void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber)
+{
+    mnRefCount++;
+    if (mpSlotReferences && nSlotNumber >= 0)
+    {
+        if (mpSlotReferences->at(nSlotNumber) == 0)
+            mnFreeSlots--;
+        mpSlotReferences->at(nSlotNumber)++;
+    }
+}
+
+void ImplOpenGLTexture::DecreaseRefCount(int nSlotNumber)
+{
+    mnRefCount--;
+    if (mpSlotReferences && nSlotNumber >= 0)
+    {
+        mpSlotReferences->at(nSlotNumber)--;
+        if (mpSlotReferences->at(nSlotNumber) == 0)
+            mnFreeSlots++;
+    }
+
+    if (mnRefCount <= 0)
+        delete this;
+}
+
+
 OpenGLTexture::OpenGLTexture() :
     maRect( 0, 0, 0, 0 ),
     mpImpl(nullptr),
commit 094faaae6982472375420e57d6b9e34eefdbced8
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Fri Apr 8 17:41:21 2016 +0900

    opengl: fix wrong clipping when drawing text
    
    Change-Id: I41a182c5309586337032328dfe82b1c6715f0dc2

diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index 7561901..ef23328 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -148,7 +148,7 @@ public:
     void DrawAxialGradient( const Gradient& rGradient, const Rectangle& rRect );
     void DrawRadialGradient( const Gradient& rGradient, const Rectangle& rRect );
     void DeferredTextDraw(OpenGLTexture& rTexture, const SalColor nMaskColor, const SalTwoRect& rPosAry);
-    void FlushDeferredDrawing(bool bInDraw = false);
+    void FlushDeferredDrawing();
 
 public:
     // get the width of the device
@@ -166,6 +166,9 @@ public:
     /// Oddly not all operations obey the XOR option.
     enum XOROption { IGNORE_XOR, IMPLEMENT_XOR };
 
+    // initialize pre-draw state
+    void InitializePreDrawState(XOROption eOpt = IGNORE_XOR);
+
     // operations to do before painting
     void PreDraw(XOROption eOpt = IGNORE_XOR);
 
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index bf979d0..90c19d5 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -187,6 +187,13 @@ void OpenGLSalGraphicsImpl::DeInit()
 
 void OpenGLSalGraphicsImpl::PreDraw(XOROption eOpt)
 {
+    FlushDeferredDrawing();
+
+    InitializePreDrawState(eOpt);
+}
+
+void OpenGLSalGraphicsImpl::InitializePreDrawState(XOROption eOpt)
+{
     OpenGLZone::enter();
 
     mnDrawCount++;
@@ -206,8 +213,6 @@ void OpenGLSalGraphicsImpl::PreDraw(XOROption eOpt)
     glViewport( 0, 0, GetWidth(), GetHeight() );
     CHECK_GL_ERROR();
 
-    FlushDeferredDrawing(true);
-
     ImplInitClipRegion();
     CHECK_GL_ERROR();
 
@@ -268,6 +273,7 @@ void OpenGLSalGraphicsImpl::freeResources()
     {
         VCL_GL_INFO( "freeResources" );
         mpContext->makeCurrent();
+        FlushDeferredDrawing();
         mpContext->ReleaseFramebuffer( maOffscreenTex );
     }
     ReleaseContext();
@@ -357,12 +363,16 @@ const vcl::Region& OpenGLSalGraphicsImpl::getClipRegion() const
 
 bool OpenGLSalGraphicsImpl::setClipRegion( const vcl::Region& rClip )
 {
-    VCL_GL_INFO( "::setClipRegion " << rClip );
     if (maClipRegion == rClip)
-        return true;
+    {
+         VCL_GL_INFO("::setClipRegion (no change) " << rClip);
+         return true;
+    }
 
     FlushDeferredDrawing();
 
+    VCL_GL_INFO("::setClipRegion " << rClip);
+
     maClipRegion = rClip;
 
     mbUseStencil = false;
@@ -378,12 +388,16 @@ bool OpenGLSalGraphicsImpl::setClipRegion( const vcl::Region& rClip )
 // set the clip region to empty
 void OpenGLSalGraphicsImpl::ResetClipRegion()
 {
-    VCL_GL_INFO( "::ResetClipRegion" );
     if (maClipRegion.IsEmpty())
+    {
+        VCL_GL_INFO("::ResetClipRegion (no change) ");
         return;
+    }
 
     FlushDeferredDrawing();
 
+    VCL_GL_INFO("::ResetClipRegion");
+
     maClipRegion.SetEmpty();
     mbUseScissor = false;
     mbUseStencil = false;
@@ -1674,13 +1688,12 @@ void OpenGLSalGraphicsImpl::DeferredTextDraw(OpenGLTexture& rTexture, SalColor a
     mpAccumulatedTextures->insert(rTexture, aMaskColor, rPosAry);
 }
 
-void OpenGLSalGraphicsImpl::FlushDeferredDrawing(bool bIsInDraw)
+void OpenGLSalGraphicsImpl::FlushDeferredDrawing()
 {
     if (mpAccumulatedTextures->empty())
         return;
 
-    if (!bIsInDraw)
-        PreDraw();
+    InitializePreDrawState();
 
     VCL_GL_INFO("FlushDeferredDrawing");
 
@@ -1725,7 +1738,9 @@ void OpenGLSalGraphicsImpl::FlushDeferredDrawing(bool bIsInDraw)
 
     if( !UseProgram( "textureVertexShader", "maskFragmentShader" ) )
         return;
+
     mpProgram->SetBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
     for (auto& rPair : mpAccumulatedTextures->getAccumulatedTexturesMap())
     {
         OpenGLTexture& rTexture = rPair.second->maTexture;
@@ -1742,8 +1757,8 @@ void OpenGLSalGraphicsImpl::FlushDeferredDrawing(bool bIsInDraw)
     }
     mpProgram->Clean();
     mpAccumulatedTextures->clear();
-    if (!bIsInDraw)
-        PostDraw();
+
+    PostDraw();
 }
 
 void OpenGLSalGraphicsImpl::DrawLinearGradient( const Gradient& rGradient, const Rectangle& rRect )
commit 80d0b2916db81a7f47bb1d368677016bbb870df6
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Mon Apr 4 19:29:27 2016 +0900

    opengl: use packed texture atlas for glyph cache in win. backend
    
    Change-Id: I6a627699d49bad47213788877fa3947ad2ef83f4

diff --git a/vcl/inc/opengl/AccumulatedTextures.hxx b/vcl/inc/opengl/AccumulatedTextures.hxx
index 9ce170c..e74c065 100644
--- a/vcl/inc/opengl/AccumulatedTextures.hxx
+++ b/vcl/inc/opengl/AccumulatedTextures.hxx
@@ -11,6 +11,8 @@
 #ifndef INCLUDED_VCL_INC_OPENGL_ACCUMULATEDTEXTURES_H
 #define INCLUDED_VCL_INC_OPENGL_ACCUMULATEDTEXTURES_H
 
+#include <vcl/opengl/OpenGLHelper.hxx>
+
 #include <o3tl/make_unique.hxx>
 #include "opengl/texture.hxx"
 #include <memory>
@@ -34,10 +36,10 @@ struct AccumulatedTexturesEntry
         : maTexture(rTexture)
     {}
 
-    void insert(const SalColor& aColor, const SalTwoRect& r2Rect)
+    void insert(const OpenGLTexture& rTexture, const SalColor& aColor, const SalTwoRect& r2Rect)
     {
         TextureDrawParameters& aDrawParameters = maColorTextureDrawParametersMap[aColor];
-        maTexture.FillCoords<GL_TRIANGLES>(aDrawParameters.maTextureCoords, r2Rect, false);
+        rTexture.FillCoords<GL_TRIANGLES>(aDrawParameters.maTextureCoords, r2Rect, false);
 
         GLfloat nX1 = r2Rect.mnDestX;
         GLfloat nY1 = r2Rect.mnDestY;
@@ -86,19 +88,18 @@ public:
         maEntries.clear();
     }
 
-    void insert(const OpenGLTexture& rTexture, const SalColor& aColor, const SalTwoRect& r2Rect)
+    void insert(OpenGLTexture& rTexture, const SalColor& aColor, const SalTwoRect& r2Rect)
     {
         GLuint nTextureId = rTexture.Id();
 
-        auto iterator = maEntries.find(nTextureId);
-
-        if (iterator == maEntries.end())
+        if (maEntries.find(nTextureId) == maEntries.end())
         {
-            maEntries[nTextureId] = o3tl::make_unique<AccumulatedTexturesEntry>(rTexture);
+            OpenGLTexture aWholeTexture(rTexture.GetWholeTexture());
+            maEntries[nTextureId] = o3tl::make_unique<AccumulatedTexturesEntry>(aWholeTexture);
         }
 
         std::unique_ptr<AccumulatedTexturesEntry>& rEntry = maEntries[nTextureId];
-        rEntry->insert(aColor, r2Rect);
+        rEntry->insert(rTexture, aColor, r2Rect);
     }
 
     AccumulatedTexturesMap& getAccumulatedTexturesMap()
diff --git a/vcl/inc/opengl/texture.hxx b/vcl/inc/opengl/texture.hxx
index f48a1a1..a220875 100644
--- a/vcl/inc/opengl/texture.hxx
+++ b/vcl/inc/opengl/texture.hxx
@@ -96,7 +96,7 @@ private:
 
 public:
                     OpenGLTexture();
-                    OpenGLTexture(ImplOpenGLTexture* pImpl, Rectangle aRectangle, int nSlotNumber = 0);
+                    OpenGLTexture(ImplOpenGLTexture* pImpl, Rectangle aRectangle, int nSlotNumber);
 
                     OpenGLTexture( int nWidth, int nHeight, bool bAllocate = true );
                     OpenGLTexture( int nWidth, int nHeight, int nFormat, int nType, void const * pData );
@@ -113,13 +113,15 @@ public:
 
     void            GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool bInverted=false ) const;
     void            GetWholeCoord( GLfloat* pCoord ) const;
-
+    OpenGLTexture   GetWholeTexture();
     void            Bind();
     void            Unbind();
     void            Read( GLenum nFormat, GLenum nType, sal_uInt8* pData );
     GLuint          AddStencil();
     GLuint          StencilId() const;
 
+    bool            CopyData(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData);
+
     void            SaveToFile(const OUString& rFileName);
 
     GLenum          GetFilter() const;
diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index 0fb2941..7561901 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -147,7 +147,7 @@ public:
     void DrawLinearGradient( const Gradient& rGradient, const Rectangle& rRect );
     void DrawAxialGradient( const Gradient& rGradient, const Rectangle& rRect );
     void DrawRadialGradient( const Gradient& rGradient, const Rectangle& rRect );
-    void DeferredTextDraw(const OpenGLTexture& rTexture, const SalColor nMaskColor, const SalTwoRect& rPosAry);
+    void DeferredTextDraw(OpenGLTexture& rTexture, const SalColor nMaskColor, const SalTwoRect& rPosAry);
     void FlushDeferredDrawing(bool bInDraw = false);
 
 public:
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index ffd83ab..8866dcf 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -172,11 +172,16 @@ public:
 
     SalTwoRect getTwoRect() { return maRects; }
 
+    Size getBitmapSize() { return Size(maRects.mnSrcWidth, maRects.mnSrcHeight); }
+
     /// Reset the DC with the defined color.
     void fill(sal_uInt32 color);
 
     /// Obtain the texture; the caller must delete it after use.
     OpenGLTexture* getTexture();
+
+    /// Copy bitmap data to the texture. Texutre must be initialized and the correct size to hold the bitmap.
+    bool copyToTexture(OpenGLTexture& aTexture);
 };
 
 class WinSalGraphics : public SalGraphics
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index bfa48b4..bf979d0 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -1669,7 +1669,7 @@ void OpenGLSalGraphicsImpl::DrawMask( OpenGLTexture& rMask, SalColor nMaskColor,
     mpProgram->Clean();
 }
 
-void OpenGLSalGraphicsImpl::DeferredTextDraw(const OpenGLTexture& rTexture, SalColor aMaskColor, const SalTwoRect& rPosAry)
+void OpenGLSalGraphicsImpl::DeferredTextDraw(OpenGLTexture& rTexture, SalColor aMaskColor, const SalTwoRect& rPosAry)
 {
     mpAccumulatedTextures->insert(rTexture, aMaskColor, rPosAry);
 }
@@ -1682,6 +1682,8 @@ void OpenGLSalGraphicsImpl::FlushDeferredDrawing(bool bIsInDraw)
     if (!bIsInDraw)
         PreDraw();
 
+    VCL_GL_INFO("FlushDeferredDrawing");
+
     OpenGLZone aZone;
 
 #if 0 // Draw a background rect under text for debugging - same color shows text from the same texture
diff --git a/vcl/opengl/texture.cxx b/vcl/opengl/texture.cxx
index fd6d11f..b9b12ad 100644
--- a/vcl/opengl/texture.cxx
+++ b/vcl/opengl/texture.cxx
@@ -373,26 +373,31 @@ template <>
 void OpenGLTexture::FillCoords<GL_TRIANGLES>(std::vector<GLfloat>& aCoord, const SalTwoRect& rPosAry, bool bInverted) const
 {
     VCL_GL_INFO("Add coord " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
+    VCL_GL_INFO("   With 2Rect Src  [" << rPosAry.mnSrcX << ", " << rPosAry.mnSrcY << "] wh (" << rPosAry.mnSrcWidth << ", " << rPosAry.mnSrcHeight << ")");
+    VCL_GL_INFO("   With 2Rect Dest [" << rPosAry.mnDestX << ", " << rPosAry.mnDestY << "] wh (" << rPosAry.mnDestWidth << ", " << rPosAry.mnDestHeight << ")");
 
     GLfloat x1 = 0.0f;
     GLfloat x2 = 0.0f;
     GLfloat y1 = 0.0f;
     GLfloat y2 = 0.0f;
 
+    double fTextureWidth(mpImpl->mnWidth);
+    double fTextureHeight(mpImpl->mnHeight);
+
     if (mpImpl)
     {
-        x1 = (maRect.Left() + rPosAry.mnSrcX) / (double) mpImpl->mnWidth;
-        x2 = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / (double) mpImpl->mnWidth;
+        x1 = (maRect.Left() + rPosAry.mnSrcX) / fTextureWidth;
+        x2 = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / fTextureWidth;
 
         if (bInverted)
         {
-            y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / (double) mpImpl->mnHeight;
-            y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / (double) mpImpl->mnHeight;
+            y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
+            y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
         }
         else
         {
-            y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / (double) mpImpl->mnHeight;
-            y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / (double) mpImpl->mnHeight;
+            y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
+            y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
         }
     }
 
@@ -433,6 +438,11 @@ void OpenGLTexture::GetWholeCoord( GLfloat* pCoord ) const
     }
 }
 
+OpenGLTexture OpenGLTexture::GetWholeTexture()
+{
+    return OpenGLTexture(mpImpl, Rectangle(Point(0, 0), Size(mpImpl->mnWidth, mpImpl->mnHeight)), -1);
+}
+
 GLenum OpenGLTexture::GetFilter() const
 {
     if( mpImpl )
@@ -440,6 +450,17 @@ GLenum OpenGLTexture::GetFilter() const
     return GL_NEAREST;
 }
 
+bool OpenGLTexture::CopyData(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
+{
+    if (!pData || mpImpl == nullptr)
+        return false;
+
+    int nX = maRect.Left();
+    int nY = maRect.Top();
+
+    return mpImpl->InsertBuffer(nX, nY, nWidth, nHeight, nFormat, nType, pData);
+}
+
 void OpenGLTexture::SetFilter( GLenum nFilter )
 {
     if( mpImpl )
diff --git a/vcl/win/gdi/salgdi.cxx b/vcl/win/gdi/salgdi.cxx
index 531b71a..7a27878 100644
--- a/vcl/win/gdi/salgdi.cxx
+++ b/vcl/win/gdi/salgdi.cxx
@@ -591,6 +591,14 @@ OpenGLTexture* OpenGLCompatibleDC::getTexture()
     return new OpenGLTexture(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, reinterpret_cast<sal_uInt8*>(mpData));
 }
 
+bool OpenGLCompatibleDC::copyToTexture(OpenGLTexture& aTexture)
+{
+    if (!mpImpl)
+        return false;
+
+    return aTexture.CopyData(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, reinterpret_cast<sal_uInt8*>(mpData));
+}
+
 WinSalGraphics::WinSalGraphics(WinSalGraphics::Type eType, bool bScreen, HWND hWnd, SalGeometryProvider *pProvider):
     mhLocalDC(0),
     mbPrinter(eType == WinSalGraphics::PRINTER),
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index 293bfa2..f370ff5 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -26,6 +26,8 @@
 
 #include <opengl/texture.hxx>
 #include <opengl/win/gdiimpl.hxx>
+#include "opengl/PackedTextureAtlas.hxx"
+
 #include <vcl/opengl/OpenGLHelper.hxx>
 #include <win/salgdi.h>
 #include <win/saldata.hxx>
@@ -74,13 +76,11 @@ const int GLYPH_SPACE_RATIO = 8;
 const int GLYPH_OFFSET_RATIO = GLYPH_SPACE_RATIO * 2;
 }
 
-struct OpenGLGlyphCacheChunk
+struct OpenGLGlyphDrawElement
 {
-    int mnFirstGlyph;           // Must be int to handle non-BMP code points when mbRealGlyphIndices is false
-    int mnGlyphCount;
-    std::vector<Rectangle> maLocation;
-    std::vector<int> maLeftOverhangs;
-    std::shared_ptr<OpenGLTexture> mpTexture;
+    Rectangle maLocation;
+    int maLeftOverhangs;
+    OpenGLTexture maTexture;
     int mnBaselineOffset;
     int mnHeight;
     bool mbVertical;
@@ -97,6 +97,41 @@ struct OpenGLGlyphCacheChunk
     }
 };
 
+class GlyphCache
+{
+private:
+    static PackedTextureAtlasManager sPackedTextureAtlas;
+    std::unordered_map<int, OpenGLGlyphDrawElement> maOpenGLTextureCache;
+
+public:
+    GlyphCache()
+    {}
+
+    void ReserveTextureSpace(OpenGLGlyphDrawElement& rElement, int nWidth, int nHeight)
+    {
+        rElement.maTexture = sPackedTextureAtlas.Reserve(nWidth, nHeight);
+    }
+
+    void PutDrawElementInCache(const OpenGLGlyphDrawElement& rElement, int nGlyphIndex)
+    {
+        assert(!IsGlyphCached(nGlyphIndex));
+        maOpenGLTextureCache[nGlyphIndex] = OpenGLGlyphDrawElement(rElement);
+    }
+
+    OpenGLGlyphDrawElement& GetDrawElement(int nGlyphIndex)
+    {
+        assert(IsGlyphCached(nGlyphIndex));
+        return maOpenGLTextureCache[nGlyphIndex];
+    }
+
+    bool IsGlyphCached(int nGlyphIndex)
+    {
+        return maOpenGLTextureCache.find(nGlyphIndex) != maOpenGLTextureCache.end();
+    }
+};
+
+PackedTextureAtlasManager GlyphCache::sPackedTextureAtlas(2048, 2048);
+
 // win32 specific physical font instance
 class WinFontInstance : public LogicalFontInstance
 {
@@ -122,7 +157,6 @@ public:
                             { return maScriptCache; }
 private:
     mutable SCRIPT_CACHE    maScriptCache;
-    std::vector<OpenGLGlyphCacheChunk> maOpenGLGlyphCache;
 
 public:
     int                     GetCachedGlyphWidth( int nCharCode ) const;
@@ -136,9 +170,15 @@ public:
     demo_atlas_t*             mpGLyphyAtlas;
     demo_font_t*              mpGLyphyFont;
 
-    bool                    GlyphIsCached(int nGlyphIndex) const;
-    bool                    AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
-    const OpenGLGlyphCacheChunk&  GetCachedGlyphChunkFor(int nGlyphIndex) const;
+private:
+    GlyphCache maGlyphCache;
+public:
+    bool CacheGlyphToAtlas(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
+
+    GlyphCache& GetGlyphCache()
+    {
+        return maGlyphCache;
+    }
 
 private:
     IntMap                  maWidthMap;
@@ -246,96 +286,6 @@ private:
     HDC               mhDC;
 };
 
-
-#ifdef SAL_LOG_INFO
-
-namespace {
-
-char ColorFor(COLORREF aColor)
-{
-    if (aColor == RGB(0xFF, 0xFF, 0xFF))
-        return ' ';
-    else if (aColor == RGB(0x00, 0x00, 0x00))
-        return 'X';
-
-    return '0' + (10*(GetRValue(aColor) + GetGValue(aColor) + GetBValue(aColor))) / (0xFF*3);
-}
-
-void DumpGlyphBitmap(HDC hDC, const OpenGLGlyphCacheChunk& rChunk)
-{
-    HBITMAP hBitmap = static_cast<HBITMAP>(GetCurrentObject(hDC, OBJ_BITMAP));
-    if (hBitmap == NULL)
-    {
-        SAL_WARN("vcl.gdi", "GetCurrentObject failed: " << WindowsErrorString(GetLastError()));
-        return;
-    }
-
-    BITMAP aBitmap;
-    if (!GetObjectW(hBitmap, sizeof(aBitmap), &aBitmap))
-    {
-        SAL_WARN("vcl.gdi", "GetObjectW failed: " << WindowsErrorString(GetLastError()));
-        return;
-    }
-
-    SAL_INFO("vcl.gdi.opengl", "Bitmap " << hBitmap << ": " << aBitmap.bmWidth << "x" << aBitmap.bmHeight << ":");
-
-    std::ostringstream sLine("\n", std::ios_base::ate);
-    std::ostringstream sScale;
-    long nPrintWidth = std::min(125l, aBitmap.bmWidth);
-    for (long y = 0; y < aBitmap.bmHeight; y++)
-    {
-        if (y == rChunk.mnBaselineOffset + rChunk.getExtraOffset())
-            sLine << "--------------------------\n";
-        long n = 0;
-        for (long x = 0; x < nPrintWidth; x++)
-        {
-            // delimit.
-            for (size_t i = 0; i < rChunk.maLocation.size(); ++i)
-            {
-                if (x == rChunk.maLocation[i].Right())
-                {
-                    n = 0;
-                    sLine << '|';
-                    if (y == 0)
-                        sScale << ' ';
-                    break;
-                }
-            }
-            sLine << ColorFor(GetPixel(hDC, x, y));
-            if (y == 0)
-                sScale << (n++ % 10);
-        }
-        sLine << "\n";
-    }
-    sLine << sScale.str();
-    SAL_INFO("vcl.gdi.opengl", sLine.str());
-}
-
-} // anonymous namespace
-
-#endif // SAL_LOG_INFO
-
-template< typename charT, typename traits >
-inline std::basic_ostream<charT, traits> & operator <<(
-    std::basic_ostream<charT, traits> & stream, const std::vector<OpenGLGlyphCacheChunk>& rCache )
-{
-    stream << "{";
-    for (auto i = rCache.cbegin(); i != rCache.cend(); ++i)
-    {
-        stream << "[" << i->mnFirstGlyph;
-        if (i->mnGlyphCount > 1)
-            stream << ".." << (i->mnFirstGlyph + i->mnGlyphCount - 1);
-        stream << "]";
-        if (i+1 != rCache.cend())
-        {
-            stream << ",";
-            assert(i->mnFirstGlyph + i->mnGlyphCount <= (i+1)->mnFirstGlyph);
-        }
-    }
-
-    return stream << "}";
-}
-
 inline void WinFontInstance::CacheGlyphWidth( int nCharCode, int nCharWidth )
 {
     maWidthMap[ nCharCode ] = nCharWidth;
@@ -349,60 +299,16 @@ inline int WinFontInstance::GetCachedGlyphWidth( int nCharCode ) const
     return it->second;
 }
 
-bool WinFontInstance::GlyphIsCached(int nGlyphIndex) const
+bool WinFontInstance::CacheGlyphToAtlas(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
 {
     if (nGlyphIndex == DROPPED_OUTGLYPH)
         return true;
 
-    for (size_t i = 0; i < maOpenGLGlyphCache.size(); i++)
-        if (nGlyphIndex >= maOpenGLGlyphCache[i].mnFirstGlyph &&
-            nGlyphIndex < maOpenGLGlyphCache[i].mnFirstGlyph + maOpenGLGlyphCache[i].mnGlyphCount)
-            return true;
-
-    return false;
-}
-
-bool WinFontInstance::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
-{
-    const int DEFAULT_CHUNK_SIZE = 40;
-
-    if (nGlyphIndex == DROPPED_OUTGLYPH)
-        return true;
+    OpenGLGlyphDrawElement aElement;
+    aElement.mbRealGlyphIndices = bRealGlyphIndices;
 
-    SAL_INFO("vcl.gdi.opengl", "this=" << this << " " << nGlyphIndex << " old: " << maOpenGLGlyphCache);
-
-    auto n = maOpenGLGlyphCache.begin();
-    while (n != maOpenGLGlyphCache.end() &&
-           nGlyphIndex > n->mnFirstGlyph)
-        ++n;
-    assert(n == maOpenGLGlyphCache.end() || nGlyphIndex < n->mnFirstGlyph);
-
-    int nCount = DEFAULT_CHUNK_SIZE;
-    if (n != maOpenGLGlyphCache.end() && nGlyphIndex + nCount >= n->mnFirstGlyph)
-        nCount = n->mnFirstGlyph - nGlyphIndex;
-
-    if (nCount < DEFAULT_CHUNK_SIZE)
-    {
-        if (n == maOpenGLGlyphCache.begin())
-        {
-            nGlyphIndex = std::max(0, n->mnFirstGlyph - DEFAULT_CHUNK_SIZE);
-        }
-        else
-        {
-            nGlyphIndex = std::max(n[-1].mnFirstGlyph + n[-1].mnGlyphCount,
-                                   n->mnFirstGlyph - DEFAULT_CHUNK_SIZE);
-        }
-        nCount = n->mnFirstGlyph - nGlyphIndex;
-    }
-
-    OpenGLGlyphCacheChunk aChunk;
-    aChunk.mnFirstGlyph = nGlyphIndex;
-    aChunk.mnGlyphCount = nCount;
-    aChunk.mbRealGlyphIndices = bRealGlyphIndices;
-
-    std::vector<uint32_t> aCodePointsOrGlyphIndices(nCount);
-    for (int i = 0; i < nCount; i++)
-        aCodePointsOrGlyphIndices[i] = nGlyphIndex + i;
+    std::vector<uint32_t> aCodePointsOrGlyphIndices(1);
+    aCodePointsOrGlyphIndices[0] = nGlyphIndex;
 
     HDC hDC = CreateCompatibleDC(rLayout.mhDC);
     if (hDC == NULL)
@@ -441,7 +347,7 @@ bool WinFontInstance::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
             return false;
         }
     }
-    std::vector<WORD> aGlyphIndices(nCount);
+    std::vector<WORD> aGlyphIndices(1);
     // Fetch the ink boxes and calculate the size of the atlas.
     if (!bRealGlyphIndices)
     {
@@ -453,69 +359,60 @@ bool WinFontInstance::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
     }
     else
     {
-        for (int i = 0; i < nCount; i++)
-            aGlyphIndices[i] = aCodePointsOrGlyphIndices[i];
+        aGlyphIndices[0] = aCodePointsOrGlyphIndices[0];
     }
     Rectangle bounds(0, 0, 0, 0);
-    auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + nCount);
+    auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + 1);
     for (auto &box : aInkBoxes)
         bounds.Union(box + Point(bounds.Right(), 0));
 
     // bounds.Top() is the offset from the baseline at (0,0) to the top of the
     // inkbox.
-    aChunk.mnBaselineOffset = -bounds.Top();
-    aChunk.mnHeight = bounds.getHeight();
-    aChunk.mbVertical = false;
-
-    aChunk.maLeftOverhangs.resize(nCount);
-    aChunk.maLocation.resize(nCount);
+    aElement.mnBaselineOffset = -bounds.Top();
+    aElement.mnHeight = bounds.getHeight();
+    aElement.mbVertical = false;
 
     // Try hard to avoid overlap as we want to be able to use
     // individual rectangles for each glyph. The ABC widths don't
     // take anti-aliasing into consideration. Let's hope that leaving
     // "extra" space between glyphs will help.
-    std::vector<float> aGlyphAdv(nCount);   // offsets between glyphs
-    std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(nCount, DWRITE_GLYPH_OFFSET{0.0f,0.0f});
-    std::vector<int> aEnds(nCount); // end of each glyph box
+    std::vector<float> aGlyphAdv(1);   // offsets between glyphs
+    std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(1, DWRITE_GLYPH_OFFSET{0.0f, 0.0f});
+    std::vector<int> aEnds(1); // end of each glyph box
     float totWidth = 0;
-    for (int i = 0; i < nCount; ++i)
     {
-        int overhang = aInkBoxes[i].Left();
-        int blackWidth = aInkBoxes[i].getWidth(); // width of non-AA pixels
-        aChunk.maLeftOverhangs[i] = overhang;
+        int overhang = aInkBoxes[0].Left();
+        int blackWidth = aInkBoxes[0].getWidth(); // width of non-AA pixels
+        aElement.maLeftOverhangs = overhang;
 
-        aGlyphAdv[i] = blackWidth + aChunk.getExtraSpace();
-        aGlyphOffset[i].advanceOffset = -overhang;
+        aGlyphAdv[0] = blackWidth + aElement.getExtraSpace();
+        aGlyphOffset[0].advanceOffset = -overhang;
 
-        totWidth += aGlyphAdv[i];
-        aEnds[i] = totWidth;
+        totWidth += aGlyphAdv[0];
+        aEnds[0] = totWidth;
     }
-
     // Leave extra space also at top and bottom
-    int nBitmapWidth = totWidth,
-        nBitmapHeight = bounds.getHeight() + aChunk.getExtraSpace();
+    int nBitmapWidth = totWidth;
+    int nBitmapHeight = bounds.getHeight() + aElement.getExtraSpace();
 
-    aChunk.maLocation.resize(nCount);
     UINT nPos = 0;
-    for (int i = 0; i < nCount; i++)
+
+    // FIXME: really I don't get why 'vertical' makes any difference [!] what does it mean !?
+    if (aElement.mbVertical)
     {
-        // FIXME: really I don't get why 'vertical' makes any difference [!] what does it mean !?
-        if (aChunk.mbVertical)
-        {
-            aChunk.maLocation[i].Left() = 0;
-            aChunk.maLocation[i].Right() = nBitmapWidth;
-            aChunk.maLocation[i].Top() = nPos;
-            aChunk.maLocation[i].Bottom() = nPos + aGlyphAdv[i] + aChunk.maLeftOverhangs[i];
-        }
-        else
-        {
-            aChunk.maLocation[i].Left() = nPos;
-            aChunk.maLocation[i].Right() = aEnds[i];
-            aChunk.maLocation[i].Top() = 0;
-            aChunk.maLocation[i].Bottom() = bounds.getHeight() + aChunk.getExtraSpace();
-        }
-        nPos = aEnds[i];
+        aElement.maLocation.Left() = 0;
+        aElement.maLocation.Right() = nBitmapWidth;
+        aElement.maLocation.Top() = nPos;
+        aElement.maLocation.Bottom() = nPos + aGlyphAdv[0] + aElement.maLeftOverhangs;
+    }
+    else
+    {
+        aElement.maLocation.Left() = nPos;
+        aElement.maLocation.Right() = aEnds[0];
+        aElement.maLocation.Top() = 0;
+        aElement.maLocation.Bottom() = bounds.getHeight() + aElement.getExtraSpace();
     }
+    nPos = aEnds[0];
 
     OpenGLCompatibleDC aDC(rGraphics, 0, 0, nBitmapWidth, nBitmapHeight);
 
@@ -536,11 +433,15 @@ bool WinFontInstance::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
         return false;
     }
 
-    D2D1_POINT_2F baseline = { aChunk.getExtraOffset(), aChunk.getExtraOffset() + aChunk.mnBaselineOffset };
+    D2D1_POINT_2F baseline = {
+        aElement.getExtraOffset(),
+        aElement.getExtraOffset() + aElement.mnBaselineOffset
+    };
+
     DWRITE_GLYPH_RUN glyphs = {
         pTxt->GetFontFace(),
         pTxt->GetEmHeight(),
-        nCount,
+        1,
         aGlyphIndices.data(),
         aGlyphAdv.data(),
         aGlyphOffset.data(),
@@ -550,11 +451,11 @@ bool WinFontInstance::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
 
     pRT->BeginDraw();
     pRT->DrawGlyphRun(baseline, &glyphs, pBrush);
-    HRESULT hr = pRT->EndDraw();
+    HRESULT hResult = pRT->EndDraw();
 
     pBrush->Release();
 
-    switch (hr)
+    switch (hResult)
     {
     case S_OK:
         break;
@@ -571,25 +472,14 @@ bool WinFontInstance::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
 
     pTxt->ReleaseFont();
 
-    aChunk.mpTexture = std::unique_ptr<OpenGLTexture>(aDC.getTexture());
-
-    maOpenGLGlyphCache.insert(n, aChunk);
+    maGlyphCache.ReserveTextureSpace(aElement, nBitmapWidth, nBitmapHeight);
+    aDC.copyToTexture(aElement.maTexture);
+    maGlyphCache.PutDrawElementInCache(aElement, nGlyphIndex);
 
     SelectFont(aDC.getCompatibleHDC(), hOrigFont);
     if (hNonAntialiasedFont != NULL)
         DeleteObject(hNonAntialiasedFont);
 
-#ifdef SAL_LOG_INFO
-    SAL_INFO("vcl.gdi.opengl", "this=" << this << " now: " << maOpenGLGlyphCache);
-    DumpGlyphBitmap(aDC.getCompatibleHDC(), aChunk);
-    {
-        std::ostringstream sLine;
-        for (int i = 0; i < nCount; i++)
-            sLine << aGlyphAdv[i] << ":" << aChunk.maLeftOverhangs[i] << " ";
-        SAL_INFO("vcl.gdi.opengl", "DX:offset : " << sLine.str());
-    }
-#endif
-
     return true;
 }
 
@@ -1288,16 +1178,6 @@ void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
         mnWidth = mnBaseAdv = 0;
 }
 
-const OpenGLGlyphCacheChunk& WinFontInstance::GetCachedGlyphChunkFor(int nGlyphIndex) const
-{
-    auto i = maOpenGLGlyphCache.cbegin();
-    while (i != maOpenGLGlyphCache.cend() && nGlyphIndex >= i->mnFirstGlyph + i->mnGlyphCount)
-        ++i;
-    assert(i != maOpenGLGlyphCache.cend());
-    assert(nGlyphIndex >= i->mnFirstGlyph && nGlyphIndex < i->mnFirstGlyph + i->mnGlyphCount);
-    return *i;
-}
-
 void WinFontInstance::setupGLyphy(HDC hDC)
 {
     if (mbGLyphySetupCalled)
@@ -1531,11 +1411,9 @@ bool SimpleWinLayout::CacheGlyphs(SalGraphics& rGraphics) const
             nCodePoint = mpOutGlyphs[i];
         }
 
-        if (mrWinFontEntry.GlyphIsCached(nCodePoint))
-            continue;
+        if (!mrWinFontEntry.GetGlyphCache().IsGlyphCached(nCodePoint))
+            assert(mrWinFontEntry.CacheGlyphToAtlas(false, nCodePoint, *this, rGraphics));
 
-        if (!mrWinFontEntry.AddChunkOfGlyphs(false, nCodePoint, *this, rGraphics))
-            return false;
     }
 
     return true;
@@ -1577,18 +1455,16 @@ bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
             nCodePoint = mpOutGlyphs[i];
         }
 
-        assert(mrWinFontEntry.GlyphIsCached(nCodePoint));
+        OpenGLGlyphDrawElement& rElement(mrWinFontEntry.GetGlyphCache().GetDrawElement(nCodePoint));
+        OpenGLTexture& rTexture = rElement.maTexture;
 
-        const OpenGLGlyphCacheChunk& rChunk = mrWinFontEntry.GetCachedGlyphChunkFor(nCodePoint);
-        const int n = nCodePoint - rChunk.mnFirstGlyph;
+        SalTwoRect a2Rects(0, 0,
+                           rTexture.GetWidth(), rTexture.GetHeight(),
+                           nAdvance + aPos.X() - rElement.getExtraOffset() + rElement.maLeftOverhangs,
+                           aPos.Y() - rElement.mnBaselineOffset - rElement.getExtraOffset(),
+                           rTexture.GetWidth(), rTexture.GetHeight());
 
-        SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
-                           rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
-                           nAdvance + aPos.X() - rChunk.getExtraOffset() + rChunk.maLeftOverhangs[n],
-                           aPos.Y() - rChunk.mnBaselineOffset - rChunk.getExtraOffset(),
-                           rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-
-        pImpl->DeferredTextDraw(*rChunk.mpTexture, salColor, a2Rects);
+        pImpl->DeferredTextDraw(rTexture, salColor, a2Rects);
 
         nAdvance += mpGlyphAdvances[i];
     }
@@ -2801,11 +2677,9 @@ bool UniscribeLayout::CacheGlyphs(SalGraphics& rGraphics) const
     {
         for (int i = 0; i < mnGlyphCount; i++)
         {
-            if (mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]))
-                continue;
-
-            if (!mrWinFontEntry.AddChunkOfGlyphs(true, mpOutGlyphs[i], *this, rGraphics))
-                return false;
+            int nCodePoint = mpOutGlyphs[i];
+            if (!mrWinFontEntry.GetGlyphCache().IsGlyphCached(nCodePoint))
+                assert(mrWinFontEntry.CacheGlyphToAtlas(true, nCodePoint, *this, rGraphics));
         }
     }
 
@@ -3093,28 +2967,30 @@ bool UniscribeLayout::DrawCachedGlyphsUsingTextures(SalGraphics& rGraphics) cons
             if (mpOutGlyphs[i] == DROPPED_OUTGLYPH)
                 continue;
 
-            assert(mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]));
-
-            const OpenGLGlyphCacheChunk& rChunk = mrWinFontEntry.GetCachedGlyphChunkFor(mpOutGlyphs[i]);
-            const int n = mpOutGlyphs[i] - rChunk.mnFirstGlyph;
+            OpenGLGlyphDrawElement& rElement = mrWinFontEntry.GetGlyphCache().GetDrawElement(mpOutGlyphs[i]);
+            OpenGLTexture& rTexture = rElement.maTexture;
 
-            if (rChunk.mbVertical)
+            if (rElement.mbVertical)
             {
-                SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
-                                   rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
-                                   aPos.X() + rChunk.maLeftOverhangs[n], nAdvance + aPos.Y(),
-                                   rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-                pImpl->DeferredTextDraw(*rChunk.mpTexture, salColor, a2Rects);
+                SalTwoRect a2Rects(0, 0,
+                                   rTexture.GetWidth(), rTexture.GetHeight(),
+                                   aPos.X() + rElement.maLeftOverhangs,
+                                   nAdvance + aPos.Y(),
+                                   rTexture.GetWidth(), rTexture.GetHeight());
+
+                pImpl->DeferredTextDraw(rTexture, salColor, a2Rects);
             }
             else
             {
-                SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
-                                   rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
-                                   nAdvance + aPos.X() + mpGlyphOffsets[i].du - rChunk.getExtraOffset() + rChunk.maLeftOverhangs[n],
-                                   aPos.Y() + mpGlyphOffsets[i].dv - rChunk.mnBaselineOffset - rChunk.getExtraOffset(),
-                                   rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-                pImpl->DeferredTextDraw(*rChunk.mpTexture, salColor, a2Rects);
+                SalTwoRect a2Rects(0, 0,
+                                   rTexture.GetWidth(), rTexture.GetHeight(),
+                                   nAdvance + aPos.X() + mpGlyphOffsets[i].du - rElement.getExtraOffset() + rElement.maLeftOverhangs,
+                                   aPos.Y() + mpGlyphOffsets[i].dv - rElement.mnBaselineOffset - rElement.getExtraOffset(),
+                                   rTexture.GetWidth(), rTexture.GetHeight());
+
+                pImpl->DeferredTextDraw(rTexture, salColor, a2Rects);
             }
+
             nAdvance += pGlyphWidths[i];
         }
     }
commit 40e9ed91bd8bbfecfc3832d73a81741d0aa97d3a
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Mon Apr 4 19:27:12 2016 +0900

    opengl: texture atlas impl. to efficiently packs textures
    
    Change-Id: I66b3eddadb172da26aa1a62f2a795895769db93b

diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index a3ec5e9..11c4dae 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -620,6 +620,7 @@ else
 	vcl/opengl/program \
 	vcl/opengl/texture \
 	vcl/opengl/FixedTextureAtlas \
+	vcl/opengl/PackedTextureAtlas \
     vcl/source/opengl/OpenGLContext \
     vcl/source/opengl/OpenGLHelper \
     vcl/source/window/openglwin \
diff --git a/vcl/inc/opengl/FixedTextureAtlas.hxx b/vcl/inc/opengl/FixedTextureAtlas.hxx
index 5b22b619..8d104a7 100644
--- a/vcl/inc/opengl/FixedTextureAtlas.hxx
+++ b/vcl/inc/opengl/FixedTextureAtlas.hxx
@@ -28,6 +28,7 @@ public:
     FixedTextureAtlasManager(int nWidthFactor, int nHeightFactor, int nTextureSize);
     ~FixedTextureAtlasManager();
     OpenGLTexture InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData);
+    OpenGLTexture Reserve(int nWidth, int nHeight);
 
     int GetSubtextureSize()
     {
diff --git a/vcl/inc/opengl/PackedTextureAtlas.hxx b/vcl/inc/opengl/PackedTextureAtlas.hxx
new file mode 100644
index 0000000..17501f3
--- /dev/null
+++ b/vcl/inc/opengl/PackedTextureAtlas.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#ifndef INCLUDED_VCL_INC_OPENGL_PACKEDTEXTUREATLAS_HXX
+#define INCLUDED_VCL_INC_OPENGL_PACKEDTEXTUREATLAS_HXX
+
+#include "opengl/texture.hxx"
+
+struct PackedTexture;
+
+/**
+ * Pack texutres into one texutre atlas.
+ *
+ * This is based on algorithm described in [1] and is an
+ * addaptation of "texture atlas generator" from [2].
+ *
+ * [1]: http://www.blackpawn.com/texts/lightmaps/
+ * [2]: https://github.com/lukaszdk/texture-atlas-generator
+ *
+ */
+class VCL_PLUGIN_PUBLIC PackedTextureAtlasManager
+{
+    std::vector<std::unique_ptr<PackedTexture>> maPackedTextures;
+
+    int mnTextureWidth;
+    int mnTextureHeight;
+
+    void CreateNewTexture();
+
+public:
+    PackedTextureAtlasManager(int nTextureWidth, int nTextureHeight);
+    ~PackedTextureAtlasManager();
+    OpenGLTexture InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData);
+    OpenGLTexture Reserve(int nWidth, int nHeight);
+};
+
+#endif // INCLUDED_VCL_INC_OPENGL_PACKEDTEXTUREATLAS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/FixedTextureAtlas.cxx b/vcl/opengl/FixedTextureAtlas.cxx
index 80c1cfe..1ed8311 100644
--- a/vcl/opengl/FixedTextureAtlas.cxx
+++ b/vcl/opengl/FixedTextureAtlas.cxx
@@ -42,7 +42,7 @@ void FixedTextureAtlasManager::CreateNewTexture()
     mpTextures.back()->InitializeSlots(mWidthFactor * mHeightFactor);
 }
 
-OpenGLTexture FixedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
+OpenGLTexture FixedTextureAtlasManager::Reserve(int nWidth, int nHeight)
 {
     ImplOpenGLTexture* pTexture = nullptr;
 
@@ -71,14 +71,18 @@ OpenGLTexture FixedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, in
 
     Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
 
-    // If available, copy the image data to the texture
-    if (pData)
-    {
-        if (!pTexture->InsertBuffer(nX, nY, nWidth, nHeight, nFormat, nType, pData))
-            return OpenGLTexture();
-    }
-
     return OpenGLTexture(pTexture, aRectangle, nSlot);
 }
 
+OpenGLTexture FixedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
+{
+    OpenGLTexture aTexture = Reserve(nWidth, nHeight);
+    if (pData == nullptr)
+        return aTexture;
+
+    aTexture.CopyData(nWidth, nHeight, nFormat, nType, pData);
+
+    return aTexture;
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/PackedTextureAtlas.cxx b/vcl/opengl/PackedTextureAtlas.cxx
new file mode 100644
index 0000000..60fa1e9
--- /dev/null
+++ b/vcl/opengl/PackedTextureAtlas.cxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <sal/config.h>
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include "opengl/framebuffer.hxx"
+#include "opengl/texture.hxx"
+
+#include "opengl/PackedTextureAtlas.hxx"
+
+struct Node
+{
+    Rectangle mRectangle;
+    std::unique_ptr<Node> mLeftNode;
+    std::unique_ptr<Node> mRightNode;
+    bool mOccupied;
+
+    Node(Rectangle& aRectangle);
+
+    bool isLeaf();
+    Node* insert(int nWidth, int nHeight, int nPadding);
+};
+
+Node::Node(Rectangle& aRectangle)
+    : mRectangle(aRectangle)
+    , mLeftNode()
+    , mRightNode()
+    , mOccupied(false)
+{}
+
+bool Node::isLeaf()
+{
+    return mLeftNode.get()  == nullptr &&
+           mRightNode.get() == nullptr;
+}
+
+Node* Node::insert(int nWidth, int nHeight, int nPadding)
+{
+    if (!isLeaf())
+    {
+        Node* pNewNode = mLeftNode->insert(nWidth, nHeight, nPadding);
+
+        if (pNewNode != nullptr)
+            return pNewNode;
+
+        return mRightNode->insert(nWidth, nHeight, nPadding);
+    }
+    else
+    {
+        if (mOccupied)
+        {
+            return nullptr;
+        }
+
+        if (nWidth > mRectangle.GetWidth() || nHeight > mRectangle.GetHeight())
+        {   // does not fit
+            return nullptr;
+        }
+
+        if (nWidth == mRectangle.GetWidth() && nHeight == mRectangle.GetHeight())
+        {   // perfect fit
+            mOccupied = true;
+            return this;
+        }
+
+        int dw = mRectangle.GetWidth() - nWidth;
+        int dh = mRectangle.GetHeight() - nHeight;
+
+        Rectangle aLeftRect;
+        Rectangle aRightRect;
+        if (dw > dh)
+        {
+            aLeftRect = Rectangle(Point(mRectangle.Left(), mRectangle.Top()),
+                                  Size(nWidth, mRectangle.GetHeight()));
+            aRightRect = Rectangle(Point(nPadding + mRectangle.Left() + nWidth, mRectangle.Top()),
+                                   Size(mRectangle.GetWidth() - nWidth - nPadding, mRectangle.GetHeight()));
+        }
+        else
+        {
+            aLeftRect = Rectangle(Point(mRectangle.Left(), mRectangle.Top()),
+                                  Size(mRectangle.GetWidth(), nHeight));
+            aRightRect = Rectangle(Point(mRectangle.Left(), nPadding + mRectangle.Top() + nHeight),
+                                   Size(mRectangle.GetWidth(), mRectangle.GetHeight() - nHeight - nPadding));
+        }
+
+        mLeftNode.reset(new Node(aLeftRect));
+        mRightNode.reset(new Node(aRightRect));
+
+        return mLeftNode->insert(nWidth, nHeight, nPadding);
+    }
+}
+
+struct PackedTexture
+{
+    std::unique_ptr<Node> mpRootNode;
+    ImplOpenGLTexture* mpTexture;
+};
+
+PackedTextureAtlasManager::PackedTextureAtlasManager(int nTextureWidth, int nTextureHeight)
+    : mnTextureWidth(nTextureWidth)
+    , mnTextureHeight(nTextureHeight)
+{
+}
+
+PackedTextureAtlasManager::~PackedTextureAtlasManager()
+{
+    for (std::unique_ptr<PackedTexture>& pPackedTexture : maPackedTextures)
+    {
+        // Free texture early in VCL shutdown while we have a context.
+        pPackedTexture->mpTexture->Dispose();
+        pPackedTexture->mpTexture->DecreaseRefCount(0);
+    }
+}
+
+void PackedTextureAtlasManager::CreateNewTexture()
+{
+    std::unique_ptr<PackedTexture> pPackedTexture(new PackedTexture);
+    pPackedTexture->mpTexture = new ImplOpenGLTexture(mnTextureWidth, mnTextureHeight, true);
+    Rectangle aInitialRect(Point(0, 0), Size(mnTextureWidth, mnTextureHeight));
+    pPackedTexture->mpRootNode.reset(new Node(aInitialRect));
+    maPackedTextures.push_back(std::move(pPackedTexture));
+}
+
+OpenGLTexture PackedTextureAtlasManager::Reserve(int nWidth, int nHeight)
+{
+    for (std::unique_ptr<PackedTexture>& pPackedTexture : maPackedTextures)
+    {
+        Node* pNode = pPackedTexture->mpRootNode->insert(nWidth, nHeight, 1);
+        if (pNode != nullptr)
+        {
+            return OpenGLTexture(pPackedTexture->mpTexture, pNode->mRectangle, -1);
+        }
+    }
+    CreateNewTexture();
+    std::unique_ptr<PackedTexture>& pPackedTexture = maPackedTextures.back();
+    Node* pNode = pPackedTexture->mpRootNode->insert(nWidth, nHeight, 1);
+    if (pNode != nullptr)
+    {
+        return OpenGLTexture(pPackedTexture->mpTexture, pNode->mRectangle, -1);
+    }
+    return OpenGLTexture();
+}
+
+OpenGLTexture PackedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
+{
+    OpenGLTexture aTexture = Reserve(nWidth, nHeight);
+    if (aTexture && pData == nullptr)
+        return aTexture;
+
+    aTexture.CopyData(nWidth, nHeight, nFormat, nType, pData);
+
+    return aTexture;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 96a098c0e8a009b77a26061dac3318da71d34ee4
Author: Tomaz Vajngerl <tomaz.vajngerl at collabora.com>
Date:   Mon Mar 14 11:20:19 2016 +0100

    opengl: deferred and optimized (text) texture drawing
    
    Switching between textures is not cheap, so minimizing the amount
    of switching performs better. So instead of immediate drawing we
    can accumulate texture draw actions and defer drawing as long as
    possible. After that switch all accumulated textures and draw
    everything needed with one GL draw call.
    
    This is beneficial for text drawing as we cache many glyphs in
    per textue.
    
    Change-Id: I1b94b9ac6a5f2c1a3dbbd75f4df76436a5d40f31

diff --git a/vcl/inc/opengl/AccumulatedTextures.hxx b/vcl/inc/opengl/AccumulatedTextures.hxx
new file mode 100644
index 0000000..9ce170c
--- /dev/null
+++ b/vcl/inc/opengl/AccumulatedTextures.hxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#ifndef INCLUDED_VCL_INC_OPENGL_ACCUMULATEDTEXTURES_H
+#define INCLUDED_VCL_INC_OPENGL_ACCUMULATEDTEXTURES_H
+
+#include <o3tl/make_unique.hxx>
+#include "opengl/texture.hxx"
+#include <memory>
+
+struct TextureDrawParameters
+{
+    std::vector<GLfloat> maVertices;
+    std::vector<GLfloat> maTextureCoords;
+    GLint getNumberOfVertices()
+    {
+        return maVertices.size() / 2;
+    }
+};
+
+struct AccumulatedTexturesEntry
+{
+    OpenGLTexture maTexture;
+    std::unordered_map<SalColor, TextureDrawParameters> maColorTextureDrawParametersMap;
+
+    AccumulatedTexturesEntry(const OpenGLTexture& rTexture)
+        : maTexture(rTexture)
+    {}
+
+    void insert(const SalColor& aColor, const SalTwoRect& r2Rect)
+    {
+        TextureDrawParameters& aDrawParameters = maColorTextureDrawParametersMap[aColor];
+        maTexture.FillCoords<GL_TRIANGLES>(aDrawParameters.maTextureCoords, r2Rect, false);
+
+        GLfloat nX1 = r2Rect.mnDestX;
+        GLfloat nY1 = r2Rect.mnDestY;
+        GLfloat nX2 = r2Rect.mnDestX + r2Rect.mnDestWidth;
+        GLfloat nY2 = r2Rect.mnDestY + r2Rect.mnDestHeight;
+
+        auto& rVertices = aDrawParameters.maVertices;
+        rVertices.push_back(nX1);
+        rVertices.push_back(nY1);
+
+        rVertices.push_back(nX2);
+        rVertices.push_back(nY1);
+
+        rVertices.push_back(nX1);
+        rVertices.push_back(nY2);
+
+        rVertices.push_back(nX1);
+        rVertices.push_back(nY2);
+
+        rVertices.push_back(nX2);
+        rVertices.push_back(nY1);
+
+        rVertices.push_back(nX2);
+        rVertices.push_back(nY2);
+    }
+};
+
+class AccumulatedTextures
+{
+private:
+    typedef std::unordered_map<GLuint, std::unique_ptr<AccumulatedTexturesEntry>> AccumulatedTexturesMap;
+
+    AccumulatedTexturesMap maEntries;
+
+public:
+    AccumulatedTextures()
+    {}
+
+    bool empty()
+    {
+        return maEntries.empty();
+    }
+
+    void clear()
+    {
+        maEntries.clear();
+    }
+
+    void insert(const OpenGLTexture& rTexture, const SalColor& aColor, const SalTwoRect& r2Rect)
+    {
+        GLuint nTextureId = rTexture.Id();
+
+        auto iterator = maEntries.find(nTextureId);
+
+        if (iterator == maEntries.end())
+        {
+            maEntries[nTextureId] = o3tl::make_unique<AccumulatedTexturesEntry>(rTexture);
+        }
+
+        std::unique_ptr<AccumulatedTexturesEntry>& rEntry = maEntries[nTextureId];
+        rEntry->insert(aColor, r2Rect);
+    }
+
+    AccumulatedTexturesMap& getAccumulatedTexturesMap()
+    {
+        return maEntries;
+    }
+};
+
+#endif // INCLUDED_VCL_INC_OPENGL_TEXTURE_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/opengl/texture.hxx b/vcl/inc/opengl/texture.hxx
index 113c65b..f48a1a1 100644
--- a/vcl/inc/opengl/texture.hxx
+++ b/vcl/inc/opengl/texture.hxx
@@ -110,6 +110,7 @@ public:
     GLuint          Id() const;
     int             GetWidth() const;
     int             GetHeight() const;
+
     void            GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool bInverted=false ) const;
     void            GetWholeCoord( GLfloat* pCoord ) const;
 
@@ -128,6 +129,9 @@ public:
     OpenGLTexture&  operator=( const OpenGLTexture& rTexture );
     bool            operator==( const OpenGLTexture& rTexture ) const;
     bool            operator!=( const OpenGLTexture& rTexture ) const;
+
+    template<GLenum type>
+    void FillCoords(std::vector<GLfloat>& aCoordVector, const SalTwoRect& rPosAry, bool bInverted) const;
 };
 
 #endif // INCLUDED_VCL_INC_OPENGL_TEXTURE_H
diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index 8dd0639..0fb2941 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -29,6 +29,7 @@
 #include "opengl/framebuffer.hxx"
 #include "opengl/program.hxx"
 #include "opengl/texture.hxx"
+#include "opengl/AccumulatedTextures.hxx"
 
 #include <memory>
 
@@ -99,6 +100,8 @@ protected:
     SalColor mProgramSolidColor;
     double mProgramSolidTransparency;
 
+    std::unique_ptr<AccumulatedTextures> mpAccumulatedTextures;
+
     void ImplInitClipRegion();
     void ImplSetClipBit( const vcl::Region& rClip, GLuint nMask );
     void ImplDrawLineAA( double nX1, double nY1, double nX2, double nY2, bool edge = false );
@@ -144,6 +147,8 @@ public:
     void DrawLinearGradient( const Gradient& rGradient, const Rectangle& rRect );
     void DrawAxialGradient( const Gradient& rGradient, const Rectangle& rRect );
     void DrawRadialGradient( const Gradient& rGradient, const Rectangle& rRect );
+    void DeferredTextDraw(const OpenGLTexture& rTexture, const SalColor nMaskColor, const SalTwoRect& rPosAry);
+    void FlushDeferredDrawing(bool bInDraw = false);
 
 public:
     // get the width of the device
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index 355fe21..bfa48b4 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -82,6 +82,7 @@ OpenGLSalGraphicsImpl::OpenGLSalGraphicsImpl(SalGraphics& rParent, SalGeometryPr
     , mnDrawCountAtFlush(0)
     , mProgramSolidColor(SALCOLOR_NONE)
     , mProgramSolidTransparency(0.0)
+    , mpAccumulatedTextures(new AccumulatedTextures)
 {
 }
 
@@ -205,6 +206,8 @@ void OpenGLSalGraphicsImpl::PreDraw(XOROption eOpt)
     glViewport( 0, 0, GetWidth(), GetHeight() );
     CHECK_GL_ERROR();
 
+    FlushDeferredDrawing(true);
+
     ImplInitClipRegion();
     CHECK_GL_ERROR();
 
@@ -355,6 +358,11 @@ const vcl::Region& OpenGLSalGraphicsImpl::getClipRegion() const
 bool OpenGLSalGraphicsImpl::setClipRegion( const vcl::Region& rClip )
 {
     VCL_GL_INFO( "::setClipRegion " << rClip );
+    if (maClipRegion == rClip)
+        return true;
+
+    FlushDeferredDrawing();
+
     maClipRegion = rClip;
 
     mbUseStencil = false;
@@ -371,6 +379,11 @@ bool OpenGLSalGraphicsImpl::setClipRegion( const vcl::Region& rClip )
 void OpenGLSalGraphicsImpl::ResetClipRegion()
 {
     VCL_GL_INFO( "::ResetClipRegion" );
+    if (maClipRegion.IsEmpty())
+        return;
+
+    FlushDeferredDrawing();
+
     maClipRegion.SetEmpty();
     mbUseScissor = false;
     mbUseStencil = false;
@@ -1656,6 +1669,81 @@ void OpenGLSalGraphicsImpl::DrawMask( OpenGLTexture& rMask, SalColor nMaskColor,
     mpProgram->Clean();
 }
 
+void OpenGLSalGraphicsImpl::DeferredTextDraw(const OpenGLTexture& rTexture, SalColor aMaskColor, const SalTwoRect& rPosAry)
+{
+    mpAccumulatedTextures->insert(rTexture, aMaskColor, rPosAry);
+}
+
+void OpenGLSalGraphicsImpl::FlushDeferredDrawing(bool bIsInDraw)
+{
+    if (mpAccumulatedTextures->empty())
+        return;
+
+    if (!bIsInDraw)
+        PreDraw();
+
+    OpenGLZone aZone;
+
+#if 0 // Draw a background rect under text for debugging - same color shows text from the same texture
+    static sal_uInt8 r = 0xBE;
+    static sal_uInt8 g = 0xF0;
+    static sal_uInt8 b = 0xFF;
+    static std::unordered_map<GLuint, Color> aColorForTextureMap;
+
+
+    for (auto& rPair : mpAccumulatedTextures->getAccumulatedTexturesMap())
+    {
+        OpenGLTexture& rTexture = rPair.second->maTexture;
+        Color aUseColor;
+        if (aColorForTextureMap.find(rTexture.Id()) == aColorForTextureMap.end())
+        {
+            Color aColor(r, g, b);
+            sal_uInt16 h,s,br;
+            aColor.RGBtoHSB(h, s, br);
+            aColor = Color::HSBtoRGB((h + 40) % 360, s, br);
+            r = aColor.GetRed();
+            g = aColor.GetGreen();
+            b = aColor.GetBlue();
+            aColorForTextureMap[rTexture.Id()] = aColor;
+        }
+        aUseColor = aColorForTextureMap[rTexture.Id()];
+
+        if (!UseSolid(MAKE_SALCOLOR(aUseColor.GetRed(), aUseColor.GetGreen(), aUseColor.GetBlue())))
+            return;
+        for (auto rColorTwoRectPair: rPair.second->maColorTextureDrawParametersMap)
+        {
+            TextureDrawParameters& rParameters = rColorTwoRectPair.second;
+            ApplyProgramMatrices();
+            mpProgram->SetTextureCoord(rParameters.maTextureCoords.data());
+            mpProgram->SetVertices(rParameters.maVertices.data());
+            glDrawArrays(GL_TRIANGLES, 0, rParameters.getNumberOfVertices());
+        }
+    }
+#endif
+
+    if( !UseProgram( "textureVertexShader", "maskFragmentShader" ) )
+        return;
+    mpProgram->SetBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    for (auto& rPair : mpAccumulatedTextures->getAccumulatedTexturesMap())
+    {
+        OpenGLTexture& rTexture = rPair.second->maTexture;
+        mpProgram->SetTexture("sampler", rTexture);
+        for (auto& rColorTwoRectPair: rPair.second->maColorTextureDrawParametersMap)
+        {
+            mpProgram->SetColor("color", rColorTwoRectPair.first, 0);
+            TextureDrawParameters& rParameters = rColorTwoRectPair.second;
+            ApplyProgramMatrices();
+            mpProgram->SetTextureCoord(rParameters.maTextureCoords.data());
+            mpProgram->SetVertices(rParameters.maVertices.data());
+            glDrawArrays(GL_TRIANGLES, 0, rParameters.getNumberOfVertices());
+        }
+    }
+    mpProgram->Clean();
+    mpAccumulatedTextures->clear();
+    if (!bIsInDraw)
+        PostDraw();
+}
+
 void OpenGLSalGraphicsImpl::DrawLinearGradient( const Gradient& rGradient, const Rectangle& rRect )
 {
     OpenGLZone aZone;
@@ -2019,6 +2107,8 @@ void OpenGLSalGraphicsImpl::DoCopyBits( const SalTwoRect& rPosAry, OpenGLSalGrap
 {
     VCL_GL_INFO( "::copyBits" );
 
+    rImpl.FlushDeferredDrawing();
+
     if( !rImpl.maOffscreenTex )
     {
         VCL_GL_INFO( "::copyBits - skipping copy of un-initialized framebuffer contents of size "
@@ -2120,6 +2210,8 @@ SalBitmap* OpenGLSalGraphicsImpl::getBitmap( long nX, long nY, long nWidth, long
 
 SalColor OpenGLSalGraphicsImpl::getPixel( long nX, long nY )
 {
+    FlushDeferredDrawing();
+
     char pixel[3] = { 0, 0, 0 };
 
     PreDraw( XOROption::IMPLEMENT_XOR );
@@ -2388,6 +2480,8 @@ bool OpenGLSalGraphicsImpl::drawGradient(const tools::PolyPolygon& rPolyPoly,
 
 void OpenGLSalGraphicsImpl::flush()
 {
+    FlushDeferredDrawing();
+
     if( IsOffscreen() )
         return;
 
@@ -2402,6 +2496,8 @@ void OpenGLSalGraphicsImpl::flush()
 
 void OpenGLSalGraphicsImpl::doFlush()
 {
+    FlushDeferredDrawing();
+
     if( IsOffscreen() )
         return;
 
diff --git a/vcl/opengl/texture.cxx b/vcl/opengl/texture.cxx
index 1b2e5a7..fd6d11f 100644
--- a/vcl/opengl/texture.cxx
+++ b/vcl/opengl/texture.cxx
@@ -369,6 +369,52 @@ void OpenGLTexture::GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool b
     }
 }
 
+template <>
+void OpenGLTexture::FillCoords<GL_TRIANGLES>(std::vector<GLfloat>& aCoord, const SalTwoRect& rPosAry, bool bInverted) const
+{
+    VCL_GL_INFO("Add coord " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
+
+    GLfloat x1 = 0.0f;
+    GLfloat x2 = 0.0f;
+    GLfloat y1 = 0.0f;
+    GLfloat y2 = 0.0f;
+
+    if (mpImpl)
+    {
+        x1 = (maRect.Left() + rPosAry.mnSrcX) / (double) mpImpl->mnWidth;
+        x2 = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / (double) mpImpl->mnWidth;
+
+        if (bInverted)
+        {
+            y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / (double) mpImpl->mnHeight;
+            y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / (double) mpImpl->mnHeight;
+        }
+        else
+        {
+            y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / (double) mpImpl->mnHeight;
+            y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / (double) mpImpl->mnHeight;
+        }
+    }
+
+    aCoord.push_back(x1);
+    aCoord.push_back(y1);
+
+    aCoord.push_back(x2);
+    aCoord.push_back(y1);
+
+    aCoord.push_back(x1);
+    aCoord.push_back(y2);
+
+    aCoord.push_back(x1);
+    aCoord.push_back(y2);
+
+    aCoord.push_back(x2);
+    aCoord.push_back(y1);
+
+    aCoord.push_back(x2);
+    aCoord.push_back(y2);
+}
+
 void OpenGLTexture::GetWholeCoord( GLfloat* pCoord ) const
 {
     if( GetWidth() != mpImpl->mnWidth || GetHeight() != mpImpl->mnHeight )
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index 1ee9d02..293bfa2 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -364,7 +364,7 @@ bool WinFontInstance::GlyphIsCached(int nGlyphIndex) const
 
 bool WinFontInstance::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
 {
-    const int DEFAULT_CHUNK_SIZE = 20;
+    const int DEFAULT_CHUNK_SIZE = 40;
 
     if (nGlyphIndex == DROPPED_OUTGLYPH)
         return true;
@@ -1556,8 +1556,6 @@ bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
     if (!pImpl)
         return false;
 
-    pImpl->PreDraw();
-
     HFONT hOrigFont = DisableFontScaling();
     Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
 
@@ -1589,7 +1587,8 @@ bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
                            nAdvance + aPos.X() - rChunk.getExtraOffset() + rChunk.maLeftOverhangs[n],
                            aPos.Y() - rChunk.mnBaselineOffset - rChunk.getExtraOffset(),
                            rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-        pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
+
+        pImpl->DeferredTextDraw(*rChunk.mpTexture, salColor, a2Rects);
 
         nAdvance += mpGlyphAdvances[i];
     }
@@ -1597,8 +1596,6 @@ bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
     if( hOrigFont )
         DeleteFont(SelectFont(hDC, hOrigFont));
 
-    pImpl->PostDraw();
-
     return true;
 }
 
@@ -3049,8 +3046,6 @@ bool UniscribeLayout::DrawCachedGlyphsUsingTextures(SalGraphics& rGraphics) cons
     if (!pImpl)
         return false;
 
-    pImpl->PreDraw();
-
     // FIXME: This code snippet is mostly copied from the one in
     // UniscribeLayout::DrawTextImpl. Should be factored out.
     int nBaseClusterOffset = 0;
@@ -3109,7 +3104,7 @@ bool UniscribeLayout::DrawCachedGlyphsUsingTextures(SalGraphics& rGraphics) cons
                                    rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
                                    aPos.X() + rChunk.maLeftOverhangs[n], nAdvance + aPos.Y(),
                                    rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-                pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
+                pImpl->DeferredTextDraw(*rChunk.mpTexture, salColor, a2Rects);
             }
             else
             {
@@ -3118,12 +3113,11 @@ bool UniscribeLayout::DrawCachedGlyphsUsingTextures(SalGraphics& rGraphics) cons
                                    nAdvance + aPos.X() + mpGlyphOffsets[i].du - rChunk.getExtraOffset() + rChunk.maLeftOverhangs[n],
                                    aPos.Y() + mpGlyphOffsets[i].dv - rChunk.mnBaselineOffset - rChunk.getExtraOffset(),
                                    rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-                pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
+                pImpl->DeferredTextDraw(*rChunk.mpTexture, salColor, a2Rects);
             }
             nAdvance += pGlyphWidths[i];
         }
     }
-    pImpl->PostDraw();
 
     return true;
 }


More information about the Libreoffice-commits mailing list