fix persistent buffers and glsl_version

macos_ci_fix
Cody Glassman 1 year ago
parent e67135a517
commit 9a5fa9b8d6

@ -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)

@ -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;

@ -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()

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

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

@ -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>())
{

@ -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);

@ -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;

@ -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 red(target=RT_Red,blend=(add, src_color, one), rt1=RT_Red) {
omw_In vec2 omw_TexCoord;
void main()
{
omw_FragColor.rgb = vec3(0.01,0,0);
}
}
fragment view(rt1=RT_Red) {
omw_In vec2 omw_TexCoord;
void main()
{
omw_FragColor = omw_Texture2D(RT_Red, omw_TexCoord);
}
}
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;
}
fragment downsample2x(target=RT_Downsample) {
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()
{
omw_FragColor.r = omw_GetLastShader(omw_TexCoord).r;
vec3 orgi = pow(omw_GetLastShader(omw_TexCoord), vec4(2.2)).rgb;
omw_FragColor.rgb = orgi;
}
}
fragment downsample4x(target=RT_Downsample4, rt1=RT_Downsample) {
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()
{
omw_FragColor = omw_Texture2D(RT_Downsample, omw_TexCoord);
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;
}
}
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 = calculateLum, fetchLumAvg, store, adaptation;
glsl_version = 330;
}
Simple Example
##############

Loading…
Cancel
Save