From 5b9a2b73b0f47728c12759f7918da50dd9ef6f2a Mon Sep 17 00:00:00 2001 From: madsbuvi Date: Sun, 16 May 2021 13:04:30 +0200 Subject: [PATCH 1/2] Retain final draw callback as a member variable in ScreenshotManager, and do not call setFinalDrawCallback after init. --- apps/openmw/mwrender/screenshotmanager.cpp | 47 ++++++++++++++-------- apps/openmw/mwrender/screenshotmanager.hpp | 4 ++ 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index 89b225da4..60ac1eedd 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -36,8 +36,8 @@ namespace MWRender class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback { public: - NotifyDrawCompletedCallback(unsigned int frame) - : mDone(false), mFrame(frame) + NotifyDrawCompletedCallback() + : mDone(false), mFrame(0) { } @@ -59,6 +59,12 @@ namespace MWRender mCondition.wait(lock); } + void reset(unsigned int frame) + { + mDone = false; + mFrame = frame; + } + mutable std::condition_variable mCondition; mutable std::mutex mMutex; mutable bool mDone; @@ -94,8 +100,18 @@ namespace MWRender : mViewer(viewer) , mRootNode(rootNode) , mSceneRoot(sceneRoot) + , mDrawCompleteCallback(new NotifyDrawCompletedCallback) , mResourceSystem(resourceSystem) , mWater(water) + { + // Note: This assumes no other final draw callbacks are set anywhere and that this callback will remain set until the application exits. + // This works around *DrawCallback manipulation being unsafe in OSG >= 3.5.10 for release 0.47 + // If you need to set other final draw callbacks, read the comments of issue 6013 for a suggestion + // Ref https://gitlab.com/OpenMW/openmw/-/issues/6013 + mViewer->getCamera()->setFinalDrawCallback(mDrawCompleteCallback); + } + + ScreenshotManager::~ScreenshotManager() { } @@ -107,16 +123,10 @@ namespace MWRender tempDrw->setCullingActive(false); tempDrw->getOrCreateStateSet()->setRenderBinDetails(100, "RenderBin", osg::StateSet::USE_RENDERBIN_DETAILS); // so its after all scene bins but before POST_RENDER gui camera camera->addChild(tempDrw); - osg::ref_ptr callback (new NotifyDrawCompletedCallback(mViewer->getFrameStamp()->getFrameNumber())); - camera->setFinalDrawCallback(callback); - mViewer->eventTraversal(); - mViewer->updateTraversal(); - mViewer->renderingTraversals(); - callback->waitTillDone(); + traversalsAndWait(mViewer->getFrameStamp()->getFrameNumber()); // now that we've "used up" the current frame, get a fresh frame number for the next frame() following after the screenshot is completed mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); camera->removeChild(tempDrw); - camera->setFinalDrawCallback(nullptr); } bool ScreenshotManager::screenshot360(osg::Image* image) @@ -257,6 +267,15 @@ namespace MWRender return true; } + void ScreenshotManager::traversalsAndWait(unsigned int frame) + { + mDrawCompleteCallback->reset(frame); + mViewer->eventTraversal(); + mViewer->updateTraversal(); + mViewer->renderingTraversals(); + mDrawCompleteCallback->waitTillDone(); + } + void ScreenshotManager::renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h) { camera->setNodeMask(Mask_RenderToTexture); @@ -280,16 +299,10 @@ namespace MWRender mRootNode->addChild(camera); - // The draw needs to complete before we can copy back our image. - osg::ref_ptr callback (new NotifyDrawCompletedCallback(0)); - camera->setFinalDrawCallback(callback); - MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false); - mViewer->eventTraversal(); - mViewer->updateTraversal(); - mViewer->renderingTraversals(); - callback->waitTillDone(); + // The draw needs to complete before we can copy back our image. + traversalsAndWait(0); MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOff(); diff --git a/apps/openmw/mwrender/screenshotmanager.hpp b/apps/openmw/mwrender/screenshotmanager.hpp index 2ac50bdf0..373fe3be8 100644 --- a/apps/openmw/mwrender/screenshotmanager.hpp +++ b/apps/openmw/mwrender/screenshotmanager.hpp @@ -16,11 +16,13 @@ namespace Resource namespace MWRender { class Water; + class NotifyDrawCompletedCallback; class ScreenshotManager { public: ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, osg::ref_ptr sceneRoot, Resource::ResourceSystem* resourceSystem, Water* water); + ~ScreenshotManager(); void screenshot(osg::Image* image, int w, int h); bool screenshot360(osg::Image* image); @@ -29,9 +31,11 @@ namespace MWRender osg::ref_ptr mViewer; osg::ref_ptr mRootNode; osg::ref_ptr mSceneRoot; + osg::ref_ptr mDrawCompleteCallback; Resource::ResourceSystem* mResourceSystem; Water* mWater; + void traversalsAndWait(unsigned int frame); void renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h); void makeCubemapScreenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd()); }; From f3e17e7c52f3ac940e53f71f3aa6e5ac3700b4a8 Mon Sep 17 00:00:00 2001 From: madsbuvi Date: Sun, 16 May 2021 18:09:48 +0200 Subject: [PATCH 2/2] Don't redundantly call notify on every frame. --- apps/openmw/mwrender/screenshotmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index 60ac1eedd..af2970fd8 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -44,7 +44,7 @@ namespace MWRender void operator () (osg::RenderInfo& renderInfo) const override { std::lock_guard lock(mMutex); - if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame) + if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame && !mDone) { mDone = true; mCondition.notify_one();