1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-11-10 00:56:41 +00:00

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.
This commit is contained in:
elsid 2024-02-18 23:14:40 +01:00
parent 3b01e209b1
commit c9b4c8632a
No known key found for this signature in database
GPG key ID: 4DE04C198CBA7625
2 changed files with 93 additions and 72 deletions

View file

@ -119,33 +119,47 @@ namespace MWRender
nullptr, shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE)); nullptr, shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE));
} }
void RipplesSurface::traverse(osg::NodeVisitor& nv) void RipplesSurface::updateState(const osg::FrameStamp& frameStamp, State& state)
{ {
if (!nv.getFrameStamp()) state.mPaused = mPaused;
if (mPaused)
return; return;
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) constexpr double updateFrequency = 60.0;
{ constexpr double updatePeriod = 1.0 / updateFrequency;
size_t frameId = nv.getFrameStamp()->getFrameNumber() % 2;
const auto& player = MWMechanics::getPlayer(); 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(); const ESM::Position& playerPos = player.getRefData().getPosition();
mCurrentPlayerPos = osg::Vec2f( mCurrentPlayerPos = osg::Vec2f(
std::floor(playerPos.pos[0] / sWorldScaleFactor), std::floor(playerPos.pos[1] / sWorldScaleFactor)); std::floor(playerPos.pos[0] / sWorldScaleFactor), std::floor(playerPos.pos[1] / sWorldScaleFactor));
osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos; const osg::Vec2f offset = mCurrentPlayerPos - mLastPlayerPos;
mLastPlayerPos = mCurrentPlayerPos; mLastPlayerPos = mCurrentPlayerPos;
mState[frameId].mPaused = mPaused;
mState[frameId].mStateset->getUniform("positionCount")->set(static_cast<int>(mPositionCount));
mState[frameId].mStateset->getUniform("offset")->set(offset);
auto* positions = mState[frameId].mStateset->getUniform("positions"); state.mStateset->getUniform("positionCount")->set(static_cast<int>(mPositionCount));
state.mStateset->getUniform("offset")->set(offset);
for (size_t i = 0; i < mPositionCount; ++i) osg::Uniform* const positions = state.mStateset->getUniform("positions");
for (std::size_t i = 0; i < mPositionCount; ++i)
{ {
osg::Vec3f pos = mPositions[i] osg::Vec3f pos = mPositions[i]
- osg::Vec3f( - osg::Vec3f(mCurrentPlayerPos.x() * sWorldScaleFactor, mCurrentPlayerPos.y() * sWorldScaleFactor, 0.0)
mCurrentPlayerPos.x() * sWorldScaleFactor, mCurrentPlayerPos.y() * sWorldScaleFactor, 0.0)
+ osg::Vec3f(sRTTSize * sWorldScaleFactor / 2, sRTTSize * sWorldScaleFactor / 2, 0.0); + osg::Vec3f(sRTTSize * sWorldScaleFactor / 2, sRTTSize * sWorldScaleFactor / 2, 0.0);
pos /= sWorldScaleFactor; pos /= sWorldScaleFactor;
positions->setElement(i, pos); positions->setElement(i, pos);
@ -154,23 +168,34 @@ namespace MWRender
mPositionCount = 0; mPositionCount = 0;
} }
void RipplesSurface::traverse(osg::NodeVisitor& nv)
{
const osg::FrameStamp* const frameStamp = nv.getFrameStamp();
if (frameStamp == nullptr)
return;
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
updateState(*frameStamp, mState[frameStamp->getFrameNumber() % 2]);
osg::Geometry::traverse(nv); osg::Geometry::traverse(nv);
} }
void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const
{ {
osg::State& state = *renderInfo.getState(); osg::State& state = *renderInfo.getState();
osg::GLExtensions& ext = *state.get<osg::GLExtensions>(); const std::size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2;
size_t contextID = state.getContextID();
size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2;
const State& frameState = mState[currentFrame]; const State& frameState = mState[currentFrame];
if (frameState.mPaused) if (frameState.mPaused)
{ {
return; return;
} }
auto bindImage = [contextID, &state, &ext](osg::Texture2D* texture, GLuint index, GLenum access) { osg::GLExtensions& ext = *state.get<osg::GLExtensions>();
const std::size_t contextID = state.getContextID();
const auto bindImage = [&](osg::Texture2D* texture, GLuint index, GLenum access) {
osg::Texture::TextureObject* to = texture->getTextureObject(contextID); osg::Texture::TextureObject* to = texture->getTextureObject(contextID);
if (!to || texture->isDirty(contextID)) if (!to || texture->isDirty(contextID))
{ {
@ -180,16 +205,10 @@ namespace MWRender
ext.glBindImageTexture(index, to->id(), 0, GL_FALSE, 0, access, GL_RGBA16F); 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 // PASS: Blot in all ripple spawners
mProgramBlobber->apply(state); mProgramBlobber->apply(state);
state.apply(frameState.mStateset); 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[1], 0, GL_WRITE_ONLY_ARB);
@ -204,14 +223,11 @@ namespace MWRender
state.applyTextureAttribute(0, mTextures[0]); state.applyTextureAttribute(0, mTextures[0]);
osg::Geometry::drawImplementation(renderInfo); osg::Geometry::drawImplementation(renderInfo);
} }
}
// PASS: Wave simulation // PASS: Wave simulation
mProgramSimulation->apply(state); mProgramSimulation->apply(state);
state.apply(frameState.mStateset); 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[0], 0, GL_WRITE_ONLY_ARB);
@ -227,7 +243,6 @@ namespace MWRender
osg::Geometry::drawImplementation(renderInfo); osg::Geometry::drawImplementation(renderInfo);
} }
} }
}
osg::Texture* RipplesSurface::getColorTexture() const osg::Texture* RipplesSurface::getColorTexture() const
{ {

View file

@ -51,17 +51,20 @@ namespace MWRender
static constexpr float sWorldScaleFactor = 2.5; static constexpr float sWorldScaleFactor = 2.5;
private: private:
void setupFragmentPipeline();
void setupComputePipeline();
Resource::ResourceSystem* mResourceSystem;
struct State struct State
{ {
osg::ref_ptr<osg::StateSet> mStateset;
bool mPaused = true; bool mPaused = true;
osg::ref_ptr<osg::StateSet> mStateset;
}; };
void setupFragmentPipeline();
void setupComputePipeline();
inline void updateState(const osg::FrameStamp& frameStamp, State& state);
Resource::ResourceSystem* mResourceSystem;
size_t mPositionCount = 0; size_t mPositionCount = 0;
std::array<osg::Vec3f, 100> mPositions; std::array<osg::Vec3f, 100> mPositions;
@ -78,6 +81,9 @@ namespace MWRender
bool mPaused = false; bool mPaused = false;
bool mUseCompute = false; bool mUseCompute = false;
double mLastSimulationTime = 0;
double mRemainingWaveTime = 0;
}; };
class Ripples : public osg::Camera class Ripples : public osg::Camera