1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-16 15:29:55 +00:00

fix persistent buffers and glsl_version

This commit is contained in:
Cody Glassman 2023-11-03 12:59:17 -07:00
parent e67135a517
commit 9a5fa9b8d6
9 changed files with 172 additions and 58 deletions

View file

@ -238,10 +238,35 @@ namespace MWRender
if (pass.mRenderTarget)
{
if (mDirtyAttachments)
{
const auto [w, h]
= pass.mSize.get(mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
pass.mRenderTexture->setTextureSize(w, h);
if (pass.mMipMap)
pass.mRenderTexture->setNumMipmapLevels(osg::Image::computeNumberOfMipmapLevels(w, h));
pass.mRenderTexture->dirtyTextureObject();
// Custom render targets must be shared between frame ids, so it's impossible to double buffer
// without expensive copies. That means the only thread-safe place to resize is in the draw
// thread.
osg::Texture2D* texture = const_cast<osg::Texture2D*>(dynamic_cast<const osg::Texture2D*>(
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
.getTexture()));
texture->setTextureSize(w, h);
texture->setNumMipmapLevels(pass.mRenderTexture->getNumMipmapLevels());
texture->dirtyTextureObject();
mDirtyAttachments = false;
}
pass.mRenderTarget->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
if (pass.mRenderTexture->getNumMipmapLevels() > 0)
{
state.setActiveTextureUnit(0);
state.applyTextureAttribute(0,
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)

View file

@ -30,6 +30,8 @@ namespace MWRender
void dirty() { mDirty = true; }
void resizeRenderTargets() { mDirtyAttachments = true; }
const fx::DispatchArray& getPasses() { return mPasses; }
void setPasses(fx::DispatchArray&& passes);
@ -65,6 +67,7 @@ namespace MWRender
osg::ref_ptr<osg::Texture> mTextureNormals;
mutable bool mDirty = false;
mutable bool mDirtyAttachments = false;
mutable osg::ref_ptr<osg::Viewport> mRenderViewport;
mutable osg::ref_ptr<osg::FrameBufferObject> mMultiviewResolveFramebuffer;
mutable osg::ref_ptr<osg::FrameBufferObject> mDestinationFBO;

View file

@ -211,25 +211,14 @@ namespace MWRender
if (Stereo::getStereo())
Stereo::Manager::instance().screenResolutionChanged();
auto width = renderWidth();
auto height = renderHeight();
for (auto& technique : mTechniques)
{
for (auto& [name, rt] : technique->getRenderTargetsMap())
{
const auto [w, h] = rt.mSize.get(width, height);
rt.mTarget->setTextureSize(w, h);
}
}
size_t frameId = frame() % 2;
createObjectsForFrame(frameId);
mRendering.updateProjectionMatrix();
mRendering.setScreenRes(width, height);
mRendering.setScreenRes(renderWidth(), renderHeight());
dirtyTechniques();
dirtyTechniques(true);
mDirty = true;
mDirtyFrameId = !frameId;
@ -534,7 +523,7 @@ namespace MWRender
mCanvases[frameId]->dirty();
}
void PostProcessor::dirtyTechniques()
void PostProcessor::dirtyTechniques(bool dirtyAttachments)
{
size_t frameId = frame() % 2;
@ -613,8 +602,6 @@ namespace MWRender
uniform->mName.c_str(), *type, uniform->getNumElements()));
}
std::unordered_map<osg::Texture2D*, osg::Texture2D*> renderTargetCache;
for (const auto& pass : technique->getPasses())
{
int subTexUnit = texUnit;
@ -626,32 +613,27 @@ namespace MWRender
if (!pass->getTarget().empty())
{
const auto& rt = technique->getRenderTargetsMap()[pass->getTarget()];
const auto [w, h] = rt.mSize.get(renderWidth(), renderHeight());
subPass.mRenderTexture = new osg::Texture2D(*rt.mTarget);
renderTargetCache[rt.mTarget] = subPass.mRenderTexture;
subPass.mRenderTexture->setTextureSize(w, h);
subPass.mRenderTexture->setName(std::string(pass->getTarget()));
if (rt.mMipMap)
subPass.mRenderTexture->setNumMipmapLevels(osg::Image::computeNumberOfMipmapLevels(w, h));
const auto& renderTarget = technique->getRenderTargetsMap()[pass->getTarget()];
subPass.mSize = renderTarget.mSize;
subPass.mRenderTexture = renderTarget.mTarget;
subPass.mMipMap = renderTarget.mMipMap;
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(
0, 0, subPass.mRenderTexture->getTextureWidth(), subPass.mRenderTexture->getTextureHeight()));
subPass.mRenderTarget = new osg::FrameBufferObject;
subPass.mRenderTarget->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
osg::FrameBufferAttachment(subPass.mRenderTexture));
const auto [w, h] = renderTarget.mSize.get(renderWidth(), renderHeight());
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(0, 0, w, h));
}
for (const auto& whitelist : pass->getRenderTargets())
for (const auto& name : pass->getRenderTargets())
{
auto it = technique->getRenderTargetsMap().find(whitelist);
if (it != technique->getRenderTargetsMap().end() && renderTargetCache[it->second.mTarget])
{
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTargetCache[it->second.mTarget]);
subPass.mStateSet->addUniform(new osg::Uniform(std::string(it->first).c_str(), subTexUnit++));
}
subPass.mStateSet->setTextureAttribute(subTexUnit, technique->getRenderTargetsMap()[name].mTarget);
subPass.mStateSet->addUniform(new osg::Uniform(name.c_str(), subTexUnit));
subTexUnit++;
}
node.mPasses.emplace_back(std::move(subPass));
@ -668,6 +650,9 @@ namespace MWRender
hud->updateTechniques();
mRendering.getSkyManager()->setSunglare(sunglare);
if (dirtyAttachments)
mCanvases[frameId]->resizeRenderTargets();
}
PostProcessor::Status PostProcessor::enableTechnique(
@ -774,7 +759,7 @@ namespace MWRender
for (auto& technique : mTemplates)
technique->compile();
dirtyTechniques();
dirtyTechniques(true);
}
void PostProcessor::disableDynamicShaders()

