From 68f37c4dcb25250cad56bb78faab7582a71f6048 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 10 Nov 2010 16:58:58 +0000 Subject: [PATCH] From Tim More and Robert Osfield, implementation of ARB_timer_query based GPU timing stats syncronization. Initial email from Tim : "I've implemented using a timestamp, available with ARB_timer_query and OpenGL 3.3, to gather GPU stats. This is nice because it can accurately fix the GPU draw time with respect to the other times on the stats graph, rather than having to estimate the wall time of the end of GPU drawing. This also prevents anomalies like the GPU phase starting before the draw phase..." Changes to Tim's submission by Robert: Removal of need for swap buffer callback in ViewerBase.cpp, by integrating a osg::State::frameCompleted() method that does the stats timing collection. Introduction of a GraphicsContext::swapBuffersCallbackOrImplementation() method that calls the State::frameCompleted() and the swap buffers callback or the swapImplementation as required. --- include/osg/Drawable | 11 +- include/osg/GraphicsContext | 35 +++++- include/osg/State | 31 ++++- include/osgViewer/Renderer | 34 ++--- src/osg/Drawable.cpp | 26 +++- src/osg/GraphicsContext.cpp | 6 +- src/osg/GraphicsThread.cpp | 2 +- src/osg/State.cpp | 85 +++++-------- src/osgViewer/Renderer.cpp | 242 +++++++++++++++++++++++++++++++----- 9 files changed, 352 insertions(+), 120 deletions(-) diff --git a/include/osg/Drawable b/include/osg/Drawable index 0f532aec3..3b6b235f9 100644 --- a/include/osg/Drawable +++ b/include/osg/Drawable @@ -45,6 +45,7 @@ #ifndef GL_TIME_ELAPSED #define GL_TIME_ELAPSED 0x88BF + #define GL_TIMESTAMP 0x8E28 #endif #ifndef GL_QUERY_RESULT @@ -607,7 +608,8 @@ class OSG_EXPORT Drawable : public Object void setTimerQuerySupported(bool flag) { _isTimerQuerySupported = flag; } bool isTimerQuerySupported() const { return _isTimerQuerySupported; } - + void setARBTimerQuerySupported(bool flag) { _isARBTimerQuerySupported = flag; } + bool isARBTimerQuerySupported() { return _isARBTimerQuerySupported; } void glSecondaryColor3ubv(const GLubyte* coord) const; void glSecondaryColor3fv(const GLfloat* coord) const; @@ -661,11 +663,13 @@ class OSG_EXPORT Drawable : public Object void glGenQueries(GLsizei n, GLuint *ids) const; void glBeginQuery(GLenum target, GLuint id) const; void glEndQuery(GLenum target) const; + void glQueryCounter(GLuint id, GLenum target) const; GLboolean glIsQuery(GLuint id) const; void glDeleteQueries(GLsizei n, const GLuint *ids) const; void glGetQueryObjectiv(GLuint id, GLenum pname, GLint *params) const; void glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) const; void glGetQueryObjectui64v(GLuint id, GLenum pname, GLuint64EXT *params) const; + void glGetInteger64v(GLenum pname, GLint64EXT *params) const; protected: @@ -715,10 +719,12 @@ class OSG_EXPORT Drawable : public Object typedef GLboolean (GL_APIENTRY *IsQueryProc) (GLuint id); typedef void (GL_APIENTRY *BeginQueryProc) (GLenum target, GLuint id); typedef void (GL_APIENTRY *EndQueryProc) (GLenum target); + typedef void (GL_APIENTRY *QueryCounterProc)(GLuint id, GLenum target); typedef void (GL_APIENTRY *GetQueryivProc) (GLenum target, GLenum pname, GLint *params); typedef void (GL_APIENTRY *GetQueryObjectivProc) (GLuint id, GLenum pname, GLint *params); typedef void (GL_APIENTRY *GetQueryObjectuivProc) (GLuint id, GLenum pname, GLuint *params); typedef void (GL_APIENTRY *GetQueryObjectui64vProc) (GLuint id, GLenum pname, GLuint64EXT *params); + typedef void (GL_APIENTRY *GetInteger64vProc) (GLenum pname, GLint64EXT *params); ~Extensions() {} @@ -729,6 +735,7 @@ class OSG_EXPORT Drawable : public Object bool _isOcclusionQuerySupported; bool _isARBOcclusionQuerySupported; bool _isTimerQuerySupported; + bool _isARBTimerQuerySupported; FogCoordProc _glFogCoordfv; @@ -785,10 +792,12 @@ class OSG_EXPORT Drawable : public Object IsQueryProc _gl_is_query_arb; BeginQueryProc _gl_begin_query_arb; EndQueryProc _gl_end_query_arb; + QueryCounterProc _glQueryCounter; GetQueryivProc _gl_get_queryiv_arb; GetQueryObjectivProc _gl_get_query_objectiv_arb; GetQueryObjectuivProc _gl_get_query_objectuiv_arb; GetQueryObjectui64vProc _gl_get_query_objectui64v; + GetInteger64vProc _glGetInteger64v; }; diff --git a/include/osg/GraphicsContext b/include/osg/GraphicsContext index 3f8a2d9aa..040e35b7b 100644 --- a/include/osg/GraphicsContext +++ b/include/osg/GraphicsContext @@ -392,6 +392,34 @@ class OSG_EXPORT GraphicsContext : public Object * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */ virtual void bindPBufferToTextureImplementation(GLenum buffer) = 0; + struct SwapCallback : public osg::Referenced + { + virtual void swapBuffersImplementation(GraphicsContext* gc) = 0; + }; + /** Set the swap callback which overrides the + * GraphicsContext::swapBuffersImplementation(), allowing + * developers to provide custom behavior for swap. + * The callback must call + * GraphicsContext::swapBuffersImplementation() */ + void setSwapCallback(SwapCallback* rc) { _swapCallback = rc; } + + /** Get the swap callback which overrides the GraphicsContext::swapBuffersImplementation().*/ + SwapCallback* getSwapCallback() { return _swapCallback.get(); } + + /** Get the const swap callback which overrides the GraphicsContext::swapBuffersImplementation().*/ + const SwapCallback* getSwapCallback() const { return _swapCallback.get(); } + + /** convinience method for handling whether to call swapbuffers callback or the standard context swapBuffersImplementation. + * swapBuffersCallbackOrImplemenation() is called by swapBuffers() and osg::SwapBuffersOperation, end users should normally + * call swapBuffers() rather than swapBuffersCallbackOrImplemenation(). */ + void swapBuffersCallbackOrImplemenation() + { + if (_state.valid()) _state->frameCompleted(); + + if (_swapCallback.valid()) _swapCallback->swapBuffersImplementation(this); + else swapBuffersImplementation(); + } + /** Swap the front and back buffers implementation. * Pure virtual - must be implemented by concrete implementations of GraphicsContext. */ virtual void swapBuffersImplementation() = 0; @@ -411,14 +439,14 @@ class OSG_EXPORT GraphicsContext : public Object virtual void resizedImplementation(GraphicsContext* gc, int x, int y, int width, int height) = 0; }; - /** Set the resized callback which overrides the GraphicsContext::resizedImplementation(), allow developers to provide custom behavior + /** Set the resized callback which overrides the GraphicsConext::realizedImplementation(), allow developers to provide custom behavior * in response to a window being resized.*/ void setResizedCallback(ResizedCallback* rc) { _resizedCallback = rc; } - /** Get the resized callback which overrides the GraphicsContext::resizedImplementation().*/ + /** Get the resized callback which overrides the GraphicsConext::realizedImplementation().*/ ResizedCallback* getResizedCallback() { return _resizedCallback.get(); } - /** Get the const resized callback which overrides the GraphicsContext::resizedImplementation().*/ + /** Get the const resized callback which overrides the GraphicsConext::realizedImplementation().*/ const ResizedCallback* getResizedCallback() const { return _resizedCallback.get(); } /** resized implementation, by default resizes the viewports and aspect ratios the cameras associated with the graphics Window. */ @@ -479,6 +507,7 @@ class OSG_EXPORT GraphicsContext : public Object ref_ptr _graphicsThread; ref_ptr _resizedCallback; + ref_ptr _swapCallback; Timer_t _lastClearTick; }; diff --git a/include/osg/State b/include/osg/State index ddd8e0a21..f28f5715a 100644 --- a/include/osg/State +++ b/include/osg/State @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -1366,6 +1367,31 @@ class OSG_EXPORT State : public Referenced, public Observer /** get the helper class for dispatching osg::Arrays as OpenGL attribute data.*/ inline ArrayDispatchers& getArrayDispatchers() { return _arrayDispatchers; } + /** Support for synchronizing the system time and the timestamp + * counter available with ARB_timer_query. Note that State + * doesn't update these values itself. + */ + Timer_t getStartTick() const { return _startTick; } + void setStartTick(Timer_t tick) { _startTick = tick; } + Timer_t getGpuTick() const { return _gpuTick; } + + double getGpuTime() const + { + return osg::Timer::instance()->delta_s(_startTick, _gpuTick); + } + GLuint64EXT getGpuTimestamp() const { return _gpuTimestamp; } + + void setGpuTimestamp(Timer_t tick, GLuint64EXT timestamp) + { + _gpuTick = tick; + _gpuTimestamp = timestamp; + } + int getTimestampBits() const { return _timestampBits; } + void setTimestampBits(int bits) { _timestampBits = bits; } + + /** called by the GraphicsContext just before GraphicsContext::swapBuffersImplementation().*/ + virtual void frameCompleted(); + protected: virtual ~State(); @@ -1776,7 +1802,10 @@ class OSG_EXPORT State : public Referenced, public Observer GLBeginEndAdapter _glBeginEndAdapter; ArrayDispatchers _arrayDispatchers; - + Timer_t _startTick; + Timer_t _gpuTick; + GLuint64EXT _gpuTimestamp; + int _timestampBits; }; inline void State::pushModeList(ModeMap& modeMap,const StateSet::ModeList& modeList) diff --git a/include/osgViewer/Renderer b/include/osgViewer/Renderer index 200fb034d..c7a44e031 100644 --- a/include/osgViewer/Renderer +++ b/include/osgViewer/Renderer @@ -21,38 +21,23 @@ namespace osgViewer { -class OSGVIEWER_EXPORT OpenGLQuerySupport +class OSGVIEWER_EXPORT OpenGLQuerySupport : public osg::Referenced { public: OpenGLQuerySupport(); - typedef std::pair QueryFrameNumberPair; - typedef std::list QueryFrameNumberList; - typedef std::vector QueryList; - - void setStartTick(osg::Timer_t startTick) { _startTick = startTick; } - osg::Timer_t getStartTick() const { return _startTick; } - - void checkQuery(osg::Stats* stats); + virtual void checkQuery(osg::Stats* stats, osg::State* state, + osg::Timer_t startTick) = 0; - GLuint createQueryObject(); - void beginQuery(int frameNumber); - void endQuery(); - void initialize(osg::State* state); - + virtual void beginQuery(int frameNumber, osg::State* state) = 0; + virtual void endQuery(osg::State* state) = 0; + virtual void initialize(osg::State* state, osg::Timer_t startTick); protected: - osg::Timer_t _startTick; - bool _initialized; - bool _timerQuerySupported; const osg::Drawable::Extensions* _extensions; - QueryFrameNumberList _queryFrameNumberList; - QueryList _availableQueryObjects; - double _previousQueryTime; - }; -class OSGVIEWER_EXPORT Renderer : public osg::GraphicsOperation, public OpenGLQuerySupport +class OSGVIEWER_EXPORT Renderer : public osg::GraphicsOperation { public: @@ -132,7 +117,7 @@ class OSGVIEWER_EXPORT Renderer : public osg::GraphicsOperation, public OpenGLQu bool getCameraRequiresSetUp() const; protected: - + void initialize(osg::State* state); virtual ~Renderer(); virtual void updateSceneView(osgUtil::SceneView* sceneView); @@ -178,6 +163,9 @@ class OSGVIEWER_EXPORT Renderer : public osg::GraphicsOperation, public OpenGLQu ThreadSafeQueue _availableQueue; ThreadSafeQueue _drawQueue; + bool _initialized; + osg::ref_ptr _querySupport; + osg::Timer_t _startTick; }; } diff --git a/src/osg/Drawable.cpp b/src/osg/Drawable.cpp index e21f15328..0a77f03c0 100644 --- a/src/osg/Drawable.cpp +++ b/src/osg/Drawable.cpp @@ -844,6 +844,7 @@ Drawable::Extensions::Extensions(const Extensions& rhs): _isMultiTexSupported = rhs._isMultiTexSupported; _isOcclusionQuerySupported = rhs._isOcclusionQuerySupported; _isTimerQuerySupported = rhs._isTimerQuerySupported; + _isARBTimerQuerySupported = rhs._isARBTimerQuerySupported; _glFogCoordfv = rhs._glFogCoordfv; _glSecondaryColor3ubv = rhs._glSecondaryColor3ubv; @@ -880,6 +881,7 @@ Drawable::Extensions::Extensions(const Extensions& rhs): _gl_get_query_objectiv_arb = rhs._gl_get_query_objectiv_arb; _gl_get_query_objectuiv_arb = rhs._gl_get_query_objectuiv_arb; _gl_get_query_objectui64v = rhs._gl_get_query_objectui64v; + _glGetInteger64v = rhs._glGetInteger64v; } @@ -893,6 +895,7 @@ void Drawable::Extensions::lowestCommonDenominator(const Extensions& rhs) if (!rhs._isARBOcclusionQuerySupported) _isARBOcclusionQuerySupported = false; if (!rhs._isTimerQuerySupported) _isTimerQuerySupported = false; + if (!rhs._isARBTimerQuerySupported) _isARBTimerQuerySupported = false; if (!rhs._glFogCoordfv) _glFogCoordfv = 0; if (!rhs._glSecondaryColor3ubv) _glSecondaryColor3ubv = 0; @@ -939,6 +942,7 @@ void Drawable::Extensions::lowestCommonDenominator(const Extensions& rhs) if (!rhs._gl_get_query_objectiv_arb) _gl_get_query_objectiv_arb = 0; if (!rhs._gl_get_query_objectuiv_arb) _gl_get_query_objectuiv_arb = 0; if (!rhs._gl_get_query_objectui64v) _gl_get_query_objectui64v = 0; + if (!rhs._glGetInteger64v) _glGetInteger64v = 0; } void Drawable::Extensions::setupGLExtensions(unsigned int contextID) @@ -950,7 +954,8 @@ void Drawable::Extensions::setupGLExtensions(unsigned int contextID) _isOcclusionQuerySupported = osg::isGLExtensionSupported(contextID, "GL_NV_occlusion_query" ); _isARBOcclusionQuerySupported = OSG_GL3_FEATURES || osg::isGLExtensionSupported(contextID, "GL_ARB_occlusion_query" ); - _isTimerQuerySupported = osg::isGLExtensionSupported(contextID, "GL_EXT_timer_query" );; + _isTimerQuerySupported = osg::isGLExtensionSupported(contextID, "GL_EXT_timer_query" ); + _isARBTimerQuerySupported = osg::isGLExtensionSupported(contextID, "GL_ARB_timer_query"); setGLExtensionFuncPtr(_glFogCoordfv, "glFogCoordfv","glFogCoordfvEXT"); @@ -1008,6 +1013,8 @@ void Drawable::Extensions::setupGLExtensions(unsigned int contextID) setGLExtensionFuncPtr(_gl_get_query_objectiv_arb, "glGetQueryObjectiv","glGetQueryObjectivARB"); setGLExtensionFuncPtr(_gl_get_query_objectuiv_arb, "glGetQueryObjectuiv","glGetQueryObjectuivARB"); setGLExtensionFuncPtr(_gl_get_query_objectui64v, "glGetQueryObjectui64v","glGetQueryObjectui64vEXT"); + setGLExtensionFuncPtr(_glQueryCounter, "glQueryCounter"); + setGLExtensionFuncPtr(_glGetInteger64v, "glGetInteger64v"); } void Drawable::Extensions::glFogCoordfv(const GLfloat* coord) const @@ -1471,6 +1478,14 @@ void Drawable::Extensions::glEndQuery(GLenum target) const OSG_WARN << "Error: glEndQuery not supported by OpenGL driver" << std::endl; } +void Drawable::Extensions::glQueryCounter(GLuint id, GLenum target) const +{ + if (_glQueryCounter) + _glQueryCounter(id, target); + else + OSG_WARN << "Error: glQueryCounter not supported by OpenGL driver\n"; +} + GLboolean Drawable::Extensions::glIsQuery(GLuint id) const { if (_gl_is_query_arb) return _gl_is_query_arb(id); @@ -1510,3 +1525,12 @@ void Drawable::Extensions::glGetQueryObjectui64v(GLuint id, GLenum pname, GLuint else OSG_WARN << "Error: glGetQueryObjectui64v not supported by OpenGL driver" << std::endl; } + +void Drawable::Extensions::glGetInteger64v(GLenum pname, GLint64EXT *params) + const +{ + if (_glGetInteger64v) + _glGetInteger64v(pname, params); + else + OSG_WARN << "Error: glGetInteger64v not supported by OpenGL driver\n"; +} diff --git a/src/osg/GraphicsContext.cpp b/src/osg/GraphicsContext.cpp index e12b63bf5..ceba69df6 100644 --- a/src/osg/GraphicsContext.cpp +++ b/src/osg/GraphicsContext.cpp @@ -617,7 +617,7 @@ void GraphicsContext::swapBuffers() { if (isCurrent()) { - swapBuffersImplementation(); + swapBuffersCallbackOrImplemenation(); clear(); } else if (_graphicsThread.valid() && @@ -628,13 +628,11 @@ void GraphicsContext::swapBuffers() else { makeCurrent(); - swapBuffersImplementation(); + swapBuffersCallbackOrImplemenation(); clear(); } } - - void GraphicsContext::createGraphicsThread() { if (!_graphicsThread) diff --git a/src/osg/GraphicsThread.cpp b/src/osg/GraphicsThread.cpp index cac0ea4a8..3ea29aa5b 100644 --- a/src/osg/GraphicsThread.cpp +++ b/src/osg/GraphicsThread.cpp @@ -55,7 +55,7 @@ void GraphicsOperation::operator () (Object* object) void SwapBuffersOperation::operator () (GraphicsContext* context) { - context->swapBuffersImplementation(); + context->swapBuffersCallbackOrImplemenation(); context->clear(); } diff --git a/src/osg/State.cpp b/src/osg/State.cpp index cebcf0d1b..366e59a44 100644 --- a/src/osg/State.cpp +++ b/src/osg/State.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -157,6 +158,11 @@ State::State(): _glBeginEndAdapter.setState(this); _arrayDispatchers.setState(this); + + _startTick = 0; + _gpuTick = 0; + _gpuTimestamp = 0; + _timestampBits = 0; } State::~State() @@ -910,6 +916,16 @@ void State::initializeExtensionProcs() _glMaxTextureCoords = 1; } + osg::Drawable::Extensions* extensions = osg::Drawable::getExtensions(getContextID(), true); + if (extensions && extensions->isARBTimerQuerySupported()) + { + GLint bits = 0; + extensions->glGetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS_ARB, &bits); + OSG_NOTICE << "timestamp query counter bits: " << bits << "\n"; + setTimestampBits(bits); + } + + _extensionProcsInitialized = true; } @@ -1568,60 +1584,17 @@ void State::print(std::ostream& fout) const fout<<(*itr)->getName()<<" "<<*itr< EnabledTexCoordArrayList; - typedef std::vector EnabledVertexAttribArrayList; - - EnabledArrayPair _vertexArray; - EnabledArrayPair _normalArray; - EnabledArrayPair _colorArray; - EnabledArrayPair _secondaryColorArray; - EnabledArrayPair _fogArray; - EnabledTexCoordArrayList _texCoordArrayList; - EnabledVertexAttribArrayList _vertexAttribArrayList; - - unsigned int _currentActiveTextureUnit; - unsigned int _currentClientActiveTextureUnit; - GLBufferObject* _currentVBO; - GLBufferObject* _currentEBO; - GLBufferObject* _currentPBO; - - mutable bool _isSecondaryColorSupportResolved; - mutable bool _isSecondaryColorSupported; - bool computeSecondaryColorSupported() const; - - mutable bool _isFogCoordSupportResolved; - mutable bool _isFogCoordSupported; - bool computeFogCoordSupported() const; - - mutable bool _isVertexBufferObjectSupportResolved; - mutable bool _isVertexBufferObjectSupported; - bool computeVertexBufferObjectSupported() const; -#endif - -#if 0 - unsigned int _dynamicObjectCount; - osg::ref_ptr _completeDynamicObjectRenderingCallback; - - GLBeginEndAdapter _glBeginEndAdapter; - ArrayDispatchers _arrayDispatchers; -#endif } + +void State::frameCompleted() +{ + + osg::Drawable::Extensions* extensions = osg::Drawable::getExtensions(getContextID(), true); + if (extensions && getTimestampBits()) + { + GLint64EXT timestamp; + extensions->glGetInteger64v(GL_TIMESTAMP, ×tamp); + setGpuTimestamp(osg::Timer::instance()->tick(), timestamp); + OSG_NOTICE<<"State::frameCompleted() setting time stamp."< QueryFrameNumberPair; + typedef std::list QueryFrameNumberList; + typedef std::vector QueryList; + + QueryFrameNumberList _queryFrameNumberList; + QueryList _availableQueryObjects; + double _previousQueryTime; +}; + + +EXTQuerySupport::EXTQuerySupport(): _previousQueryTime(0.0) { } -void OpenGLQuerySupport::checkQuery(osg::Stats* stats) +void EXTQuerySupport::checkQuery(osg::Stats* stats, osg::State* state, + osg::Timer_t startTick) { for(QueryFrameNumberList::iterator itr = _queryFrameNumberList.begin(); itr != _queryFrameNumberList.end(); @@ -59,7 +81,7 @@ void OpenGLQuerySupport::checkQuery(osg::Stats* stats) _extensions->glGetQueryObjectui64v(query, GL_QUERY_RESULT, &timeElapsed); double timeElapsedSeconds = double(timeElapsed)*1e-9; - double currentTime = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick()); + double currentTime = osg::Timer::instance()->delta_s(startTick, osg::Timer::instance()->tick()); double estimatedEndTime = (_previousQueryTime + currentTime) * 0.5; double estimatedBeginTime = estimatedEndTime - timeElapsedSeconds; @@ -77,10 +99,10 @@ void OpenGLQuerySupport::checkQuery(osg::Stats* stats) } } - _previousQueryTime = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick()); + _previousQueryTime = osg::Timer::instance()->delta_s(startTick, osg::Timer::instance()->tick()); } -GLuint OpenGLQuerySupport::createQueryObject() +GLuint EXTQuerySupport::createQueryObject() { if (_availableQueryObjects.empty()) { @@ -96,26 +118,169 @@ GLuint OpenGLQuerySupport::createQueryObject() } } -void OpenGLQuerySupport::beginQuery(int frameNumber) +void EXTQuerySupport::beginQuery(int frameNumber, osg::State* state) { GLuint query = createQueryObject(); _extensions->glBeginQuery(GL_TIME_ELAPSED, query); _queryFrameNumberList.push_back(QueryFrameNumberPair(query, frameNumber)); } -void OpenGLQuerySupport::endQuery() +void EXTQuerySupport::endQuery(osg::State* state) { _extensions->glEndQuery(GL_TIME_ELAPSED); } -void OpenGLQuerySupport::initialize(osg::State* state) +void OpenGLQuerySupport::initialize(osg::State* state, osg::Timer_t startTick) { - if (_initialized) return; - - _initialized = true; _extensions = osg::Drawable::getExtensions(state->getContextID(),true); - _timerQuerySupported = _extensions && _extensions->isTimerQuerySupported(); - _previousQueryTime = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick()); +} + +void EXTQuerySupport::initialize(osg::State* state, osg::Timer_t startTick) +{ + OpenGLQuerySupport::initialize(state, startTick); + _previousQueryTime = osg::Timer::instance()->delta_s(startTick, osg::Timer::instance()->tick()); + +} + +class ARBQuerySupport : public OpenGLQuerySupport +{ +public: + virtual void checkQuery(osg::Stats* stats, osg::State* state, + osg::Timer_t startTick); + + virtual void beginQuery(int frameNumber, osg::State* state); + virtual void endQuery(osg::State* state); + virtual void initialize(osg::State* state, osg::Timer_t startTick); +protected: + typedef std::pair QueryPair; + struct ActiveQuery { + ActiveQuery() : queries(0, 0), frameNumber(0) {} + ActiveQuery(GLuint start_, GLuint end_, int frameNumber_) + : queries(start_, end_), frameNumber(frameNumber_) + { + } + ActiveQuery(const QueryPair& queries_, int frameNumber_) + : queries(queries_), frameNumber(frameNumber_) + { + } + QueryPair queries; + int frameNumber; + }; + typedef std::list QueryFrameList; + typedef std::vector QueryList; + QueryFrameList _queryFrameList; + QueryList _availableQueryObjects; +}; + +void ARBQuerySupport::initialize(osg::State* state, osg::Timer_t startTick) +{ + OpenGLQuerySupport::initialize(state, startTick); +} + +void ARBQuerySupport::beginQuery(int frameNumber, osg::State* state) +{ + QueryPair query; + if (_availableQueryObjects.empty()) + { + _extensions->glGenQueries(1, &query.first); + _extensions->glGenQueries(1, &query.second); + } + else + { + query = _availableQueryObjects.back(); + _availableQueryObjects.pop_back(); + } + _extensions->glQueryCounter(query.first, GL_TIMESTAMP); + _queryFrameList.push_back(ActiveQuery(query, frameNumber)); +} + +void ARBQuerySupport::endQuery(osg::State* state) +{ + _extensions->glQueryCounter(_queryFrameList.back().queries.second, + GL_TIMESTAMP); +} + +void ARBQuerySupport::checkQuery(osg::Stats* stats, osg::State* state, + osg::Timer_t startTick) +{ + for(QueryFrameList::iterator itr = _queryFrameList.begin(); + itr != _queryFrameList.end(); + ) + { + GLint available = 0; + // If the end query is available, the begin query must be too. + _extensions->glGetQueryObjectiv(itr->queries.second, + GL_QUERY_RESULT_AVAILABLE, &available); + if (available) + { + QueryPair queries = itr->queries; + GLuint64EXT beginTimestamp = 0; + GLuint64EXT endTimestamp = 0; + _extensions->glGetQueryObjectui64v(queries.first, GL_QUERY_RESULT, + &beginTimestamp); + _extensions->glGetQueryObjectui64v(queries.second, GL_QUERY_RESULT, + &endTimestamp); + GLuint64EXT gpuTimestamp = state->getGpuTimestamp(); + // Have any of the timestamps wrapped around? + int tbits = state->getTimestampBits(); + if (tbits < 64) + { + // If the high bits on any of the timestamp bits are + // different then the counters may have wrapped. + const int hiShift = (tbits - 1); + const GLuint64EXT hiMask = 1 << hiShift; + const GLuint64EXT sum = (beginTimestamp >> hiShift) + + (endTimestamp >> hiShift) + (gpuTimestamp >> hiShift); + if (sum == 1 || sum == 2) { + const GLuint64EXT wrapAdd = 1 << tbits; + // Counter wrapped between begin and end? + if (beginTimestamp > endTimestamp) + { + endTimestamp += wrapAdd; + } + else if (gpuTimestamp < beginTimestamp + && beginTimestamp - gpuTimestamp > (hiMask >> 1)) + { + gpuTimestamp += wrapAdd; + } + else if (endTimestamp < gpuTimestamp + && gpuTimestamp - endTimestamp > (hiMask >> 1)) + { + beginTimestamp += wrapAdd; + endTimestamp += wrapAdd; + } + } + } + GLuint64EXT timeElapsed = endTimestamp - beginTimestamp; + double timeElapsedSeconds = double(timeElapsed)*1e-9; + double gpuTick = state->getGpuTime(); + double beginTime = 0.0; + double endTime = 0.0; + if (beginTimestamp > gpuTimestamp) + beginTime = gpuTick + + double(beginTimestamp - gpuTimestamp) * 1e-9; + else + beginTime = gpuTick + - double(gpuTimestamp - beginTimestamp) * 1e-9; + if (endTimestamp > gpuTimestamp) + endTime = gpuTick + + double(endTimestamp - gpuTimestamp) * 1e-9; + else + endTime = gpuTick + - double(gpuTimestamp - endTimestamp) * 1e-9; + stats->setAttribute(itr->frameNumber, "GPU draw begin time", + beginTime); + stats->setAttribute(itr->frameNumber, "GPU draw end time", endTime); + stats->setAttribute(itr->frameNumber, "GPU draw time taken", + timeElapsedSeconds); + itr = _queryFrameList.erase(itr); + _availableQueryObjects.push_back(queries); + } + else + { + ++itr; + } + } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -162,7 +327,6 @@ static OpenThreads::Mutex s_drawSerializerMutex; // Renderer Renderer::Renderer(osg::Camera* camera): osg::GraphicsOperation("Renderer",true), - OpenGLQuerySupport(), _targetFrameRate(100.0), _minimumTimeAvailableForGLCompileAndDeletePerFrame(0.001), _flushTimeRatio(0.5), @@ -170,7 +334,9 @@ Renderer::Renderer(osg::Camera* camera): _camera(camera), _done(false), _graphicsThreadDoesCull(true), - _compileOnNextDraw(true) + _compileOnNextDraw(true), + _initialized(false), + _startTick(0) { DEBUG_MESSAGE<<"Render::Render() "<getContextID(), true); + if (ext->isARBTimerQuerySupported()) + _querySupport = new ARBQuerySupport(); + else if (ext->isTimerQuerySupported()) + _querySupport = new EXTQuerySupport(); + if (_querySupport.valid()) + _querySupport->initialize(state, _startTick); + } +} + void Renderer::setGraphicsThreadDoesCull(bool flag) { if (_graphicsThreadDoesCull==flag) return; @@ -296,6 +477,7 @@ void Renderer::updateSceneView(osgUtil::SceneView* sceneView) sceneView->setDisplaySettings(ds); if (view) _startTick = view->getStartTick(); + if (state) state->setStartTick(_startTick); } void Renderer::compile() @@ -477,18 +659,18 @@ void Renderer::draw() state->getDynamicObjectRenderingCompletedCallback()->completed(state); } - bool acquireGPUStats = stats && _timerQuerySupported && stats->collectStats("gpu"); + bool acquireGPUStats = stats && _querySupport && stats->collectStats("gpu"); if (acquireGPUStats) { - checkQuery(stats); + _querySupport->checkQuery(stats, state, _startTick); } // do draw traversal if (acquireGPUStats) { - checkQuery(stats); - beginQuery(frameNumber); + _querySupport->checkQuery(stats, state, _startTick); + _querySupport->beginQuery(frameNumber, state); } osg::Timer_t beforeDrawTick; @@ -519,8 +701,8 @@ void Renderer::draw() if (acquireGPUStats) { - endQuery(); - checkQuery(stats); + _querySupport->endQuery(state); + _querySupport->checkQuery(stats, state, _startTick); } //glFlush(); @@ -585,11 +767,11 @@ void Renderer::cull_draw() initialize(state); } - bool acquireGPUStats = stats && _timerQuerySupported && stats->collectStats("gpu"); + bool acquireGPUStats = stats && _querySupport && stats->collectStats("gpu"); if (acquireGPUStats) { - checkQuery(stats); + _querySupport->checkQuery(stats, state, _startTick); } // do cull traversal @@ -626,8 +808,8 @@ void Renderer::cull_draw() // do draw traversal if (acquireGPUStats) { - checkQuery(stats); - beginQuery(frameNumber); + _querySupport->checkQuery(stats, state, _startTick); + _querySupport->beginQuery(frameNumber, state); } osg::Timer_t beforeDrawTick; @@ -656,8 +838,8 @@ void Renderer::cull_draw() if (acquireGPUStats) { - endQuery(); - checkQuery(stats); + _querySupport->endQuery(state); + _querySupport->checkQuery(stats, state, _startTick); } osg::Timer_t afterDrawTick = osg::Timer::instance()->tick();