From dc5371d157833b5cc834cfcaf5e460dc3af26504 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 18 Feb 2024 22:28:33 +0100 Subject: [PATCH 1/4] Remove unused RipplesSurface::State::mOffset --- apps/openmw/mwrender/ripples.cpp | 1 - apps/openmw/mwrender/ripples.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/openmw/mwrender/ripples.cpp b/apps/openmw/mwrender/ripples.cpp index dea372666e..6dcee1c9bd 100644 --- a/apps/openmw/mwrender/ripples.cpp +++ b/apps/openmw/mwrender/ripples.cpp @@ -136,7 +136,6 @@ namespace MWRender osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos; mLastPlayerPos = mCurrentPlayerPos; mState[frameId].mPaused = mPaused; - mState[frameId].mOffset = offset; mState[frameId].mStateset->getUniform("positionCount")->set(static_cast(mPositionCount)); mState[frameId].mStateset->getUniform("offset")->set(offset); diff --git a/apps/openmw/mwrender/ripples.hpp b/apps/openmw/mwrender/ripples.hpp index 0d5b055eb5..6dd48e221f 100644 --- a/apps/openmw/mwrender/ripples.hpp +++ b/apps/openmw/mwrender/ripples.hpp @@ -54,7 +54,6 @@ namespace MWRender struct State { - osg::Vec2f mOffset; osg::ref_ptr mStateset; bool mPaused = true; }; From 56e69cf7a2626ce6c196c5543db100f834481765 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 18 Feb 2024 22:30:51 +0100 Subject: [PATCH 2/4] Make some RipplesSurface members private --- apps/openmw/mwrender/ripples.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/ripples.hpp b/apps/openmw/mwrender/ripples.hpp index 6dd48e221f..41ca055174 100644 --- a/apps/openmw/mwrender/ripples.hpp +++ b/apps/openmw/mwrender/ripples.hpp @@ -50,6 +50,10 @@ namespace MWRender // e.g. texel to cell unit ratio static constexpr float mWorldScaleFactor = 2.5; + private: + void setupFragmentPipeline(); + void setupComputePipeline(); + Resource::ResourceSystem* mResourceSystem; struct State @@ -63,10 +67,6 @@ namespace MWRender std::array mState; - private: - void setupFragmentPipeline(); - void setupComputePipeline(); - osg::Vec2f mCurrentPlayerPos; osg::Vec2f mLastPlayerPos; From 3b01e209b1c180841eda565e491ad4680412cae7 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 18 Feb 2024 22:31:50 +0100 Subject: [PATCH 3/4] Use proper names for static members --- apps/openmw/mwrender/ripples.cpp | 20 ++++++++++---------- apps/openmw/mwrender/ripples.hpp | 4 ++-- apps/openmw/mwrender/water.cpp | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/ripples.cpp b/apps/openmw/mwrender/ripples.cpp index 6dcee1c9bd..7017101011 100644 --- a/apps/openmw/mwrender/ripples.cpp +++ b/apps/openmw/mwrender/ripples.cpp @@ -63,7 +63,7 @@ namespace MWRender stateset->addUniform(new osg::Uniform("offset", osg::Vec2f())); stateset->addUniform(new osg::Uniform("positionCount", 0)); stateset->addUniform(new osg::Uniform(osg::Uniform::Type::FLOAT_VEC3, "positions", 100)); - stateset->setAttributeAndModes(new osg::Viewport(0, 0, RipplesSurface::mRTTSize, RipplesSurface::mRTTSize)); + stateset->setAttributeAndModes(new osg::Viewport(0, 0, RipplesSurface::sRTTSize, RipplesSurface::sRTTSize)); mState[i].mStateset = stateset; } @@ -78,7 +78,7 @@ namespace MWRender texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER); texture->setBorderColor(osg::Vec4(0, 0, 0, 0)); - texture->setTextureSize(mRTTSize, mRTTSize); + texture->setTextureSize(sRTTSize, sRTTSize); mTextures[i] = texture; @@ -99,7 +99,7 @@ namespace MWRender { auto& shaderManager = mResourceSystem->getSceneManager()->getShaderManager(); - Shader::ShaderManager::DefineMap defineMap = { { "ripple_map_size", std::to_string(mRTTSize) + ".0" } }; + Shader::ShaderManager::DefineMap defineMap = { { "ripple_map_size", std::to_string(sRTTSize) + ".0" } }; osg::ref_ptr vertex = shaderManager.getShader("fullscreen_tri.vert", {}, osg::Shader::VERTEX); @@ -132,7 +132,7 @@ namespace MWRender const ESM::Position& playerPos = player.getRefData().getPosition(); mCurrentPlayerPos = osg::Vec2f( - std::floor(playerPos.pos[0] / mWorldScaleFactor), std::floor(playerPos.pos[1] / mWorldScaleFactor)); + std::floor(playerPos.pos[0] / sWorldScaleFactor), std::floor(playerPos.pos[1] / sWorldScaleFactor)); osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos; mLastPlayerPos = mCurrentPlayerPos; mState[frameId].mPaused = mPaused; @@ -145,9 +145,9 @@ namespace MWRender { osg::Vec3f pos = mPositions[i] - osg::Vec3f( - mCurrentPlayerPos.x() * mWorldScaleFactor, mCurrentPlayerPos.y() * mWorldScaleFactor, 0.0) - + osg::Vec3f(mRTTSize * mWorldScaleFactor / 2, mRTTSize * mWorldScaleFactor / 2, 0.0); - pos /= mWorldScaleFactor; + mCurrentPlayerPos.x() * sWorldScaleFactor, mCurrentPlayerPos.y() * sWorldScaleFactor, 0.0) + + osg::Vec3f(sRTTSize * sWorldScaleFactor / 2, sRTTSize * sWorldScaleFactor / 2, 0.0); + pos /= sWorldScaleFactor; positions->setElement(i, pos); } positions->dirty(); @@ -195,7 +195,7 @@ namespace MWRender bindImage(mTextures[1], 0, GL_WRITE_ONLY_ARB); bindImage(mTextures[0], 1, GL_READ_ONLY_ARB); - ext.glDispatchCompute(mRTTSize / 16, mRTTSize / 16, 1); + ext.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1); ext.glMemoryBarrier(GL_ALL_BARRIER_BITS); } else @@ -217,7 +217,7 @@ namespace MWRender bindImage(mTextures[0], 0, GL_WRITE_ONLY_ARB); bindImage(mTextures[1], 1, GL_READ_ONLY_ARB); - ext.glDispatchCompute(mRTTSize / 16, mRTTSize / 16, 1); + ext.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1); ext.glMemoryBarrier(GL_ALL_BARRIER_BITS); } else @@ -270,7 +270,7 @@ namespace MWRender setReferenceFrame(osg::Camera::ABSOLUTE_RF); setNodeMask(Mask_RenderToTexture); setClearMask(GL_NONE); - setViewport(0, 0, RipplesSurface::mRTTSize, RipplesSurface::mRTTSize); + setViewport(0, 0, RipplesSurface::sRTTSize, RipplesSurface::sRTTSize); addChild(mRipples); setCullingActive(false); setImplicitBufferAttachmentMask(0, 0); diff --git a/apps/openmw/mwrender/ripples.hpp b/apps/openmw/mwrender/ripples.hpp index 41ca055174..3559c164a6 100644 --- a/apps/openmw/mwrender/ripples.hpp +++ b/apps/openmw/mwrender/ripples.hpp @@ -46,9 +46,9 @@ namespace MWRender void releaseGLObjects(osg::State* state) const override; - static constexpr size_t mRTTSize = 1024; + static constexpr size_t sRTTSize = 1024; // e.g. texel to cell unit ratio - static constexpr float mWorldScaleFactor = 2.5; + static constexpr float sWorldScaleFactor = 2.5; private: void setupFragmentPipeline(); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 9fdb0583a2..283ec85c48 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -704,8 +704,8 @@ namespace MWRender defineMap["refraction_enabled"] = std::string(mRefraction ? "1" : "0"); const int rippleDetail = Settings::water().mRainRippleDetail; defineMap["rain_ripple_detail"] = std::to_string(rippleDetail); - defineMap["ripple_map_world_scale"] = std::to_string(RipplesSurface::mWorldScaleFactor); - defineMap["ripple_map_size"] = std::to_string(RipplesSurface::mRTTSize) + ".0"; + defineMap["ripple_map_world_scale"] = std::to_string(RipplesSurface::sWorldScaleFactor); + defineMap["ripple_map_size"] = std::to_string(RipplesSurface::sRTTSize) + ".0"; Stereo::shaderStereoDefines(defineMap); From c9b4c8632a4bcddb260324bdc53b747c23d7c881 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 18 Feb 2024 23:14:40 +0100 Subject: [PATCH 4/4] Update ripples surface only when there is need to do so This depends on the difference between FPS which is dynamic and ripples update frequency which is contant. If FPS > ripples update frequency, some frames do nothing. If FPS <= ripples update frequency each frame runs shaders once. Update offset, possitions shader uniforms only when it will be run. --- apps/openmw/mwrender/ripples.cpp | 147 +++++++++++++++++-------------- apps/openmw/mwrender/ripples.hpp | 18 ++-- 2 files changed, 93 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwrender/ripples.cpp b/apps/openmw/mwrender/ripples.cpp index 7017101011..94135eeec5 100644 --- a/apps/openmw/mwrender/ripples.cpp +++ b/apps/openmw/mwrender/ripples.cpp @@ -119,58 +119,83 @@ namespace MWRender nullptr, shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE)); } + void RipplesSurface::updateState(const osg::FrameStamp& frameStamp, State& state) + { + state.mPaused = mPaused; + + if (mPaused) + return; + + constexpr double updateFrequency = 60.0; + constexpr double updatePeriod = 1.0 / updateFrequency; + + const double simulationTime = frameStamp.getSimulationTime(); + const double frameDuration = simulationTime - mLastSimulationTime; + mLastSimulationTime = simulationTime; + + mRemainingWaveTime += frameDuration; + const double ticks = std::floor(mRemainingWaveTime * updateFrequency); + mRemainingWaveTime -= ticks * updatePeriod; + + if (ticks == 0) + { + state.mPaused = true; + return; + } + + const MWWorld::Ptr player = MWMechanics::getPlayer(); + const ESM::Position& playerPos = player.getRefData().getPosition(); + + mCurrentPlayerPos = osg::Vec2f( + std::floor(playerPos.pos[0] / sWorldScaleFactor), std::floor(playerPos.pos[1] / sWorldScaleFactor)); + const osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos; + mLastPlayerPos = mCurrentPlayerPos; + + state.mStateset->getUniform("positionCount")->set(static_cast(mPositionCount)); + state.mStateset->getUniform("offset")->set(offset); + + osg::Uniform* const positions = state.mStateset->getUniform("positions"); + + for (std::size_t i = 0; i < mPositionCount; ++i) + { + osg::Vec3f pos = mPositions[i] + - osg::Vec3f(mCurrentPlayerPos.x() * sWorldScaleFactor, mCurrentPlayerPos.y() * sWorldScaleFactor, 0.0) + + osg::Vec3f(sRTTSize * sWorldScaleFactor / 2, sRTTSize * sWorldScaleFactor / 2, 0.0); + pos /= sWorldScaleFactor; + positions->setElement(i, pos); + } + positions->dirty(); + + mPositionCount = 0; + } + void RipplesSurface::traverse(osg::NodeVisitor& nv) { - if (!nv.getFrameStamp()) + const osg::FrameStamp* const frameStamp = nv.getFrameStamp(); + + if (frameStamp == nullptr) return; if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) - { - size_t frameId = nv.getFrameStamp()->getFrameNumber() % 2; + updateState(*frameStamp, mState[frameStamp->getFrameNumber() % 2]); - const auto& player = MWMechanics::getPlayer(); - const ESM::Position& playerPos = player.getRefData().getPosition(); - - mCurrentPlayerPos = osg::Vec2f( - std::floor(playerPos.pos[0] / sWorldScaleFactor), std::floor(playerPos.pos[1] / sWorldScaleFactor)); - osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos; - mLastPlayerPos = mCurrentPlayerPos; - mState[frameId].mPaused = mPaused; - mState[frameId].mStateset->getUniform("positionCount")->set(static_cast(mPositionCount)); - mState[frameId].mStateset->getUniform("offset")->set(offset); - - auto* positions = mState[frameId].mStateset->getUniform("positions"); - - for (size_t i = 0; i < mPositionCount; ++i) - { - osg::Vec3f pos = mPositions[i] - - osg::Vec3f( - mCurrentPlayerPos.x() * sWorldScaleFactor, mCurrentPlayerPos.y() * sWorldScaleFactor, 0.0) - + osg::Vec3f(sRTTSize * sWorldScaleFactor / 2, sRTTSize * sWorldScaleFactor / 2, 0.0); - pos /= sWorldScaleFactor; - positions->setElement(i, pos); - } - positions->dirty(); - - mPositionCount = 0; - } osg::Geometry::traverse(nv); } void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const { osg::State& state = *renderInfo.getState(); - osg::GLExtensions& ext = *state.get(); - size_t contextID = state.getContextID(); - - size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2; + const std::size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2; const State& frameState = mState[currentFrame]; if (frameState.mPaused) { return; } - auto bindImage = [contextID, &state, &ext](osg::Texture2D* texture, GLuint index, GLenum access) { + osg::GLExtensions& ext = *state.get(); + const std::size_t contextID = state.getContextID(); + + const auto bindImage = [&](osg::Texture2D* texture, GLuint index, GLenum access) { osg::Texture::TextureObject* to = texture->getTextureObject(contextID); if (!to || texture->isDirty(contextID)) { @@ -180,52 +205,42 @@ namespace MWRender ext.glBindImageTexture(index, to->id(), 0, GL_FALSE, 0, access, GL_RGBA16F); }; - // Run simulation at a fixed rate independent on current FPS - // FIXME: when we skip frames we need to preserve positions. this doesn't work now - size_t ticks = 1; - // PASS: Blot in all ripple spawners mProgramBlobber->apply(state); state.apply(frameState.mStateset); - for (size_t i = 0; i < ticks; i++) + if (mUseCompute) { - if (mUseCompute) - { - bindImage(mTextures[1], 0, GL_WRITE_ONLY_ARB); - bindImage(mTextures[0], 1, GL_READ_ONLY_ARB); + bindImage(mTextures[1], 0, GL_WRITE_ONLY_ARB); + bindImage(mTextures[0], 1, GL_READ_ONLY_ARB); - ext.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1); - ext.glMemoryBarrier(GL_ALL_BARRIER_BITS); - } - else - { - mFBOs[1]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); - state.applyTextureAttribute(0, mTextures[0]); - osg::Geometry::drawImplementation(renderInfo); - } + ext.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1); + ext.glMemoryBarrier(GL_ALL_BARRIER_BITS); + } + else + { + mFBOs[1]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); + state.applyTextureAttribute(0, mTextures[0]); + osg::Geometry::drawImplementation(renderInfo); } // PASS: Wave simulation mProgramSimulation->apply(state); state.apply(frameState.mStateset); - for (size_t i = 0; i < ticks; i++) + if (mUseCompute) { - if (mUseCompute) - { - bindImage(mTextures[0], 0, GL_WRITE_ONLY_ARB); - bindImage(mTextures[1], 1, GL_READ_ONLY_ARB); + bindImage(mTextures[0], 0, GL_WRITE_ONLY_ARB); + bindImage(mTextures[1], 1, GL_READ_ONLY_ARB); - ext.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1); - ext.glMemoryBarrier(GL_ALL_BARRIER_BITS); - } - else - { - mFBOs[0]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); - state.applyTextureAttribute(0, mTextures[1]); - osg::Geometry::drawImplementation(renderInfo); - } + ext.glDispatchCompute(sRTTSize / 16, sRTTSize / 16, 1); + ext.glMemoryBarrier(GL_ALL_BARRIER_BITS); + } + else + { + mFBOs[0]->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); + state.applyTextureAttribute(0, mTextures[1]); + osg::Geometry::drawImplementation(renderInfo); } } diff --git a/apps/openmw/mwrender/ripples.hpp b/apps/openmw/mwrender/ripples.hpp index 3559c164a6..e355b16ecd 100644 --- a/apps/openmw/mwrender/ripples.hpp +++ b/apps/openmw/mwrender/ripples.hpp @@ -51,17 +51,20 @@ namespace MWRender static constexpr float sWorldScaleFactor = 2.5; private: - void setupFragmentPipeline(); - void setupComputePipeline(); - - Resource::ResourceSystem* mResourceSystem; - struct State { - osg::ref_ptr mStateset; bool mPaused = true; + osg::ref_ptr mStateset; }; + void setupFragmentPipeline(); + + void setupComputePipeline(); + + inline void updateState(const osg::FrameStamp& frameStamp, State& state); + + Resource::ResourceSystem* mResourceSystem; + size_t mPositionCount = 0; std::array mPositions; @@ -78,6 +81,9 @@ namespace MWRender bool mPaused = false; bool mUseCompute = false; + + double mLastSimulationTime = 0; + double mRemainingWaveTime = 0; }; class Ripples : public osg::Camera