From Colin McDonald, "Attached is an updated to osgViewer::PixelBufferWin32.

The win32 pbuffer implementation returned an error unless both the
WGL_ARB_pbuffer and the WGL_ARB_render_texture functions were present.
This was too restrictive, as a pbuffer can usefully be created without
render-to-texture, e.g. for use with glReadPixels.  The osg 1.2/Producer
pbuffers worked without RTT, and osgUtil::RenderStage has all the code to
handle both RTT and non-RTT pbuffers, doing a read and copy in the
latter case.

With these changes I have successfully tested the osgprerender example
on a graphics card which supports RTT, and one which doesn't.  Plus
tested in my own application.

In order to aid diagnostics I have also added more function status
return checks, and associated error messages.  I have included the win32
error text in all error messages output.  And there were some errors
with multi-threaded handling of "bind to texture" and a temporary window
context which I have corrected.

These is one (pre-existing) problem with multi-threaded use of pbuffers
in osgViewer & osgprerender, which I have not been able to fix.  A win32
device context (HDC) can only be destroyed from the thread that created
it.  The pbuffers for pre-render cameras are created in
osgUtil::RenderStage::runCameraSetUp, from the draw thread.  But
closeImplementation is normally invoked from the destructor in the main
application thread.  With the additional error messages I have added,
osgprerender will now output a couple of warnings from
osgViewer::PixelBufferWin32::closeImplementation() at exit, after
running multi-threaded on windows.  I think that is a good thing, to
highlight the problem.  I looked into fixing it in osgViewer::Renderer &
osgUtil::RenderStage, but it was too involved for me.  My own
application requirements are only single-threaded.

Unrelated fix - an uninitialised variable in
osg::GraphicsThread::FlushDeletedGLObjectsOperation().
"
This commit is contained in:
Robert Osfield 2008-03-04 16:39:44 +00:00
parent dd2b4ed83e
commit 9d0c950bb0
3 changed files with 139 additions and 103 deletions

View File

@ -84,6 +84,7 @@ class OSGVIEWER_EXPORT PixelBufferWin32 : public osg::GraphicsContext
bool _initialized;
bool _valid;
bool _realized;
int _boundBuffer;
};
}

View File

@ -112,7 +112,8 @@ void BlockAndFlushOperation::operator () (GraphicsContext*)
}
FlushDeletedGLObjectsOperation::FlushDeletedGLObjectsOperation(double availableTime, bool keep):
GraphicsOperation("FlushDeletedGLObjectsOperation",keep)
GraphicsOperation("FlushDeletedGLObjectsOperation",keep),
_availableTime(availableTime)
{
}

View File

@ -154,6 +154,36 @@ DECLARE_HANDLE(HPBUFFERARB);
namespace
{
static std::string sysError()
{
DWORD stat, err = GetLastError();
LPVOID lpMsgBuf = 0;
stat = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,\
0,NULL
);
std::ostringstream msgResult;
if ( stat > 0 && lpMsgBuf )
{
msgResult << (LPCTSTR)lpMsgBuf;
LocalFree( lpMsgBuf );
}
else
{
msgResult << "Error code " << err;
}
return msgResult.str();
}
static int __tempwnd_id = 0;
class TemporaryWindow: public osg::Referenced
{
@ -233,12 +263,14 @@ void TemporaryWindow::create()
_instance,
0)))
{
osg::notify(osg::WARN) << "PixelBufferWin32, could not create temporary window: " << sysError() << std::endl;
kill();
return;
}
if (!(_dc = GetDC(_handle)))
{
osg::notify(osg::WARN) << "PixelBufferWin32, could not get device context for temporary window: " << sysError() << std::endl;
kill();
return;
}
@ -267,12 +299,14 @@ void TemporaryWindow::create()
if (!SetPixelFormat(_dc, visual_id, &pfd))
{
osg::notify(osg::WARN) << "PixelBufferWin32, could not set pixel format for temporary window: " << sysError() << std::endl;
kill();
return;
}
if (!(_context = wglCreateContext(_dc)))
{
osg::notify(osg::WARN) << "PixelBufferWin32, could not get graphics context for temporary window: " << sysError() << std::endl;
kill();
return;
}
@ -313,7 +347,12 @@ void TemporaryWindow::kill()
bool TemporaryWindow::makeCurrent()
{
return wglMakeCurrent(_dc, _context) == TRUE ? true : false;
bool result = wglMakeCurrent(_dc, _context) == TRUE ? true : false;
if (!result)
{
osg::notify(osg::NOTICE) << "PixelBufferWin32, could not make the temporary window's context active: " << sysError() << std::endl;
}
return result;
}
class WGLExtensions : public osg::Referenced
@ -377,39 +416,28 @@ WGLExtensions::~WGLExtensions()
bool WGLExtensions::isValid()
{
return (wglCreatePbufferARB && wglGetPbufferDCARB && wglReleasePbufferDCARB && wglDestroyPbufferARB &&
wglQueryPbufferARB && wglBindTexImageARB && wglReleaseTexImageARB && wglChoosePixelFormatARB &&
wglMakeContextCurrentARB);
wglQueryPbufferARB && wglChoosePixelFormatARB );
}
osg::ref_ptr<TemporaryWindow> __default_wnd;
WGLExtensions *WGLExtensions::instance()
{
HGLRC context = wglGetCurrentContext();
bool nocontext = (context == 0);
if (nocontext || !__default_wnd.valid())
{
if (!__default_wnd.valid() || !__default_wnd->getHandle())
{
__default_wnd = new TemporaryWindow;
if (!__default_wnd ->getHandle())
{
osg::notify(osg::NOTICE) << "WGLExtensions: could not create and initialize the temporary window" << std::endl;
return 0;
}
}
context = __default_wnd->getContext();
if (!__default_wnd->makeCurrent())
{
osg::notify(osg::NOTICE) << "WGLExtensions: could not make the temporary window's context active" << std::endl;
}
}
// Get wgl function pointers for the current graphics context, or if there is no
// current context then use a temporary window.
if (!_instances[context])
{
_instances[context] = new WGLExtensions;
if ( context == 0 )
{
osg::ref_ptr<TemporaryWindow> tempWin= new TemporaryWindow;
tempWin->makeCurrent();
_instances[0] = new WGLExtensions;
}
else
{
_instances[context] = new WGLExtensions;
}
}
return _instances[context].get();
@ -427,7 +455,8 @@ PixelBufferWin32::PixelBufferWin32( osg::GraphicsContext::Traits* traits ):
_hglrc(0),
_initialized(false),
_valid(false),
_realized(false)
_realized(false),
_boundBuffer(0)
{
_traits = traits;
@ -454,24 +483,6 @@ PixelBufferWin32::~PixelBufferWin32()
{
closeImplementation();
}
static void doInternalError( char *msg )
{
DWORD err = GetLastError();
LPVOID lpMsgBuf = 0;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,\
0,NULL
);
std::string szMessage = std::string("osgViewer::PixelBufferWin32: Internal Error ")+std::string(msg);
MessageBox( NULL, (LPCTSTR)lpMsgBuf, szMessage.c_str(), MB_OK | MB_ICONINFORMATION );
if (lpMsgBuf) LocalFree( lpMsgBuf );
}
void PixelBufferWin32::init()
{
@ -479,25 +490,6 @@ void PixelBufferWin32::init()
if (!_traits) return;
if (!_traits->pbuffer) return;
struct RestoreContext
{
RestoreContext()
{
_hdc = wglGetCurrentDC();
_hglrc = wglGetCurrentContext();
}
~RestoreContext()
{
if (_hdc)
{
wglMakeCurrent(_hdc,_hglrc);
}
}
protected:
HDC _hdc;
HGLRC _hglrc;
} restoreContext;
WGLExtensions* wgle = WGLExtensions::instance();
if (!wgle || !wgle->isValid())
@ -555,7 +547,7 @@ void PixelBufferWin32::init()
fAttribList.push_back(true);
}
if (_traits->target != 0)
if (_traits->target != 0 && wgle->wglBindTexImageARB )
{
// TODO: Cube Maps
if (_traits->target == GL_TEXTURE_RECTANGLE)
@ -621,24 +613,21 @@ void PixelBufferWin32::init()
_hwnd = reinterpret_cast<HWND>(wgle->wglCreatePbufferARB(hdc, format, _traits->width, _traits->height, &bAttribList[0]));
if (!_hwnd)
{
//doInternalError("wglCreatePbufferARB() failed");
osg::notify(osg::NOTICE) << "PixelBufferWin32::init(), Error: wglCreatePbufferARB failed" << std::endl;
osg::notify(osg::NOTICE) << "PixelBufferWin32::init, wglCreatePbufferARB error: " << sysError() << std::endl;
return ;
}
_hdc = wgle->wglGetPbufferDCARB(reinterpret_cast<HPBUFFERARB>(_hwnd));
if (!_hdc)
{
//doInternalError("wglGetPbufferDCARB() failed");
osg::notify(osg::NOTICE) << "PixelBufferWin32::init(), Error: wglGetPbufferDCARB failed" << std::endl;
osg::notify(osg::NOTICE) << "PixelBufferWin32::init, wglGetPbufferDCARB error: " << sysError() << std::endl;
return;
}
_hglrc = wglCreateContext(_hdc);
if (!_hglrc)
{
//doInternalError("wglCreateContext() failed");
osg::notify(osg::NOTICE) << "PixelBufferWin32::init(), Error: wglCreateContext failed" << std::endl;
osg::notify(osg::NOTICE) << "PixelBufferWin32::init, wglCreateContext error: " << sysError() << std::endl;
return;
}
@ -692,7 +681,10 @@ bool PixelBufferWin32::realizeImplementation()
}
}
wglShareLists(hglrc, _hglrc);
if ( !wglShareLists(hglrc, _hglrc) )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32::realizeImplementation, wglShareLists error: " << sysError() << std::endl;
}
}
_realized = true;
@ -703,13 +695,28 @@ void PixelBufferWin32::closeImplementation()
{
if (_hwnd)
{
wglDeleteContext(_hglrc);
WGLExtensions* wgle = WGLExtensions::instance();
wglMakeCurrent(NULL,NULL);
if ( !wglDeleteContext(_hglrc) )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32::closeImplementation, wglDeleteContext error: " << sysError() << std::endl;
}
if (wgle && wgle->isValid())
{
wgle->wglReleasePbufferDCARB(reinterpret_cast<HPBUFFERARB>(_hwnd), _hdc);
wgle->wglDestroyPbufferARB(reinterpret_cast<HPBUFFERARB>(_hwnd));
// Note that closeImplementation() should only be called from the same thread as created the pbuffer,
// otherwise these routines will return an error.
if ( !wgle->wglReleasePbufferDCARB(reinterpret_cast<HPBUFFERARB>(_hwnd), _hdc) )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32::closeImplementation, wglReleasePbufferDCARB error: " << sysError() << std::endl;
}
if ( !wgle->wglDestroyPbufferARB(reinterpret_cast<HPBUFFERARB>(_hwnd)) )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32::closeImplementation, wglDestroyPbufferARB error: " << sysError() << std::endl;
}
}
}
_valid = false;
@ -721,36 +728,50 @@ void PixelBufferWin32::closeImplementation()
bool PixelBufferWin32::makeCurrentImplementation()
{
WGLExtensions* wgle = WGLExtensions::instance();
if (!wgle) return false;
if (_traits->target != 0)
{
wgle->wglReleaseTexImageARB(reinterpret_cast<HPBUFFERARB>(_hwnd), WGL_FRONT_LEFT_ARB);
wgle->wglReleaseTexImageARB(reinterpret_cast<HPBUFFERARB>(_hwnd), WGL_BACK_LEFT_ARB);
}
bool result = wglMakeCurrent(_hdc, _hglrc)==TRUE?true:false;
if (!result)
{
//doInternalError("wglMakeCurrent() failed");
osg::notify(osg::NOTICE) << "PixelBufferWin32::makeCurrentImplementation(), failed" << std::endl;
osg::notify(osg::NOTICE) << "PixelBufferWin32::makeCurrentImplementation, wglMakeCurrent error: " << sysError() << std::endl;
}
// If the pbuffer is bound to a texture then release it. This operation requires a current context, so
// do it after the MakeCurrent.
if ( _boundBuffer!=0 )
{
WGLExtensions* wgle = WGLExtensions::instance();
if ( wgle && wgle->wglReleaseTexImageARB )
{
if ( !wgle->wglReleaseTexImageARB(reinterpret_cast<HPBUFFERARB>(_hwnd), _boundBuffer) )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32::makeCurrentImplementation, wglReleaseTexImageARB error: " << sysError() << std::endl;
}
_boundBuffer=0;
}
}
return result;
}
bool PixelBufferWin32::makeContextCurrentImplementation( GraphicsContext* readContext )
{
WGLExtensions* wgle = WGLExtensions::instance();
if ( !wgle || !wgle->wglMakeContextCurrentARB )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32, wglMakeContextCurrentARB not available" << std::endl;
return false;
}
GraphicsWindowWin32* graphicsWindowWin32 = dynamic_cast<GraphicsWindowWin32*>(readContext);
if (graphicsWindowWin32)
{
if (WGLExtensions::instance()->wglMakeContextCurrentARB(_hdc, graphicsWindowWin32->getHDC(), _hglrc))
return true;
return wgle->wglMakeContextCurrentARB(_hdc, graphicsWindowWin32->getHDC(), _hglrc);
}
PixelBufferWin32* pixelBufferWin32 = dynamic_cast<PixelBufferWin32*>(_traits->sharedContext);
if (pixelBufferWin32)
{
if (WGLExtensions::instance()->wglMakeContextCurrentARB(_hdc, pixelBufferWin32->getHDC(), _hglrc))
return true;
return wgle->wglMakeContextCurrentARB(_hdc, pixelBufferWin32->getHDC(), _hglrc);
}
return false;
}
@ -768,28 +789,41 @@ bool PixelBufferWin32::releaseContextImplementation()
void PixelBufferWin32::bindPBufferToTextureImplementation( GLenum buffer )
{
bool result;
WGLExtensions* wgle = WGLExtensions::instance();
if (!wgle) return;
if ( !wgle || !wgle->wglBindTexImageARB )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32, wglBindTexImageARB not available" << std::endl;
return;
}
int bindBuffer;
switch (buffer)
{
case GL_BACK:
result = wgle->wglBindTexImageARB(reinterpret_cast<HPBUFFERARB>(_hwnd), WGL_BACK_LEFT_ARB);
bindBuffer = WGL_BACK_LEFT_ARB;
break;
case GL_FRONT:
result = wgle->wglBindTexImageARB(reinterpret_cast<HPBUFFERARB>(_hwnd), WGL_FRONT_LEFT_ARB);
bindBuffer = WGL_FRONT_LEFT_ARB;
break;
default:
result = wgle->wglBindTexImageARB(reinterpret_cast<HPBUFFERARB>(_hwnd), static_cast<GLenum>(buffer));
}
if (!result)
{
//doInternalError("wglBindTexImageARB() failed");
osg::notify(osg::NOTICE) << "PixelBufferWin32::wglBindTexImageARB(), failed" << std::endl;
bindBuffer = static_cast<int>(buffer);
}
if ( bindBuffer != _boundBuffer )
{
if ( _boundBuffer != 0 && !wgle->wglReleaseTexImageARB(reinterpret_cast<HPBUFFERARB>(_hwnd), _boundBuffer) )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32::bindPBufferToTextureImplementation, wglReleaseTexImageARB error: " << sysError() << std::endl;
}
if ( !wgle->wglBindTexImageARB(reinterpret_cast<HPBUFFERARB>(_hwnd), bindBuffer) )
{
osg::notify(osg::NOTICE) << "PixelBufferWin32::bindPBufferToTextureImplementation, wglBindTexImageARB error: " << sysError() << std::endl;
}
_boundBuffer = bindBuffer;
}
}
void PixelBufferWin32::swapBuffersImplementation()