Kaydet (Commit) e614a1e8 authored tarafından Michael Meeks's avatar Michael Meeks

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.
   + bind stencil buffer for clipping vs. textures - fixing complex
     clopping when rendering to virtual-devices, and off-screen.
   + document environment. variables.
   + use white as default background glClear color, but red or
     random color for DBGUTIL.

Change-Id: I6be08595b6c8deb7e6db0dbd81308b2c97d2b4ff
üst 382eafb5
......@@ -21,6 +21,8 @@ EMF_PLUS_DISABLE - use EMF rendering and ignore EMF+ specifics
OpenGL
------
SAL_FORCEGL - force enable OpenGL
SAL_GL_NO_SWAP - disable buffer swapping if set (should show nothing)
SAL_GL_SLEEP_ON_SWAP - sleep for half a second on each swap-buffers.
SAL_WITHOUT_WIDGET_CACHE - disable LRU caching of native widget texutres
SAL_DISABLE_GLYPH_CACHING - don't render glyphs through OpenGL textures
SAL_DISABLE_GL_WATCHDOG - don't start the thread that watches for broken GL drivers
......@@ -37,6 +37,8 @@ public:
int mnWidth;
int mnHeight;
GLenum mnFilter;
GLuint mnOptStencil;
bool mbHasOptStencil;
std::unique_ptr<std::vector<int>> mpSlotReferences;
int mnFreeSlots;
......@@ -76,7 +78,8 @@ public:
}
bool InitializeSlots(int nSlotSize);
int FindFreeSlot();
int FindFreeSlot();
GLuint AddStencil();
};
class VCL_DLLPUBLIC OpenGLTexture
......@@ -110,6 +113,9 @@ public:
void Bind();
void Unbind();
void Read( GLenum nFormat, GLenum nType, sal_uInt8* pData );
GLuint AddStencil();
bool HasStencil() const;
GLuint StencilId() const;
void SaveToFile(const OUString& rFileName);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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,12 +75,19 @@ 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;
......@@ -80,6 +95,8 @@ protected:
#ifdef DBG_UTIL
bool mProgramIsSolidColor;
#endif
sal_uInt32 mnDrawCount;
sal_uInt32 mnDrawCountAtFlush;
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
......
......@@ -72,6 +72,16 @@ void OpenGLFramebuffer::AttachTexture( const OpenGLTexture& rTexture )
mnHeight = rTexture.GetHeight();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mnAttachedTexture, 0);
CHECK_GL_ERROR();
GLuint nStencil = rTexture.StencilId();
if( nStencil )
{
VCL_GL_INFO( "Attaching stencil " << nStencil << " to framebuffer " << (int)mnId );
glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, nStencil );
CHECK_GL_ERROR();
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
CHECK_GL_ERROR();
if (status != GL_FRAMEBUFFER_COMPLETE)
......@@ -87,6 +97,11 @@ void OpenGLFramebuffer::DetachTexture()
mnAttachedTexture = 0;
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0 );
CHECK_GL_ERROR();
// FIXME: we could make this conditional on having a stencil ?
glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, 0 );
CHECK_GL_ERROR();
}
}
......
This diff is collapsed.
......@@ -35,6 +35,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate )
mnWidth( nWidth ),
mnHeight( nHeight ),
mnFilter( GL_NEAREST ),
mnOptStencil( 0 ),
mnFreeSlots(-1)
{
glGenTextures( 1, &mnTexture );
......@@ -67,6 +68,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nX, int nY, int nWidth, int nHeight )
mnWidth( nWidth ),
mnHeight( nHeight ),
mnFilter( GL_NEAREST ),
mnOptStencil( 0 ),
mnFreeSlots(-1)
{
// FIXME We need the window height here
......@@ -99,6 +101,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int
mnWidth( nWidth ),
mnHeight( nHeight ),
mnFilter( GL_NEAREST ),
mnOptStencil( 0 ),
mnFreeSlots(-1)
{
if( !mnTexture )
......@@ -126,6 +129,21 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int
VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " from data" );
}
GLuint ImplOpenGLTexture::AddStencil()
{
assert( mnOptStencil == 0 );
glGenRenderbuffers( 1, &mnOptStencil );
glBindRenderbuffer( GL_RENDERBUFFER, mnOptStencil );
CHECK_GL_ERROR();
VCL_GL_INFO( "Allocate stencil " << mnWidth << " x " << mnHeight );
glRenderbufferStorage( GL_RENDERBUFFER, GL_STENCIL_INDEX,
mnWidth, mnHeight );
CHECK_GL_ERROR();
return mnOptStencil;
}
ImplOpenGLTexture::~ImplOpenGLTexture()
{
VCL_GL_INFO( "~OpenGLTexture " << mnTexture );
......@@ -136,9 +154,11 @@ ImplOpenGLTexture::~ImplOpenGLTexture()
// Check we have been correctly un-bound from all framebuffers.
ImplSVData* pSVData = ImplGetSVData();
rtl::Reference<OpenGLContext> pContext = pSVData->maGDIData.mpLastContext;
if (pContext.is())
if( pContext.is() )
pContext->UnbindTextureFromFramebuffers( mnTexture );
if( mnOptStencil != 0 )
glDeleteRenderbuffers( 1, &mnOptStencil );
glDeleteTextures( 1, &mnTexture );
}
}
......@@ -279,6 +299,24 @@ int OpenGLTexture::GetHeight() const
return maRect.GetHeight();
}
bool OpenGLTexture::HasStencil() const
{
return mpImpl && mpImpl->mnOptStencil != 0;
}
GLuint OpenGLTexture::StencilId() const
{
return mpImpl ? mpImpl->mnOptStencil : 0;
}
GLuint OpenGLTexture::AddStencil()
{
if (mpImpl)
return mpImpl->AddStencil();
else
return 0;
}
void OpenGLTexture::GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool bInverted ) const
{
VCL_GL_INFO( "Getting coord " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
......@@ -349,6 +387,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()
......
......@@ -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() &&
......
......@@ -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());
......
......@@ -18,6 +18,8 @@
#include <vcl/bmpacc.hxx>
#include <vcl/graph.hxx>
#include <osl/thread.hxx>
#if defined(MACOSX)
#include <premac.h>
#include "OpenGLWrapper.hxx"
......@@ -1486,6 +1488,14 @@ void OpenGLContext::swapBuffers()
#elif defined( UNX )
glXSwapBuffers(m_aGLWin.dpy, m_aGLWin.win);
#endif
static bool bSleep = getenv("SAL_GL_SLEEP_ON_SWAP");
if (bSleep)
{
// half a second.
TimeValue aSleep( 0, 500*1000*1000 );
osl::Thread::wait( aSleep );
}
}
void OpenGLContext::sync()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment