mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-29 09:11:34 +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:
parent
3b01e209b1
commit
c9b4c8632a
2 changed files with 93 additions and 72 deletions
|
@ -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<int>(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<int>(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<osg::GLExtensions>();
|
||||
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<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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<osg::StateSet> mStateset;
|
||||
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;
|
||||
std::array<osg::Vec3f, 100> mPositions;
|
||||
|
||||
|
@ -78,6 +81,9 @@ namespace MWRender
|
|||
|
||||
bool mPaused = false;
|
||||
bool mUseCompute = false;
|
||||
|
||||
double mLastSimulationTime = 0;
|
||||
double mRemainingWaveTime = 0;
|
||||
};
|
||||
|
||||
class Ripples : public osg::Camera
|
||||
|
|
Loading…
Reference in a new issue