[Libreoffice-commits] core.git: Branch 'private/mmeeks/opengl-backbuffer2' - vcl/inc vcl/opengl
Michael Meeks
michael.meeks at collabora.com
Fri Dec 4 06:57:00 PST 2015
Rebased ref, commits from common ancestor:
commit a594a901aa3e37c157427735c269f1109c2ab953
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Fri Nov 13 12:00:59 2015 +0000
tdf#93529 - move to a Mac-like double-buffered OpenGL model.
This moves us to always rendering to an off-screen texture, and then
(at idle) blitting this to the screen & swapping buffers. Ideally we
should never see any rendering, or flicker again with this approach.
Several fixes are included:
+ avoid multiple OpenGL contexts being created for the same window,
created excessive flicker problems.
+ de-virtualize UseContext - which context we use is less critical.
+ kill 'mbOffscreen' distinction - all VCL rendering is offscreen.
+ implement 'doFlush' and high priority idle flushing.
Change-Id: I6be08595b6c8deb7e6db0dbd81308b2c97d2b4ff
diff --git a/vcl/inc/opengl/win/gdiimpl.hxx b/vcl/inc/opengl/win/gdiimpl.hxx
index ecefede..8102d2e 100644
--- a/vcl/inc/opengl/win/gdiimpl.hxx
+++ b/vcl/inc/opengl/win/gdiimpl.hxx
@@ -31,7 +31,6 @@ public:
protected:
virtual rtl::Reference<OpenGLContext> CreateWinContext() override;
- virtual bool UseContext( const rtl::Reference<OpenGLContext> &pContext ) override;
bool RenderTextureCombo(TextureCombo& rCombo, int nX, int nY);
diff --git a/vcl/inc/opengl/x11/gdiimpl.hxx b/vcl/inc/opengl/x11/gdiimpl.hxx
index f07468d..eccd0ef 100644
--- a/vcl/inc/opengl/x11/gdiimpl.hxx
+++ b/vcl/inc/opengl/x11/gdiimpl.hxx
@@ -29,7 +29,6 @@ public:
protected:
virtual rtl::Reference<OpenGLContext> CreateWinContext() override;
- virtual bool UseContext( const rtl::Reference<OpenGLContext> &pContext ) override;
bool RenderPixmap(X11Pixmap* pPixmap, X11Pixmap* pMask, int nX, int nY, TextureCombo& rCombo);
diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index 667b2ec..b2969cc 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -52,12 +52,20 @@ struct TextureCombo
std::unique_ptr<OpenGLTexture> mpMask;
};
+class OpenGLFlushIdle;
+
class VCL_DLLPUBLIC OpenGLSalGraphicsImpl : public SalGraphicsImpl
{
friend class OpenGLTests;
protected:
+ /// This context is solely for blitting @maOffscreenTex
+ rtl::Reference<OpenGLContext> mpWindowContext;
+
+ /// This context is whatever is most convenient to render
+ /// to @maOffscreenTex with.
rtl::Reference<OpenGLContext> mpContext;
+
SalGraphics& mrParent;
/// Pointer to the SalFrame or SalVirtualDevice
SalGeometryProvider* mpProvider;
@@ -67,18 +75,27 @@ protected:
/// Is it someone else's context we shouldn't be fiddling with ?
static bool IsForeignContext(const rtl::Reference<OpenGLContext> &xContext);
+ /// This idle handler is used to swap buffers after rendering.
+ OpenGLFlushIdle *mpFlush;
+
// clipping
vcl::Region maClipRegion;
bool mbUseScissor;
bool mbUseStencil;
- bool mbOffscreen;
+ /**
+ * All rendering happens to this off-screen texture. For
+ * non-virtual devices, ie. windows - we will blit it and
+ * swapBuffers later.
+ */
OpenGLTexture maOffscreenTex;
SalColor mnLineColor;
SalColor mnFillColor;
#ifdef DBG_UTIL
bool mProgramIsSolidColor;
+ sal_uInt32 mnDrawCount;
+ sal_uInt32 mnDrawCountAtFlush;
#endif
SalColor mProgramSolidColor;
double mProgramSolidTransparency;
@@ -131,7 +148,10 @@ public:
// get the height of the device
GLfloat GetHeight() const { return mpProvider ? mpProvider->GetHeight() : 1; }
- // check whether this instance is used for offscreen rendering
+ /**
+ * check whether this instance is used for offscreen (Virtual Device)
+ * rendering ie. does it need its own context.
+ */
bool IsOffscreen() const { return mpProvider == nullptr || mpProvider->IsOffScreen(); }
// operations to do before painting
@@ -144,14 +164,18 @@ protected:
bool AcquireContext();
bool ReleaseContext();
- // retrieve the default context for offscreen rendering
+ /// retrieve the default context for offscreen rendering
static rtl::Reference<OpenGLContext> GetDefaultContext();
- // create a new context for window rendering
+ /// create a new context for rendering to the underlying window
virtual rtl::Reference<OpenGLContext> CreateWinContext() = 0;
- // check whether the given context can be used by this instance
- virtual bool UseContext( const rtl::Reference<OpenGLContext> &pContext ) = 0;
+ /// check whether the given context can be used for off-screen rendering
+ bool UseContext( const rtl::Reference<OpenGLContext> &pContext )
+ {
+ return pContext->isInitialized() && // not released by the OS etc.
+ IsForeignContext( pContext ); // a genuine VCL context.
+ }
public:
OpenGLSalGraphicsImpl(SalGraphics& pParent, SalGeometryProvider *pProvider);
@@ -328,8 +352,12 @@ public:
virtual bool drawGradient(const tools::PolyPolygon& rPolygon, const Gradient& rGradient) override;
- virtual OpenGLContext *beginPaint() override;
-private:
+ /// queue an idle flush of contents of the back-buffer to the screen
+ void flush();
+
+public:
+ /// do flush of contents of the back-buffer to the screen & swap.
+ void doFlush();
};
#endif
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index 413f802..3266dea 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -37,19 +37,43 @@
#include <vector>
+#include <stdlib.h>
+
+class OpenGLFlushIdle : public Idle
+{
+ OpenGLSalGraphicsImpl *m_pImpl;
+public:
+ OpenGLFlushIdle( OpenGLSalGraphicsImpl *pImpl )
+ : Idle( "gl idle swap" )
+ , m_pImpl( pImpl )
+ {
+ SetPriority( SchedulerPriority::HIGHEST );
+ }
+ ~OpenGLFlushIdle()
+ {
+ }
+ virtual void Invoke() override
+ {
+ m_pImpl->doFlush();
+ Stop();
+ }
+};
+
OpenGLSalGraphicsImpl::OpenGLSalGraphicsImpl(SalGraphics& rParent, SalGeometryProvider *pProvider)
: mpContext(nullptr)
, mrParent(rParent)
, mpProvider(pProvider)
, mpFramebuffer(nullptr)
, mpProgram(nullptr)
+ , mpFlush(new OpenGLFlushIdle(this))
, mbUseScissor(false)
, mbUseStencil(false)
- , mbOffscreen(false)
, mnLineColor(SALCOLOR_NONE)
, mnFillColor(SALCOLOR_NONE)
#ifdef DBG_UTIL
, mProgramIsSolidColor(false)
+ , mnDrawCount(0)
+ , mnDrawCountAtFlush(0)
#endif
, mProgramSolidColor(SALCOLOR_NONE)
, mProgramSolidTransparency(0.0)
@@ -58,6 +82,11 @@ OpenGLSalGraphicsImpl::OpenGLSalGraphicsImpl(SalGraphics& rParent, SalGeometryPr
OpenGLSalGraphicsImpl::~OpenGLSalGraphicsImpl()
{
+ if( !IsOffscreen() && mnDrawCountAtFlush != mnDrawCount )
+ VCL_GL_INFO( "Destroying un-flushed on-screen graphics" );
+
+ delete mpFlush;
+
ReleaseContext();
}
@@ -79,10 +108,9 @@ bool OpenGLSalGraphicsImpl::AcquireContext( )
// We always prefer to bind our VirtualDevice / offscreen graphics
// to the current OpenGLContext - to avoid switching contexts.
- if (mpContext.is() && mbOffscreen)
+ if (mpContext.is() && OpenGLContext::hasCurrent() && !mpContext->isCurrent())
{
- if (OpenGLContext::hasCurrent() && !mpContext->isCurrent())
- mpContext.clear();
+ mpContext.clear();
}
if( mpContext.is() )
@@ -97,15 +125,17 @@ bool OpenGLSalGraphicsImpl::AcquireContext( )
while( pContext )
{
// check if this context can be used by this SalGraphicsImpl instance
- if( UseContext( pContext ) )
+ if( UseContext( pContext ) )
break;
pContext = pContext->mpPrevContext;
}
- if( pContext )
+ if( mpContext.is() )
mpContext = pContext;
+ else if( mpWindowContext.is() )
+ mpContext = mpWindowContext;
else
- mpContext = mbOffscreen ? GetDefaultContext() : CreateWinContext();
+ mpContext = GetDefaultContext();
return mpContext.is();
}
@@ -124,19 +154,15 @@ void OpenGLSalGraphicsImpl::Init()
if( !mpProvider )
return;
- mbOffscreen = IsOffscreen();
-
// check if we can simply re-use the same context
if( mpContext.is() )
{
- if( !mpContext->isInitialized() ||
- !UseContext( mpContext ) )
+ if( !UseContext( mpContext ) )
ReleaseContext();
}
- // reset the offscreen texture
- if( !mbOffscreen ||
- maOffscreenTex.GetWidth() != GetWidth() ||
+ // Always create the offscreen texture
+ if( maOffscreenTex.GetWidth() != GetWidth() ||
maOffscreenTex.GetHeight() != GetHeight() )
{
if( maOffscreenTex && // don't work to release empty textures
@@ -146,6 +172,14 @@ void OpenGLSalGraphicsImpl::Init()
mpContext->ReleaseFramebuffer( maOffscreenTex );
}
maOffscreenTex = OpenGLTexture();
+ VCL_GL_INFO("::Init - re-size offscreen texture");
+ }
+
+ if( !IsOffscreen() )
+ {
+ if( mpWindowContext.is() )
+ mpWindowContext->reset();
+ mpWindowContext = CreateWinContext();
}
}
@@ -160,12 +194,15 @@ void OpenGLSalGraphicsImpl::DeInit()
// get a shiny new context in AcquireContext:: next PreDraw.
if( mpContext.is() && !IsOffscreen() )
mpContext->reset();
+ mpContext.clear();
}
void OpenGLSalGraphicsImpl::PreDraw()
{
OpenGLZone::enter();
+ mnDrawCount++;
+
if( !AcquireContext() )
{
SAL_WARN( "vcl.opengl", "Couldn't acquire context" );
@@ -175,10 +212,7 @@ void OpenGLSalGraphicsImpl::PreDraw()
mpContext->makeCurrent();
CHECK_GL_ERROR();
- if( !mbOffscreen )
- mpContext->AcquireDefaultFramebuffer();
- else
- CheckOffscreenTexture();
+ CheckOffscreenTexture();
CHECK_GL_ERROR();
glViewport( 0, 0, GetWidth(), GetHeight() );
@@ -190,15 +224,13 @@ void OpenGLSalGraphicsImpl::PreDraw()
void OpenGLSalGraphicsImpl::PostDraw()
{
- if( !mbOffscreen && mpContext->mnPainting == 0 )
- glFlush();
if( mbUseScissor )
{
glDisable( GL_SCISSOR_TEST );
CHECK_GL_ERROR();
}
- if( mbUseStencil )
- {
+ if( mbUseStencil )
+ {
glDisable( GL_STENCIL_TEST );
CHECK_GL_ERROR();
}
@@ -210,6 +242,18 @@ void OpenGLSalGraphicsImpl::PostDraw()
mProgramIsSolidColor = false;
#endif
}
+
+ assert (maOffscreenTex);
+
+ if( IsOffscreen() )
+ assert( !mpWindowContext.is() );
+ else
+ assert( mpWindowContext.is() );
+
+ // Always queue the flush.
+ if( !IsOffscreen() )
+ flush();
+
OpenGLZone::leave();
}
@@ -221,8 +265,9 @@ void OpenGLSalGraphicsImpl::ApplyProgramMatrices(float fPixelOffset)
void OpenGLSalGraphicsImpl::freeResources()
{
// TODO Delete shaders, programs and textures if not shared
- if( mbOffscreen && mpContext.is() && mpContext->isInitialized() )
+ if( mpContext.is() && mpContext->isInitialized() )
{
+ VCL_GL_INFO( "freeResources" );
mpContext->makeCurrent();
mpContext->ReleaseFramebuffer( maOffscreenTex );
}
@@ -387,8 +432,29 @@ void OpenGLSalGraphicsImpl::SetROPFillColor( SalROPColor /*nROPColor*/ )
bool OpenGLSalGraphicsImpl::CheckOffscreenTexture()
{
+ bool bClearTexture = false;
+
+ VCL_GL_INFO( "Check Offscreen texture" );
+
+ // Always create the offscreen texture
+ if( maOffscreenTex )
+ {
+ if( maOffscreenTex.GetWidth() != GetWidth() ||
+ maOffscreenTex.GetHeight() != GetHeight() )
+ {
+ mpContext->ReleaseFramebuffer( maOffscreenTex );
+ maOffscreenTex = OpenGLTexture();
+ VCL_GL_INFO( "re-size offscreen texture" );
+ }
+ }
+
if( !maOffscreenTex )
+ {
+ VCL_GL_INFO( "create texture of size "
+ << GetWidth() << " x " << GetHeight() );
maOffscreenTex = OpenGLTexture( GetWidth(), GetHeight() );
+ bClearTexture = true;
+ }
if( !maOffscreenTex.IsUnique() )
{
@@ -405,8 +471,19 @@ bool OpenGLSalGraphicsImpl::CheckOffscreenTexture()
else
{
mpFramebuffer = mpContext->AcquireFramebuffer( maOffscreenTex );
+ CHECK_GL_ERROR();
+
+ if( bClearTexture )
+ {
+ glDrawBuffer( GL_COLOR_ATTACHMENT0 );
+ GLfloat clearColor[4] = { 1.0, 0, 0, 0 };
+ glClearBufferfv( GL_COLOR, 0, clearColor );
+ // FIXME: use glClearTexImage if we have it ?
+ }
}
+ assert( maOffscreenTex );
+
CHECK_GL_ERROR();
return true;
}
@@ -1546,17 +1623,9 @@ void OpenGLSalGraphicsImpl::DoCopyBits( const SalTwoRect& rPosAry, OpenGLSalGrap
return;
}
- if( rImpl.mbOffscreen )
- {
- PreDraw();
- DrawTexture( rImpl.maOffscreenTex, rPosAry );
- PostDraw();
- return;
- }
-
- SAL_WARN( "vcl.opengl", "*** NOT IMPLEMENTED *** copyBits" );
- // TODO: Copy from one FBO to the other (glBlitFramebuffer)
- // ie. copying from one visible window to another visible window
+ PreDraw();
+ DrawTexture( rImpl.maOffscreenTex, rPosAry );
+ PostDraw();
}
void OpenGLSalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap )
@@ -1883,12 +1952,126 @@ bool OpenGLSalGraphicsImpl::drawGradient(const tools::PolyPolygon& rPolyPoly,
return true;
}
-OpenGLContext *OpenGLSalGraphicsImpl::beginPaint()
+void OpenGLSalGraphicsImpl::flush()
{
- if( mbOffscreen || !AcquireContext() )
- return nullptr;
+ if( IsOffscreen() )
+ return;
+
+ if( !Application::IsInExecute() )
+ {
+ // otherwise nothing would trigger idle rendering
+ doFlush();
+ }
+ else if( !mpFlush->IsActive() )
+ mpFlush->Start();
+}
+
+void OpenGLSalGraphicsImpl::doFlush()
+{
+ if( IsOffscreen() )
+ return;
+
+ assert( mpWindowContext.is() );
+
+ if( !maOffscreenTex )
+ {
+ VCL_GL_INFO( "flushAndSwap - odd no texture !" );
+ return;
+ }
+
+ if (mnDrawCountAtFlush == mnDrawCount)
+ {
+ VCL_GL_INFO( "eliding redundant flushAndSwap, no drawing since last!" );
+ return;
+ }
+
+ mnDrawCountAtFlush = mnDrawCount;
+
+ OpenGLZone aZone;
+
+ VCL_GL_INFO( "flushAndSwap" );
+
+ // Interesting ! -> this destroys a context [ somehow ] ...
+ mpWindowContext->makeCurrent();
+ CHECK_GL_ERROR();
+
+ VCL_GL_INFO( "flushAndSwap - acquire default frame buffer" );
+
+ mpWindowContext->AcquireDefaultFramebuffer();
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 ); // FIXME: paranoid double check.
+ CHECK_GL_ERROR();
+
+ VCL_GL_INFO( "flushAndSwap - acquired default frame buffer" );
+
+ glDisable( GL_SCISSOR_TEST ); // FIXME: paranoia ...
+ CHECK_GL_ERROR();
+ glDisable( GL_STENCIL_TEST ); // FIXME: paranoia ...
+ CHECK_GL_ERROR();
+
+ glViewport( 0, 0, GetWidth(), GetHeight() );
+ CHECK_GL_ERROR();
+
+ glClearColor((float)rand()/RAND_MAX, (float)rand()/RAND_MAX,
+ (float)rand()/RAND_MAX, 1.0);
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+ CHECK_GL_ERROR();
+
+ SalTwoRect aPosAry( 0, 0, maOffscreenTex.GetWidth(), maOffscreenTex.GetHeight(),
+ 0, 0, maOffscreenTex.GetWidth(), maOffscreenTex.GetHeight() );
+ VCL_GL_INFO( "Texture height " << maOffscreenTex.GetHeight() << " vs. window height " << GetHeight() );
+
+ OpenGLProgram *pProgram =
+ mpWindowContext->UseProgram( "textureVertexShader", "textureFragmentShader", "" );
+ if( !pProgram )
+ VCL_GL_INFO( "Can't compile simple copying shader !" );
else
- return mpContext.get();
+ {
+ pProgram->Use(); // FIXME: paranoia ...
+ VCL_GL_INFO( "done paranoid re-use." );
+ pProgram->SetTexture( "sampler", maOffscreenTex );
+ maOffscreenTex.Bind(); // FIXME: paranoia ...
+
+ VCL_GL_INFO( "bound bits etc." );
+
+ GLfloat aTexCoord[8];
+ maOffscreenTex.GetCoord( aTexCoord, aPosAry, false );
+ pProgram->SetTextureCoord( aTexCoord );
+
+ long nX1( aPosAry.mnDestX );
+ long nY1( aPosAry.mnDestY );
+ long nX2( nX1 + aPosAry.mnDestWidth );
+ long nY2( nY1 + aPosAry.mnDestHeight );
+ const SalPoint aPoints[] = { { nX1, nY2 }, { nX1, nY1 },
+ { nX2, nY1 }, { nX2, nY2 }};
+
+ sal_uInt32 nPoints = 4;
+ std::vector<GLfloat> aVertices(nPoints * 2);
+ sal_uInt32 i, j;
+
+ for( i = 0, j = 0; i < nPoints; i++, j += 2 )
+ {
+ aVertices[j] = GLfloat(aPoints[i].mnX);
+ aVertices[j+1] = GLfloat(aPoints[i].mnY);
+ }
+
+ pProgram->ApplyMatrix(GetWidth(), GetHeight(), 0.0);
+ pProgram->SetVertices( &aVertices[0] );
+ if (!getenv("NO_COPY"))
+ glDrawArrays( GL_TRIANGLE_FAN, 0, nPoints );
+
+ pProgram->Clean();
+
+ glBindTexture( GL_TEXTURE_2D, 0 );
+
+ if (!getenv("NO_SWAP"))
+ {
+ mpWindowContext->swapBuffers();
+ if (!getenv("NO_SLEEP"))
+ usleep(500 * 1000);
+ }
+ }
+
+ VCL_GL_INFO( "flushAndSwap - end." );
}
bool OpenGLSalGraphicsImpl::IsForeignContext(const rtl::Reference<OpenGLContext> &xContext)
diff --git a/vcl/opengl/texture.cxx b/vcl/opengl/texture.cxx
index 2d4cb40..b540e96 100644
--- a/vcl/opengl/texture.cxx
+++ b/vcl/opengl/texture.cxx
@@ -349,6 +349,10 @@ void OpenGLTexture::Bind()
glBindTexture( GL_TEXTURE_2D, mpImpl->mnTexture );
CHECK_GL_ERROR();
}
+ else
+ VCL_GL_INFO( "OpenGLTexture::Binding invalid texture" );
+
+ CHECK_GL_ERROR();
}
void OpenGLTexture::Unbind()
diff --git a/vcl/opengl/win/gdiimpl.cxx b/vcl/opengl/win/gdiimpl.cxx
index f57c82d..666cdbf 100644
--- a/vcl/opengl/win/gdiimpl.cxx
+++ b/vcl/opengl/win/gdiimpl.cxx
@@ -29,21 +29,10 @@ void WinOpenGLSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics*
rtl::Reference<OpenGLContext> WinOpenGLSalGraphicsImpl::CreateWinContext()
{
rtl::Reference<OpenGLContext> pContext = OpenGLContext::Create();
- pContext->requestSingleBufferedRendering();
pContext->init( mrParent.mhLocalDC, mrParent.mhWnd );
return pContext;
}
-bool WinOpenGLSalGraphicsImpl::UseContext( const rtl::Reference<OpenGLContext> &pContext )
-{
- if( !pContext.is() || !pContext->isInitialized() || IsForeignContext( pContext ) )
- return false;
- if( IsOffscreen() )
- return true;
- return pContext->getOpenGLWindow().hWnd == mrParent.mhWnd &&
- pContext->getOpenGLWindow().hDC == mrParent.mhLocalDC;
-}
-
void WinOpenGLSalGraphicsImpl::Init()
{
if ( !IsOffscreen() && mpContext.is() && mpContext->isInitialized() &&
diff --git a/vcl/opengl/x11/gdiimpl.cxx b/vcl/opengl/x11/gdiimpl.cxx
index 2418bc6..595ca45c 100644
--- a/vcl/opengl/x11/gdiimpl.cxx
+++ b/vcl/opengl/x11/gdiimpl.cxx
@@ -58,18 +58,6 @@ rtl::Reference<OpenGLContext> X11OpenGLSalGraphicsImpl::CreateWinContext()
return pContext;
}
-bool X11OpenGLSalGraphicsImpl::UseContext( const rtl::Reference<OpenGLContext> &pContext )
-{
- X11WindowProvider *pProvider = dynamic_cast<X11WindowProvider*>(mrParent.m_pFrame);
-
- if( !pContext->isInitialized() || IsForeignContext( pContext ) )
- return false;
- if( !pProvider )
- return pContext->getOpenGLWindow().win != None;
- else
- return pContext->getOpenGLWindow().win == pProvider->GetX11Window();
-}
-
void X11OpenGLSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
{
OpenGLSalGraphicsImpl *pImpl = pSrcGraphics ? static_cast< OpenGLSalGraphicsImpl* >(pSrcGraphics->GetImpl()) : static_cast< OpenGLSalGraphicsImpl *>(mrParent.GetImpl());
More information about the Libreoffice-commits
mailing list