View file

@ -204,7 +204,7 @@ namespace MWRender
void createObjectsForFrame(size_t frameId);
void dirtyTechniques();
void dirtyTechniques(bool dirtyAttachments = false);
void update(size_t frameId);

View file

@ -339,7 +339,7 @@ float omw_EstimateFogCoverageFromUV(vec2 uv)
if (mCompiled)
return;
mLegacyGLSL = technique.getGLSLVersion() != 330;
mLegacyGLSL = technique.getGLSLVersion() < 330;
if (mType == Type::Pixel)
{

View file

@ -279,6 +279,7 @@ namespace fx
rt.mTarget->setSourceType(GL_UNSIGNED_BYTE);
rt.mTarget->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
rt.mTarget->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
rt.mTarget->setName(std::string(mBlockName));
while (!isNext<Lexer::Close_bracket>() && !isNext<Lexer::Eof>())
{

View file

@ -54,10 +54,14 @@ namespace fx
osg::ref_ptr<osg::FrameBufferObject> mRenderTarget;
osg::ref_ptr<osg::Texture2D> mRenderTexture;
bool mResolve = false;
Types::SizeProxy mSize;
bool mMipMap;
SubPass(const SubPass& other, const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
: mStateSet(new osg::StateSet(*other.mStateSet, copyOp))
, mResolve(other.mResolve)
, mSize(other.mSize)
, mMipMap(other.mMipMap)
{
if (other.mRenderTarget)
mRenderTarget = new osg::FrameBufferObject(*other.mRenderTarget, copyOp);

View file

@ -29,6 +29,16 @@ namespace fx
std::optional<int> mWidth;
std::optional<int> mHeight;
SizeProxy() = default;
SizeProxy(const SizeProxy& other)
: mWidthRatio(other.mWidthRatio)
, mHeightRatio(other.mHeightRatio)
, mWidth(other.mWidth)
, mHeight(other.mHeight)
{
}
std::tuple<int, int> get(int width, int height) const
{
int scaledWidth = width;

View file

@ -529,48 +529,134 @@ is not wanted and you want a custom render target.
| mipmaps | boolean | Whether mipmaps should be generated every frame |
+------------------+---------------------+-----------------------------------------------------------------------------+
To use the render target a pass must be assigned to it, along with any optional clear or blend modes.
To use the render target a pass must be assigned to it, along with any optional blend modes.
As a restriction, only three render targets can be bound per pass with ``rt1``, ``rt2``, ``rt3``, respectively.
In the code snippet below a rendertarget is used to draw the red channel of a scene at half resolution, then a quarter. As a restriction,
only three render targets can be bound per pass with ``rt1``, ``rt2``, ``rt3``, respectively.
Blending modes can be useful at times. Below is a simple shader which, when activated, will slowly turn the screen pure red.
Notice how we only ever write the value `.01` to the `RT_Red` buffer. Since we're using appropriate blending modes the
color buffer will accumulate.
.. code-block:: none
render_target RT_Downsample {
width_ratio = 0.5;
height_ratio = 0.5;
internal_format = r16f;
render_target RT_Red {
width = 4;
height = 4;
source_format = rgb;
internal_format = rgb16f;
source_type = float;
source_format = red;
}
render_target RT_Downsample4 {
width_ratio = 0.25;
height_ratio = 0.25;
}
fragment downsample2x(target=RT_Downsample) {
fragment red(target=RT_Red,blend=(add, src_color, one), rt1=RT_Red) {
omw_In vec2 omw_TexCoord;
void main()
{
omw_FragColor.r = omw_GetLastShader(omw_TexCoord).r;
omw_FragColor.rgb = vec3(0.01,0,0);
}
}
fragment downsample4x(target=RT_Downsample4, rt1=RT_Downsample) {
fragment view(rt1=RT_Red) {
omw_In vec2 omw_TexCoord;
void main()
{
omw_FragColor = omw_Texture2D(RT_Downsample, omw_TexCoord);
omw_FragColor = omw_Texture2D(RT_Red, omw_TexCoord);
}
}
Now, when the `downsample2x` pass runs it will write to the target buffer instead of the default
one assigned by the engine.
technique {
author = "OpenMW";
passes = red, view;
}
These custom render targets are persistent and ownership is given to the shader which defines them.
This gives potential to implement temporal effects by storing previous frame data in these buffers.
Below is an example which calculates a naive average scene luminance and transitions between values smoothly.
.. code-block:: none
render_target RT_Lum {
width = 256;
height = 256;
mipmaps = true;
source_format = rgb;
internal_format = rgb16f;
source_type = float;
min_filter = linear_mipmap_linear;
mag_filter = linear;
}
render_target RT_LumAvg {
source_type = float;
source_format = rgb;
internal_format = rgb16f;
min_filter = nearest;
mag_filter = nearest;
}
render_target RT_LumAvgLastFrame {
source_type = float;
source_format = rgb;
internal_format = rgb16f;
min_filter = nearest;
mag_filter = nearest;
}
fragment calculateLum(target=RT_Lum) {
omw_In vec2 omw_TexCoord;
void main()
{
vec3 orgi = pow(omw_GetLastShader(omw_TexCoord), vec4(2.2)).rgb;
omw_FragColor.rgb = orgi;
}
}
fragment fetchLumAvg(target=RT_LumAvg, rt1=RT_Lum, rt2=RT_LumAvgLastFrame) {
omw_In vec2 omw_TexCoord;
void main()
{
vec3 avgLumaCurrFrame = textureLod(RT_Lum, vec2(0.5, 0.5), 6).rgb;
vec3 avgLumaLastFrame = omw_Texture2D(RT_LumAvgLastFrame, vec2(0.5, 0.5)).rgb;
const float speed = 0.9;
vec3 avgLuma = avgLumaLastFrame + (avgLumaCurrFrame - avgLumaLastFrame) * (1.0 - exp(-omw.deltaSimulationTime * speed));
omw_FragColor.rgb = avgLuma;
}
}
fragment adaptation(rt1=RT_LumAvg) {
omw_In vec2 omw_TexCoord;
void main()
{
vec3 avgLuma = omw_Texture2D(RT_LumAvg, vec2(0.5, 0.5)).rgb;
if (omw_TexCoord.y < 0.2)
omw_FragColor = vec4(avgLuma, 1.0);
else
omw_FragColor = omw_GetLastShader(omw_TexCoord);
}
}
fragment store(target=RT_LumAvgLastFrame, rt1=RT_LumAvg) {
void main()
{
vec3 avgLuma = omw_Texture2D(RT_LumAvg, vec2(0.5, 0.5)).rgb;
omw_FragColor.rgb = avgLuma;
}
}
technique {
author = "OpenMW";
passes = calculateLum, fetchLumAvg, store, adaptation;
glsl_version = 330;
}
Simple Example
##############