From 1d1947c51a5599566318ba55cd09db71f388de73 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 29 Jul 2012 04:28:11 -0500 Subject: [PATCH] Reworked LLPostProcess and implemented FBO support (much faster if multiple post shaders are enabled, or need a lot of passes). Tweaked LLRenderTarget to support depth textures if FBO support is lacking. Prefer LLRenderTarget::getFBO() over LLRenderTarget::sUseFBO when determining how to handle a specific LLRenderTarget object. (Decoupling to simplify logic without having to track a global) --- indra/llrender/llpostprocess.cpp | 696 +++++++++++------- indra/llrender/llpostprocess.h | 274 +++---- indra/llrender/llrender.cpp | 2 +- indra/llrender/llrendertarget.cpp | 37 +- indra/llrender/llrendertarget.h | 2 + .../shaders/class1/effects/PosterizeF.glsl | 23 + .../windlight/postprocesseffects.xml | 6 +- indra/newview/llfloaterdaycycle.cpp | 1 - indra/newview/llfloaterpostprocess.cpp | 26 +- indra/newview/llfloaterwater.cpp | 1 - indra/newview/llfloaterwindlight.cpp | 1 - indra/newview/llstartup.cpp | 1 - indra/newview/llviewerdisplay.cpp | 14 +- indra/newview/llviewershadermgr.cpp | 18 + indra/newview/llviewerwindow.cpp | 2 +- indra/newview/pipeline.cpp | 10 +- .../xui/en-us/floater_post_process.xml | 19 + 17 files changed, 660 insertions(+), 473 deletions(-) create mode 100644 indra/newview/app_settings/shaders/class1/effects/PosterizeF.glsl diff --git a/indra/llrender/llpostprocess.cpp b/indra/llrender/llpostprocess.cpp index 25e775c1e..e09d0f349 100644 --- a/indra/llrender/llpostprocess.cpp +++ b/indra/llrender/llpostprocess.cpp @@ -33,34 +33,247 @@ #include "linden_common.h" #include "llpostprocess.h" -#include "llglslshader.h" -#include "llsdserialize.h" -#include "llrender.h" -#include "llvertexbuffer.h" #include "lldir.h" +#include "llgl.h" +#include "llglslshader.h" +#include "llrender.h" +#include "llsdserialize.h" +#include "llsdutil.h" +#include "llsdutil_math.h" +#include "llvertexbuffer.h" +#include "llfasttimer.h" + extern LLGLSLShader gPostColorFilterProgram; extern LLGLSLShader gPostNightVisionProgram; extern LLGLSLShader gPostGaussianBlurProgram; +extern LLGLSLShader gPostPosterizeProgram; static const unsigned int NOISE_SIZE = 512; -/// CALCULATING LUMINANCE (Using NTSC lum weights) -/// http://en.wikipedia.org/wiki/Luma_%28video%29 -static const float LUMINANCE_R = 0.299f; -static const float LUMINANCE_G = 0.587f; -static const float LUMINANCE_B = 0.114f; - static const char * const XML_FILENAME = "postprocesseffects.xml"; -LLPostProcess::LLPostProcess(void) : - mVBO(NULL), - mAllEffects(LLSD::emptyMap()), - mScreenWidth(1), mScreenHeight(1) +template<> LLSD LLPostProcessShader::LLShaderSetting::getDefaultValue() { - mSceneRenderTexture = NULL ; - mNoiseTexture = NULL ; - + return mDefault.getValue(); +} +template<> void LLPostProcessShader::LLShaderSetting::setValue(const LLSD& value) +{ + mValue = ll_vector4_from_sd(value); +} + +LLSD LLPostProcessShader::getDefaults() +{ + LLSD defaults; + for(std::vector::iterator it=mSettings.begin();it!=mSettings.end();++it) + { + defaults[(*it)->mSettingName]=(*it)->getDefaultValue(); + } + return defaults; +} +void LLPostProcessShader::loadSettings(const LLSD& settings) +{ + for(std::vector::iterator it=mSettings.begin();it!=mSettings.end();++it) + { + LLSD value = settings[(*it)->mSettingName]; + (*it)->setValue(value); + } +} + +class LLColorFilterShader : public LLPostProcessShader +{ +private: + LLShaderSetting mEnabled; + LLShaderSetting mGamma, mBrightness, mContrast, mSaturation; + LLShaderSetting mContrastBase; +public: + LLColorFilterShader() : + mEnabled("enable_color_filter",false), + mGamma("gamma",1.f), + mBrightness("brightness",1.f), + mContrast("contrast",1.f), + mSaturation("saturation",1.f), + mContrastBase("contrast_base",LLVector4(1.f,1.f,1.f,0.5f)) + { + mSettings.push_back(&mEnabled); + mSettings.push_back(&mGamma); + mSettings.push_back(&mBrightness); + mSettings.push_back(&mContrast); + mSettings.push_back(&mSaturation); + mSettings.push_back(&mContrastBase); + } + + bool isEnabled() { return mEnabled && gPostColorFilterProgram.mProgramObject; } + S32 getColorChannel() { return 0; } + S32 getDepthChannel() { return -1; } + + QuadType bind() + { + if(!isEnabled()) + return QUAD_NONE; + + /// CALCULATING LUMINANCE (Using NTSC lum weights) + /// http://en.wikipedia.org/wiki/Luma_%28video%29 + static const float LUMINANCE_R = 0.299f; + static const float LUMINANCE_G = 0.587f; + static const float LUMINANCE_B = 0.114f; + + gPostColorFilterProgram.bind(); + + gPostColorFilterProgram.uniform1f("gamma", mGamma); + gPostColorFilterProgram.uniform1f("brightness", mBrightness); + gPostColorFilterProgram.uniform1f("contrast", mContrast); + float baseI = (mContrastBase.mValue[VX] + mContrastBase.mValue[VY] + mContrastBase.mValue[VZ]) / 3.0f; + baseI = mContrastBase.mValue[VW] / ((baseI < 0.001f) ? 0.001f : baseI); + float baseR = mContrastBase.mValue[VX] * baseI; + float baseG = mContrastBase.mValue[VY] * baseI; + float baseB = mContrastBase.mValue[VZ] * baseI; + gPostColorFilterProgram.uniform3fv("contrastBase", 1, LLVector3(baseR, baseG, baseB).mV); + gPostColorFilterProgram.uniform1f("saturation", mSaturation); + gPostColorFilterProgram.uniform3fv("lumWeights", 1, LLVector3(LUMINANCE_R, LUMINANCE_G, LUMINANCE_B).mV); + return QUAD_NORMAL; + } + bool draw(U32 pass) {return pass == 1;} + void unbind() + { + gPostColorFilterProgram.unbind(); + } +}; + +class LLNightVisionShader : public LLPostProcessShader +{ +private: + LLShaderSetting mEnabled; + LLShaderSetting mBrightnessMult, mNoiseStrength; +public: + LLNightVisionShader() : + mEnabled("enable_night_vision",false), + mBrightnessMult("brightness_multiplier",3.f), + mNoiseStrength("noise_strength",.4f) + { + mSettings.push_back(&mEnabled); + mSettings.push_back(&mBrightnessMult); + mSettings.push_back(&mNoiseStrength); + } + bool isEnabled() { return mEnabled && gPostNightVisionProgram.mProgramObject; } + S32 getColorChannel() { return 0; } + S32 getDepthChannel() { return -1; } + QuadType bind() + { + if(!isEnabled()) + return QUAD_NONE; + + gPostNightVisionProgram.bind(); + + LLPostProcess::getInstance()->bindNoise(1); + + gPostNightVisionProgram.uniform1f("brightMult", mBrightnessMult); + gPostNightVisionProgram.uniform1f("noiseStrength", mNoiseStrength); + + return QUAD_NOISE; + + } + bool draw(U32 pass) {return pass == 1;} + void unbind() + { + gPostNightVisionProgram.unbind(); + } +}; + +class LLGaussBlurShader : public LLPostProcessShader +{ +private: + LLShaderSetting mEnabled; + LLShaderSetting mNumPasses; + GLint mPassLoc; +public: + LLGaussBlurShader() : + mEnabled("enable_gauss_blur",false), + mNumPasses("gauss_blur_passes",2), + mPassLoc(0) + { + mSettings.push_back(&mEnabled); + mSettings.push_back(&mNumPasses); + } + bool isEnabled() { return mEnabled && mNumPasses && gPostGaussianBlurProgram.mProgramObject; } + S32 getColorChannel() { return 0; } + S32 getDepthChannel() { return -1; } + QuadType bind() + { + if(!isEnabled()) + return QUAD_NONE; + + gPostGaussianBlurProgram.bind(); + + mPassLoc = gPostGaussianBlurProgram.getUniformLocation("horizontalPass"); + + return QUAD_NORMAL; + } + bool draw(U32 pass) + { + if((S32)pass > mNumPasses*2) + return false; + glUniform1iARB(mPassLoc, (pass-1) % 2); + return true; + } + void unbind() + { + gPostGaussianBlurProgram.unbind(); + } +}; + +class LLPosterizeShader : public LLPostProcessShader +{ +private: + LLShaderSetting mEnabled; + LLShaderSetting mNumLayers; +public: + LLPosterizeShader() : + mEnabled("enable_posterize",false), + mNumLayers("posterize_layers",2) + { + mSettings.push_back(&mEnabled); + mSettings.push_back(&mNumLayers); + } + bool isEnabled() { return mEnabled && gPostPosterizeProgram.mProgramObject; } + S32 getColorChannel() { return 0; } + S32 getDepthChannel() { return -1; } + QuadType bind() + { + if(!isEnabled()) + return QUAD_NONE; + + gPostPosterizeProgram.bind(); + + gPostPosterizeProgram.uniform1i("layerCount", mNumLayers); + + return QUAD_NORMAL; + } + bool draw(U32 pass) + { + return pass == 1; + } + void unbind() + { + gPostPosterizeProgram.unbind(); + } +}; + +LLPostProcess::LLPostProcess(void) : + mVBO(NULL), + mDepthTexture(0), + mNoiseTexture(NULL), + mScreenWidth(0), + mScreenHeight(0), + mNoiseTextureScale(0.f), + mSelectedEffectInfo(LLSD::emptyMap()), + mAllEffectInfo(LLSD::emptyMap()) +{ + mShaders.push_back(new LLColorFilterShader()); + mShaders.push_back(new LLNightVisionShader()); + mShaders.push_back(new LLGaussBlurShader()); + mShaders.push_back(new LLPosterizeShader()); + /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.*/ std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME)); LL_DEBUGS2("AppInit", "Shaders") << "Loading PostProcess Effects settings from " << pathName << LL_ENDL; @@ -71,99 +284,43 @@ LLPostProcess::LLPostProcess(void) : { LLPointer parser = new LLSDXMLParser(); - parser->parse(effectsXML, mAllEffects, LLSDSerialize::SIZE_UNLIMITED); + parser->parse(effectsXML, mAllEffectInfo, LLSDSerialize::SIZE_UNLIMITED); } - if (!mAllEffects.has("default")) + if (!mAllEffectInfo.has("default")) + mAllEffectInfo["default"] = LLSD::emptyMap(); + + LLSD& defaults = mAllEffectInfo["default"]; + + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) { - LLSD & defaultEffect = (mAllEffects["default"] = LLSD::emptyMap()); - - /*defaultEffect["enable_night_vision"] = LLSD::Boolean(false); - defaultEffect["enable_color_filter"] = LLSD::Boolean(false);*/ - - /// NVG Defaults - defaultEffect["brightness_multiplier"] = 3.0; - defaultEffect["noise_size"] = 25.0; - defaultEffect["noise_strength"] = 0.4; - - // TODO BTest potentially add this to tweaks? - mNoiseTextureScale = 1.0f; - - /// Color Filter Defaults - defaultEffect["gamma"] = 1.0; - defaultEffect["brightness"] = 1.0; - defaultEffect["contrast"] = 1.0; - defaultEffect["saturation"] = 1.0; - - LLSD& contrastBase = (defaultEffect["contrast_base"] = LLSD::emptyArray()); - contrastBase.append(1.0); - contrastBase.append(1.0); - contrastBase.append(1.0); - contrastBase.append(0.5); - - defaultEffect["gauss_blur_passes"] = 2; + LLSD shader_defaults = (*it)->getDefaults(); + for (LLSD::map_const_iterator it2 = defaults.beginMap();it2 != defaults.endMap();++it2) + { + if(!defaults.has(it2->first)) + defaults[it2->first]=it2->second; + } + } + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + { + (*it)->loadSettings(defaults); } - setSelectedEffect("default"); - // */ } LLPostProcess::~LLPostProcess(void) { - invalidate() ; -} - -/*static*/void LLPostProcess::cleanupClass() -{ - if(instanceExists()) - getInstance()->invalidate() ; -} - -void LLPostProcess::setSelectedEffect(std::string const & effectName) -{ - mSelectedEffectName = effectName; - static_cast(tweaks) = mAllEffects[effectName]; -} - -void LLPostProcess::saveEffect(std::string const & effectName) -{ - mAllEffects[effectName] = tweaks; - - std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME)); - //llinfos << "Saving PostProcess Effects settings to " << pathName << llendl; - - llofstream effectsXML(pathName); - - LLPointer formatter = new LLSDXMLFormatter(); - - formatter->format(mAllEffects, effectsXML); -} -void LLPostProcess::invalidate() -{ - mSceneRenderTexture = NULL ; - mNoiseTexture = NULL ; - mVBO = NULL ; -} - -void LLPostProcess::apply(unsigned int width, unsigned int height) -{ - if(shadersEnabled()) - { - if (mVBO.isNull() || width != mScreenWidth || height != mScreenHeight) - { - initialize(width, height); - } - doEffects(); - } + destroyGL() ; } void LLPostProcess::initialize(unsigned int width, unsigned int height) { - invalidate(); + destroyGL(); mScreenWidth = width; mScreenHeight = height; - createScreenTexture(); + createScreenTextures(); + createNoiseTexture(); //Setup our VBO. { @@ -185,127 +342,129 @@ void LLPostProcess::initialize(unsigned int width, unsigned int height) mVBO->flush(); } - - checkError(); - createNoiseTexture(); - checkError(); + stop_glerror(); } -inline bool LLPostProcess::shadersEnabled(void) +void LLPostProcess::createScreenTextures() { - return (tweaks.useColorFilter().asBoolean() || - tweaks.useNightVisionShader().asBoolean() || - tweaks.useGaussBlurFilter().asBoolean() ); + const LLTexUnit::eTextureType type = LLTexUnit::TT_RECT_TEXTURE; + + mRenderTarget[0].allocate(mScreenWidth,mScreenHeight,GL_RGBA,FALSE,FALSE,type,FALSE); + if(mRenderTarget[0].getFBO())//Only need pingpong between two rendertargets if FBOs are supported. + mRenderTarget[1].allocate(mScreenWidth,mScreenHeight,GL_RGBA,FALSE,FALSE,type,FALSE); + stop_glerror(); + + if(mDepthTexture) + LLImageGL::deleteTextures(type, 0, 0, 1, &mDepthTexture, true); + + LLImageGL::generateTextures(type, GL_DEPTH_COMPONENT24, 1, &mDepthTexture); + gGL.getTexUnit(0)->bindManual(type, mDepthTexture); + LLImageGL::setManualImage(LLTexUnit::getInternalType(type), 0, GL_DEPTH_COMPONENT24, mScreenWidth, mScreenHeight, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); + stop_glerror(); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + stop_glerror(); } -void LLPostProcess::applyShaders(void) -{ - bool copy_buffer = false; - if (tweaks.useColorFilter()) - { - applyColorFilterShader(); - checkError(); - copy_buffer = true; - } - if (tweaks.useGaussBlurFilter()) - { - /// If any of the above shaders have been called update the frame buffer; - if (copy_buffer) - copyFrameBuffer(); - applyGaussBlurShader(); - checkError(); - copy_buffer = true; - } - if (tweaks.useNightVisionShader()) - { - /// If any of the above shaders have been called update the frame buffer; - if (copy_buffer) - copyFrameBuffer(); - applyNightVisionShader(); - checkError(); - copy_buffer = true; - } -} - -void LLPostProcess::applyColorFilterShader(void) +void LLPostProcess::createNoiseTexture() { - if(gPostColorFilterProgram.mProgramObject == 0) - return; - - gPostColorFilterProgram.bind(); - - gGL.getTexUnit(0)->bind(mSceneRenderTexture); - - gPostColorFilterProgram.uniform1f("gamma", tweaks.getGamma()); - gPostColorFilterProgram.uniform1f("brightness", tweaks.getBrightness()); - gPostColorFilterProgram.uniform1f("contrast", tweaks.getContrast()); - float baseI = (tweaks.getContrastBaseR() + tweaks.getContrastBaseG() + tweaks.getContrastBaseB()) / 3.0f; - baseI = tweaks.getContrastBaseIntensity() / ((baseI < 0.001f) ? 0.001f : baseI); - float baseR = tweaks.getContrastBaseR() * baseI; - float baseG = tweaks.getContrastBaseG() * baseI; - float baseB = tweaks.getContrastBaseB() * baseI; - gPostColorFilterProgram.uniform3fv("contrastBase", 1, LLVector3(baseR, baseG, baseB).mV); - gPostColorFilterProgram.uniform1f("saturation", tweaks.getSaturation()); - gPostColorFilterProgram.uniform3fv("lumWeights", 1, LLVector3(LUMINANCE_R, LUMINANCE_G, LUMINANCE_B).mV); - - /// Draw a screen space quad - drawOrthoQuad(QUAD_NORMAL); - gPostColorFilterProgram.unbind(); -} - -void LLPostProcess::applyNightVisionShader(void) -{ - if(gPostNightVisionProgram.mProgramObject == 0) - return; - - gPostNightVisionProgram.bind(); - - gGL.getTexUnit(0)->bind(mSceneRenderTexture); - gGL.getTexUnit(1)->bind(mNoiseTexture); - - gPostNightVisionProgram.uniform1f("brightMult", tweaks.getBrightMult()); - gPostNightVisionProgram.uniform1f("noiseStrength", tweaks.getNoiseStrength()); - mNoiseTextureScale = 0.001f + ((100.f - tweaks.getNoiseSize()) / 100.f); - mNoiseTextureScale *= (mScreenHeight / NOISE_SIZE); - - /// Draw a screen space quad - drawOrthoQuad(QUAD_NOISE); - gPostNightVisionProgram.unbind(); -} - -void LLPostProcess::applyGaussBlurShader(void) -{ - int pass_count = tweaks.getGaussBlurPasses(); - if(!pass_count || gPostGaussianBlurProgram.mProgramObject == 0) - return; - - gPostGaussianBlurProgram.bind(); - - gGL.getTexUnit(0)->bind(mSceneRenderTexture); - - GLint horiz_pass = gPostGaussianBlurProgram.getUniformLocation("horizontalPass"); - for(int i = 0;i buffer(NOISE_SIZE * NOISE_SIZE); + for (unsigned int i = 0; i < NOISE_SIZE; i++){ + for (unsigned int k = 0; k < NOISE_SIZE; k++){ + buffer[(i * NOISE_SIZE) + k] = (GLubyte)((double) rand() / ((double) RAND_MAX + 1.f) * 255.f); + } + } + + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + { + if((*it)->getDepthChannel()>=0) + { + mNoiseTexture = new LLImageGL(FALSE) ; + if(mNoiseTexture->createGLTexture()) + { + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mNoiseTexture->getTexName()); + LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0]); + stop_glerror(); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP); + stop_glerror(); + break; + } + } + } +} + +void LLPostProcess::destroyGL() +{ + mRenderTarget[0].release(); + mRenderTarget[1].release(); + if(mDepthTexture) + LLImageGL::deleteTextures(LLTexUnit::TT_RECT_TEXTURE, 0, 0, 1, &mDepthTexture, true); + mDepthTexture=0; + mNoiseTexture = NULL ; + mVBO = NULL ; +} + +/*static*/void LLPostProcess::cleanupClass() +{ + if(instanceExists()) + getInstance()->destroyGL() ; +} + +void LLPostProcess::copyFrameBuffer() +{ + mRenderTarget[!!mRenderTarget[0].getFBO()].bindTexture(0,0); + glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB,0,0,0,0,0,mScreenWidth, mScreenHeight); + + if(mDepthTexture) + { + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + { + if((*it)->isEnabled() && (*it)->getDepthChannel()>=0) + { + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, mDepthTexture); + glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB,0,0,0,0,0,mScreenWidth, mScreenHeight); + break; + } + } + } + +} + +void LLPostProcess::bindNoise(U32 channel) +{ + gGL.getTexUnit(channel)->bind(mNoiseTexture); +} + +void LLPostProcess::renderEffects(unsigned int width, unsigned int height) +{ + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + { + if((*it)->isEnabled()) + { + if (mVBO.isNull() || width != mScreenWidth || height != mScreenHeight) + { + initialize(width, height); + } + doEffects(); + return; } } - gPostGaussianBlurProgram.unbind(); } void LLPostProcess::doEffects(void) { LLVertexBuffer::unbind(); + mNoiseTextureScale = 0.001f + ((100.f - mSelectedEffectInfo["noise_size"].asFloat()) / 100.f); + mNoiseTextureScale *= (mScreenHeight / NOISE_SIZE); + /// Copy the screen buffer to the render texture copyFrameBuffer(); + stop_glerror(); //Disable depth. Set blendmode to replace. - LLGLDepthTest depth(GL_FALSE); + LLGLDepthTest depth(GL_FALSE,GL_FALSE); LLGLEnable blend(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_REPLACE); @@ -319,7 +478,6 @@ void LLPostProcess::doEffects(void) gGL.loadIdentity(); applyShaders(); - checkError(); LLGLSLShader::bindNoShader(); @@ -333,13 +491,46 @@ void LLPostProcess::doEffects(void) gGL.setSceneBlendType(LLRender::BT_ALPHA); //Restore blendstate. Alpha is ASSUMED for hud/ui render, etc. gGL.getTexUnit(1)->disable(); - checkError(); } -void LLPostProcess::copyFrameBuffer() +void LLPostProcess::applyShaders(void) { - gGL.getTexUnit(0)->bindManual(mSceneRenderTexture->getTarget(), mSceneRenderTexture->getTexName()); - glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 0, 0, mScreenWidth, mScreenHeight, 0); + QuadType quad; + bool primary_rendertarget = 1; + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + { + if((quad = (*it)->bind()) != QUAD_NONE) + { + S32 color_channel = (*it)->getColorChannel(); + S32 depth_channel = (*it)->getDepthChannel(); + + if(depth_channel >= 0) + gGL.getTexUnit(depth_channel)->bindManual(LLTexUnit::TT_RECT_TEXTURE, mDepthTexture); + + U32 pass = 1; + + while((*it)->draw(pass++)) + { + mRenderTarget[!primary_rendertarget].bindTarget(); + + if(color_channel >= 0) + mRenderTarget[mRenderTarget[0].getFBO() ? primary_rendertarget : !primary_rendertarget].bindTexture(0,color_channel); + + drawOrthoQuad(quad); + mRenderTarget[!primary_rendertarget].flush(); + if(mRenderTarget[0].getFBO()) + primary_rendertarget = !primary_rendertarget; + } + (*it)->unbind(); + } + } + //Only need to copy to framebuffer if FBOs are supported, else we've already been drawing to the framebuffer to begin with. + if(mRenderTarget[0].getFBO()) + { + //copyContentsToFramebuffer also binds the main framebuffer. + LLRenderTarget::copyContentsToFramebuffer(mRenderTarget[primary_rendertarget],0,0,mScreenWidth,mScreenHeight,0,0,mScreenWidth,mScreenHeight,GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + stop_glerror(); } void LLPostProcess::drawOrthoQuad(QuadType type) @@ -364,64 +555,59 @@ void LLPostProcess::drawOrthoQuad(QuadType type) mVBO->drawArrays(LLRender::TRIANGLE_STRIP, 0, 4); } -void LLPostProcess::createScreenTexture() +void LLPostProcess::setSelectedEffect(std::string const & effectName) { - std::vector data(mScreenWidth * mScreenHeight * 3, 0) ; - - mSceneRenderTexture = new LLImageGL(FALSE) ; - if(mSceneRenderTexture->createGLTexture()) + mSelectedEffectName = effectName; + mSelectedEffectInfo = mAllEffectInfo[effectName]; + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) { - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, mSceneRenderTexture->getTexName()); - LLImageGL::setManualImage(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, mScreenWidth, mScreenHeight, GL_RGB, GL_UNSIGNED_BYTE, &data[0]); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + (*it)->loadSettings(mSelectedEffectInfo); } } -void LLPostProcess::createNoiseTexture() -{ - std::vector buffer(NOISE_SIZE * NOISE_SIZE); - for (unsigned int i = 0; i < NOISE_SIZE; i++){ - for (unsigned int k = 0; k < NOISE_SIZE; k++){ - buffer[(i * NOISE_SIZE) + k] = (GLubyte)((double) rand() / ((double) RAND_MAX + 1.f) * 255.f); - } - } - - mNoiseTexture = new LLImageGL(FALSE) ; - if(mNoiseTexture->createGLTexture()) - { - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mNoiseTexture->getTexName()); - LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0]); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP); - } -} - -bool LLPostProcess::checkError(void) +void LLPostProcess::setSelectedEffectValue(std::string const & setting, LLSD& value) { - GLenum glErr; - bool retCode = false; - - glErr = glGetError(); - while (glErr != GL_NO_ERROR) - { - // shaderErrorLog << (const char *) gluErrorString(glErr) << std::endl; - char const * err_str_raw = (const char *) gluErrorString(glErr); - - if(err_str_raw == NULL) - { - std::ostringstream err_builder; - err_builder << "unknown error number " << glErr; - mShaderErrorString = err_builder.str(); - } - else - { - mShaderErrorString = err_str_raw; - } - - retCode = true; - glErr = glGetError(); - } - return retCode; + char buf[256]; + S32 elem=0; + if(sscanf(setting.c_str(),"%255[^[][%d]", buf, &elem) == 2) + { + mSelectedEffectInfo[buf][elem] = value; + } + else + { + mSelectedEffectInfo[setting] = value; + } + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + { + (*it)->loadSettings(mSelectedEffectInfo); + } } +void LLPostProcess::resetSelectedEffect() +{ + if(!llsd_equals(mAllEffectInfo[mSelectedEffectName], mSelectedEffectInfo)) + { + mSelectedEffectInfo = mAllEffectInfo[mSelectedEffectName]; + for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + { + (*it)->loadSettings(mSelectedEffectInfo); + } + } +} + +void LLPostProcess::saveEffectAs(std::string const & effectName) +{ + mAllEffectInfo[effectName] = mSelectedEffectInfo; + + std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME)); + //llinfos << "Saving PostProcess Effects settings to " << pathName << llendl; + + llofstream effectsXML(pathName); + + LLPointer formatter = new LLSDXMLFormatter(); + + formatter->format(mAllEffectInfo, effectsXML); +} + + + diff --git a/indra/llrender/llpostprocess.h b/indra/llrender/llpostprocess.h index 459fa6851..830598a92 100644 --- a/indra/llrender/llpostprocess.h +++ b/indra/llrender/llpostprocess.h @@ -34,189 +34,123 @@ #define LL_POSTPROCESS_H #include -#include -#include "llgl.h" -#include "llglheaders.h" +#include "llsd.h" +#include "llrendertarget.h" + +class LLSD; + +typedef enum _QuadType { + QUAD_NONE, + QUAD_NORMAL, + QUAD_NOISE +} QuadType; + +//LLPostProcessShader is an attempt to encapsulate the shaders a little better. +class LLPostProcessShader : public LLRefCount //Abstract. PostProcess shaders derive off of this common base. +{ +protected: + //LLShaderSetting is used to associate key names to member variables to avoid LLSD lookups when drawing. + //It also facilitates automating the assigning of defaults to, as well as parsing from, the effects LLSD list. + //This replaces the entire old PostProcessTweaks structure. More will be done in the future to move into a more + //xml-driven configuration. + struct LLShaderSettingBase + { + LLShaderSettingBase(const char* name) : mSettingName(name) {} + const char* mSettingName; //LLSD key names as found in postprocesseffects.xml. eg 'contrast_base' + virtual LLSD getDefaultValue() = 0; //Converts the member variable as an LLSD. Used to set defaults absent in postprocesseffects.xml + virtual void setValue(const LLSD& value) = 0; //Connects the LLSD element to the member variable. Used when loading effects (such as default) + }; + template + struct LLShaderSetting : public LLShaderSettingBase + { + LLShaderSetting(const char* setting_name, T def) : LLShaderSettingBase(setting_name), mValue(def), mDefault(def) {} + T mValue; //The member variable mentioned above. + T mDefault; //Set via ctor. Value is inserted into the defaults LLSD list if absent from postprocesseffects.xml + LLSD getDefaultValue() { return mDefault; } //See LLShaderSettingBase::getDefaultValue + void setValue(const LLSD& value) { mValue = value; } //See LLShaderSettingBase::setValue + operator T() { return mValue; } //Typecast operator overload so this object can be handled as if it was whatever T represents. + }; + std::vector mSettings; //Contains a list of all the 'settings' this shader uses. Manually add via push_back in ctor. +public: + virtual ~LLPostProcessShader() {}; + virtual bool isEnabled() = 0; //Returning false avoids bind/draw/unbind calls. If no shaders are enabled, framebuffer copying is skipped. + virtual S32 getColorChannel() = 0; //If color buffer is used in this shader returns > -1 to cue LLPostProcess on copying it from the framebuffer. + virtual S32 getDepthChannel() = 0; //If depth buffer is used in this shader returns > -1 to cue LLPostProcess on copying it from the framebuffer. + virtual QuadType bind() = 0; //Bind shader and textures, set up attribs. Returns the 'type' of quad to be drawn. + virtual bool draw(U32 pass) = 0; //returning false means finished. Used to update per-pass attributes and such. LLPostProcess will call + //drawOrthoQuad when this returns true, increment pass, then call this again, and keep repeating this until false is returned. + virtual void unbind() = 0; //Unbind shader and textures. + + LLSD getDefaults(); //Returns a full LLSD kvp list filled with default values. + void loadSettings(const LLSD& settings); //Parses the effects LLSD list and sets the member variables linked to them (via LLShaderSetting::setValue()) +}; + +//LLVector4 does not implicitly convert to and from LLSD, so template specilizations are necessary. +template<> LLSD LLPostProcessShader::LLShaderSetting::getDefaultValue(); +template<> void LLPostProcessShader::LLShaderSetting::setValue(const LLSD& value); class LLPostProcess : public LLSingleton { -public: - - typedef enum _QuadType { - QUAD_NORMAL, - QUAD_NOISE - } QuadType; - - /// GLSL Shader Encapsulation Struct - //typedef std::map glslUniforms; - - struct PostProcessTweaks : public LLSD { - inline PostProcessTweaks() : LLSD(LLSD::emptyMap()) - { - } - - inline LLSD & brightMult() { - return (*this)["brightness_multiplier"]; - } - - inline LLSD & noiseStrength() { - return (*this)["noise_strength"]; - } - - inline LLSD & noiseSize() { - return (*this)["noise_size"]; - } - - inline LLSD & brightness() { - return (*this)["brightness"]; - } - - inline LLSD & contrast() { - return (*this)["contrast"]; - } - - inline LLSD & contrastBaseR() { - return (*this)["contrast_base"][0]; - } - - inline LLSD & contrastBaseG() { - return (*this)["contrast_base"][1]; - } - - inline LLSD & contrastBaseB() { - return (*this)["contrast_base"][2]; - } - - inline LLSD & contrastBaseIntensity() { - return (*this)["contrast_base"][3]; - } - - inline LLSD & saturation() { - return (*this)["saturation"]; - } - - inline LLSD & useNightVisionShader() { - return (*this)["enable_night_vision"]; - } - - inline LLSD & useColorFilter() { - return (*this)["enable_color_filter"]; - } - - inline LLSD & useGaussBlurFilter() { - return (*this)["enable_gauss_blur"]; - } - - inline F32 getBrightMult() const { - return F32((*this)["brightness_multiplier"].asReal()); - } - - inline F32 getNoiseStrength() const { - return F32((*this)["noise_strength"].asReal()); - } - - inline F32 getNoiseSize() const { - return F32((*this)["noise_size"].asReal()); - } - - inline F32 getGamma() const { - return F32((*this)["gamma"].asReal()); - } - - inline F32 getBrightness() const { - return F32((*this)["brightness"].asReal()); - } - - inline F32 getContrast() const { - return F32((*this)["contrast"].asReal()); - } - - inline F32 getContrastBaseR() const { - return F32((*this)["contrast_base"][0].asReal()); - } - - inline F32 getContrastBaseG() const { - return F32((*this)["contrast_base"][1].asReal()); - } - - inline F32 getContrastBaseB() const { - return F32((*this)["contrast_base"][2].asReal()); - } - - inline F32 getContrastBaseIntensity() const { - return F32((*this)["contrast_base"][3].asReal()); - } - - inline F32 getSaturation() const { - return F32((*this)["saturation"].asReal()); - } - - inline LLSD & getGaussBlurPasses() { - return (*this)["gauss_blur_passes"]; - } - }; - - PostProcessTweaks tweaks; - - // the map of all availible effects - LLSD mAllEffects; - private: + std::list > mShaders; //List of all registered LLPostProcessShader instances. + LLPointer mVBO; - LLPointer mSceneRenderTexture ; + U32 mNextDrawTarget; //Need to pingpong between two rendertargets. Cannot sample target texture of currently bound FBO. + // However this is ONLY the case if fbos are actually supported, else swapping isn't needed. + LLRenderTarget mRenderTarget[2]; + U32 mDepthTexture; LLPointer mNoiseTexture ; + + U32 mScreenWidth; + U32 mScreenHeight; + F32 mNoiseTextureScale; + + // The name of currently selected effect in mAllEffectInfo + std::string mSelectedEffectName; + // The map of settings for currently selected effect. + LLSD mSelectedEffectInfo; + // The map of all availible effects + LLSD mAllEffectInfo; public: LLPostProcess(void); - ~LLPostProcess(void); +private: + // OpenGL initialization + void initialize(unsigned int width, unsigned int height); //Sets mScreenWidth and mScreenHeight + // calls createScreenTextures and createNoiseTexture + // creates VBO + void createScreenTextures(); //Creates color texture and depth texture(if needed). + void createNoiseTexture(); //Creates 'random' noise texture. - void apply(unsigned int width, unsigned int height); - void invalidate() ; - - // Cleanup of global data that's only inited once per class. +public: + // Teardown + // Called on destroyGL or cleanupClass. Releases VBOs, rendertargets and textures. + void destroyGL(); + // Cleanup of global data that's only inited once per class. static void cleanupClass(); - void setSelectedEffect(std::string const & effectName); - - inline std::string const & getSelectedEffect(void) const { - return mSelectedEffectName; - } - - void saveEffect(std::string const & effectName); - -private: - /// read in from file - std::string mShaderErrorString; - unsigned int mScreenWidth; - unsigned int mScreenHeight; - - float mNoiseTextureScale; - - // the name of currently selected effect in mAllEffects - // invariant: tweaks == mAllEffects[mSelectedEffectName] - std::string mSelectedEffectName; - - /// General functions - void initialize(unsigned int width, unsigned int height); - void doEffects(void); - void applyShaders(void); - bool shadersEnabled(void); - - /// Night Vision Functions - void applyNightVisionShader(void); - - /// Color Filter Functions - void applyColorFilterShader(void); - - /// Gaussian blur Filter Functions - void applyGaussBlurShader(void); - - /// OpenGL Helper Functions + // Setup for draw. void copyFrameBuffer(); - void createScreenTexture(); - void createNoiseTexture(); - bool checkError(void); - void drawOrthoQuad(QuadType type); + void bindNoise(U32 channel); + + // Draw + void renderEffects(unsigned int width, unsigned int height); //Entry point for newview. +private: + void doEffects(void); //Sets up viewmatrix, blits the framebuffer, then calls applyShaders. + void applyShaders(void); //Iterates over all active post shaders, manages binding, calls drawOrthoQuad for render. + void drawOrthoQuad(QuadType type); //Finally draws fullscreen quad with the shader currently bound. + +public: + // UI interaction + // Getters + inline LLSD const & getAllEffectInfo(void) const { return mAllEffectInfo; } + inline std::string const & getSelectedEffectName(void) const { return mSelectedEffectName; } + inline LLSD const & getSelectedEffectInfo(void) const { return mSelectedEffectInfo; } + // Setters + void setSelectedEffect(std::string const & effectName); + void setSelectedEffectValue(std::string const & setting, LLSD& value); + void resetSelectedEffect(); + void saveEffectAs(std::string const & effectName); }; #endif // LL_POSTPROCESS_H diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index d73b17a51..b952a0e4b 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -380,7 +380,7 @@ bool LLTexUnit::bind(LLRenderTarget* renderTarget, bool bindDepth) if (bindDepth) { - if (renderTarget->hasStencil()) + if (renderTarget->hasStencil() && renderTarget->getFBO()) { llerrs << "Cannot bind a render buffer for sampling. Allocate render target without a stencil buffer if sampling of depth buffer is required." << llendl; } diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 27fec6843..c75304957 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -93,7 +93,7 @@ void LLRenderTarget::resize(U32 resx, U32 resy, U32 color_fmt) if (mDepth) { //resize depth attachment - if (mStencil) + if (mStencil && mFBO) { //use render buffers where stencil buffers are in play glBindRenderbuffer(GL_RENDERBUFFER, mDepth); @@ -104,7 +104,10 @@ void LLRenderTarget::resize(U32 resx, U32 resy, U32 color_fmt) { gGL.getTexUnit(0)->bindManual(mUsage, mDepth); U32 internal_type = LLTexUnit::getInternalType(mUsage); - LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); + if(!mStencil) + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); + else + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH24_STENCIL8, mResX, mResY, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL, false); } sBytesAllocated += pix_diff*4; @@ -131,9 +134,10 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo mUsage = usage; mUseDepth = depth; - if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject) { + glGenFramebuffers(1, (GLuint *) &mFBO); + if (depth) { if (!allocateDepth()) @@ -143,8 +147,6 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo } } - glGenFramebuffers(1, (GLuint *) &mFBO); - if (mDepth) { glBindFramebuffer(GL_FRAMEBUFFER, mFBO); @@ -162,7 +164,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo } glBindFramebuffer(GL_FRAMEBUFFER, 0); } - + stop_glerror(); } @@ -255,7 +257,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) bool LLRenderTarget::allocateDepth() { - if (mStencil) + if (mStencil && mFBO) { //use render buffers where stencil buffers are in play glGenRenderbuffers(1, (GLuint *) &mDepth); @@ -267,13 +269,19 @@ bool LLRenderTarget::allocateDepth() } else { - LLImageGL::generateTextures(mUsage, GL_DEPTH_COMPONENT24, 1, &mDepth); + if(!mStencil) + LLImageGL::generateTextures(mUsage, GL_DEPTH_COMPONENT24, 1, &mDepth); + else + LLImageGL::generateTextures(mUsage, GL_DEPTH24_STENCIL8, 1, &mDepth); gGL.getTexUnit(0)->bindManual(mUsage, mDepth); U32 internal_type = LLTexUnit::getInternalType(mUsage); stop_glerror(); clear_glerror(); - LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); + if(!mStencil) + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); + else + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH24_STENCIL8, mResX, mResY, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL, false); gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); } @@ -337,7 +345,7 @@ void LLRenderTarget::release() { if (mDepth) { - if (mStencil) + if (mStencil && mFBO) { glDeleteRenderbuffers(1, (GLuint*) &mDepth); stop_glerror(); @@ -490,6 +498,7 @@ void LLRenderTarget::flush(bool fetch_depth) { gGL.getTexUnit(0)->bind(this); glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, 0, 0, 0, 0, mResX, mResY); + stop_glerror(); if (fetch_depth) { @@ -498,8 +507,10 @@ void LLRenderTarget::flush(bool fetch_depth) allocateDepth(); } - gGL.getTexUnit(0)->bind(this); - glCopyTexImage2D(LLTexUnit::getInternalType(mUsage), 0, GL_DEPTH24_STENCIL8, 0, 0, mResX, mResY, 0); + gGL.getTexUnit(0)->bind(this,true); + glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, 0, 0, 0, 0, mResX, mResY); + stop_glerror(); + //glCopyTexImage2D(LLTexUnit::getInternalType(mUsage), 0, GL_DEPTH24_STENCIL8, 0, 0, mResX, mResY, 0); } gGL.getTexUnit(0)->disable(); @@ -589,7 +600,7 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, } else { - if (mask == GL_DEPTH_BUFFER_BIT && source.mStencil != mStencil) + if (mask == GL_DEPTH_BUFFER_BIT && !mStencil && source.mStencil != mStencil) { stop_glerror(); diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index 8268f1984..d9a529ff0 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -151,6 +151,8 @@ public: //one renderable attachment (i.e. color buffer, depth buffer). bool isComplete() const; + U32 getFBO() const {return mFBO;} + static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; } protected: diff --git a/indra/newview/app_settings/shaders/class1/effects/PosterizeF.glsl b/indra/newview/app_settings/shaders/class1/effects/PosterizeF.glsl new file mode 100644 index 000000000..1da0cdbad --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/effects/PosterizeF.glsl @@ -0,0 +1,23 @@ +/** + * @file colorFilterF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#extension GL_ARB_texture_rectangle : enable + +#ifdef DEFINE_GL_FRAGCOLOR +out vec4 gl_FragColor; +#endif + +uniform sampler2DRect tex0; +uniform int layerCount; + +VARYING vec2 vary_texcoord0; + +void main(void) +{ + vec3 color = pow(floor(pow(vec3(texture2D(tex0, vary_texcoord0.st)),vec3(.6)) * layerCount)/layerCount,vec3(1.66666)); + gl_FragColor = vec4(color, 1.0); +} diff --git a/indra/newview/app_settings/windlight/postprocesseffects.xml b/indra/newview/app_settings/windlight/postprocesseffects.xml index 9261f3287..0914b275a 100644 --- a/indra/newview/app_settings/windlight/postprocesseffects.xml +++ b/indra/newview/app_settings/windlight/postprocesseffects.xml @@ -174,6 +174,8 @@ 0 enable_gauss_blur 0 + enable_posterize + 0 gauss_blur_passes 2 extract_high @@ -186,6 +188,8 @@ 0.40000000000000002 saturation 1 - + posterize_layers + 10 + \ No newline at end of file diff --git a/indra/newview/llfloaterdaycycle.cpp b/indra/newview/llfloaterdaycycle.cpp index 4f6b5fb5a..7028e5016 100644 --- a/indra/newview/llfloaterdaycycle.cpp +++ b/indra/newview/llfloaterdaycycle.cpp @@ -57,7 +57,6 @@ #include "lldaycyclemanager.h" #include "llwlparamset.h" #include "llwlparammanager.h" -#include "llpostprocess.h" #include "llfloaterwindlight.h" diff --git a/indra/newview/llfloaterpostprocess.cpp b/indra/newview/llfloaterpostprocess.cpp index 40eb5e048..dc4af9e94 100644 --- a/indra/newview/llfloaterpostprocess.cpp +++ b/indra/newview/llfloaterpostprocess.cpp @@ -115,17 +115,7 @@ LLFloaterPostProcess* LLFloaterPostProcess::instance() void LLFloaterPostProcess::onControlChanged(LLUICtrl* ctrl, void* userData) { - char const *VariableName = (char const *)userData; - char buf[256]; - S32 elem=0; - if(sscanf(VariableName,"%255[^[][%d]", buf, &elem) == 2) - { - LLPostProcess::getInstance()->tweaks[(const char*)buf][elem] = ctrl->getValue(); - } - else - { - LLPostProcess::getInstance()->tweaks[VariableName] = ctrl->getValue(); - } + LLPostProcess::getInstance()->setSelectedEffectValue((char const *)userData,ctrl->getValue()); } void LLFloaterPostProcess::onLoadEffect(void* userData) @@ -145,7 +135,7 @@ void LLFloaterPostProcess::onSaveEffect(void* userData) std::string effectName(editBox->getValue().asString()); - if (LLPostProcess::getInstance()->mAllEffects.has(effectName)) + if (LLPostProcess::getInstance()->getAllEffectInfo().has(effectName)) { LLSD payload; payload["effect_name"] = effectName; @@ -153,7 +143,7 @@ void LLFloaterPostProcess::onSaveEffect(void* userData) } else { - LLPostProcess::getInstance()->saveEffect(effectName); + LLPostProcess::getInstance()->saveEffectAs(effectName); sPostProcess->syncMenu(); } } @@ -175,7 +165,7 @@ bool LLFloaterPostProcess::saveAlertCallback(const LLSD& notification, const LLS // if they choose save, do it. Otherwise, don't do anything if (option == 0) { - LLPostProcess::getInstance()->saveEffect(notification["payload"]["effect_name"].asString()); + LLPostProcess::getInstance()->saveEffectAs(notification["payload"]["effect_name"].asString()); sPostProcess->syncMenu(); } @@ -209,17 +199,17 @@ void LLFloaterPostProcess::syncMenu() comboBox->removeall(); LLSD::map_const_iterator currEffect; - for(currEffect = LLPostProcess::getInstance()->mAllEffects.beginMap(); - currEffect != LLPostProcess::getInstance()->mAllEffects.endMap(); + for(currEffect = LLPostProcess::getInstance()->getAllEffectInfo().beginMap(); + currEffect != LLPostProcess::getInstance()->getAllEffectInfo().endMap(); ++currEffect) { comboBox->add(currEffect->first); } // set the current effect as selected. - comboBox->selectByValue(LLPostProcess::getInstance()->getSelectedEffect()); + comboBox->selectByValue(LLPostProcess::getInstance()->getSelectedEffectName()); - LLSD &tweaks = LLPostProcess::getInstance()->tweaks; + const LLSD &tweaks = LLPostProcess::getInstance()->getSelectedEffectInfo(); //Iterate down all uniforms handled by post-process shaders. Update any linked ui elements. for (LLSD::map_const_iterator it = tweaks.beginMap(); it != tweaks.endMap(); ++it) { diff --git a/indra/newview/llfloaterwater.cpp b/indra/newview/llfloaterwater.cpp index 04ef4e5af..0a72fdadf 100644 --- a/indra/newview/llfloaterwater.cpp +++ b/indra/newview/llfloaterwater.cpp @@ -62,7 +62,6 @@ #include "llwaterparamset.h" #include "llwaterparammanager.h" -#include "llpostprocess.h" #undef max diff --git a/indra/newview/llfloaterwindlight.cpp b/indra/newview/llfloaterwindlight.cpp index 942763ad8..d84eaf2a2 100644 --- a/indra/newview/llfloaterwindlight.cpp +++ b/indra/newview/llfloaterwindlight.cpp @@ -62,7 +62,6 @@ #include "llwlparamset.h" #include "llwlparammanager.h" -#include "llpostprocess.h" #undef max diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 18aaabef4..973ca7ad2 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -200,7 +200,6 @@ #include "llnamelistctrl.h" #include "llnamebox.h" #include "llnameeditor.h" -#include "llpostprocess.h" #include "llwlparammanager.h" #include "llwaterparammanager.h" #include "llagentlanguage.h" diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 8b8c8b703..2279197e5 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1009,7 +1009,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo if (LLPipeline::sRenderDeferred && !LLPipeline::sUnderWaterRender) { gPipeline.mDeferredScreen.flush(); - if(LLRenderTarget::sUseFBO) + if(gPipeline.mDeferredScreen.getFBO()) { LLRenderTarget::copyContentsToFramebuffer(gPipeline.mDeferredScreen, 0, 0, gPipeline.mDeferredScreen.getWidth(), gPipeline.mDeferredScreen.getHeight(), 0, 0, @@ -1021,7 +1021,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot, boo else { gPipeline.mScreen.flush(); - if(LLRenderTarget::sUseFBO) + if(gPipeline.mScreen.getFBO()) { LLRenderTarget::copyContentsToFramebuffer(gPipeline.mScreen, 0, 0, gPipeline.mScreen.getWidth(), gPipeline.mScreen.getHeight(), 0, 0, @@ -1283,14 +1283,12 @@ void render_ui(F32 zoom_factor, int subfield, bool tiling) if (to_texture) { gPipeline.renderBloom(gSnapshot, zoom_factor, subfield, tiling); - gPipeline.mScreen.flush(); //blit, etc. } - /// We copy the frame buffer straight into a texture here, - /// and then display it again with compositor effects. - /// Using render to texture would be faster/better, but I don't have a - /// grasp of their full display stack just yet. + if(gPipeline.canUseVertexShaders()) - LLPostProcess::getInstance()->apply(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight()); + { + LLPostProcess::getInstance()->renderEffects(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight()); + } render_hud_elements(); render_hud_attachments(); diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 6ab2cfd17..2b2de806f 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -170,6 +170,7 @@ LLGLSLShader gGlowExtractProgram(LLViewerShaderMgr::SHADER_EFFECT); //Not in LLGLSLShader gPostColorFilterProgram(LLViewerShaderMgr::SHADER_EFFECT); //Not in mShaderList LLGLSLShader gPostNightVisionProgram(LLViewerShaderMgr::SHADER_EFFECT); //Not in mShaderList LLGLSLShader gPostGaussianBlurProgram(LLViewerShaderMgr::SHADER_EFFECT); //Not in mShaderList +LLGLSLShader gPostPosterizeProgram(LLViewerShaderMgr::SHADER_EFFECT); //Not in mShaderList // Deferred rendering shaders LLGLSLShader gDeferredImpostorProgram(LLViewerShaderMgr::SHADER_DEFERRED); @@ -958,6 +959,23 @@ BOOL LLViewerShaderMgr::loadShadersEffects() gPostGaussianBlurProgram.uniform1i("tex0", 0); } } + + { + vector shaderUniforms; + shaderUniforms.reserve(1); + shaderUniforms.push_back("layerCount"); + + gPostPosterizeProgram.mName = "Posterize Shader (Post)"; + gPostPosterizeProgram.mShaderFiles.clear(); + gPostPosterizeProgram.mShaderFiles.push_back(make_pair("effects/PosterizeF.glsl", GL_FRAGMENT_SHADER_ARB)); + gPostPosterizeProgram.mShaderFiles.push_back(make_pair("interface/onetexturenocolorV.glsl", GL_VERTEX_SHADER_ARB)); + gPostPosterizeProgram.mShaderLevel = mVertexShaderLevel[SHADER_EFFECT]; + if(gPostPosterizeProgram.createShader(NULL, &shaderUniforms)) + { + gPostPosterizeProgram.bind(); + gPostPosterizeProgram.uniform1i("tex0", 0); + } + } #endif return success; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index acd55d913..d31c262ea 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -4839,7 +4839,7 @@ void LLViewerWindow::stopGL(BOOL save_state) if(LLPostProcess::instanceExists()) { - LLPostProcess::getInstance()->invalidate(); + LLPostProcess::getInstance()->destroyGL(); } gTextureList.destroyGL(save_state); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 9f4c7b1f7..ff866877d 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -54,6 +54,7 @@ #include "llglheaders.h" #include "llrender.h" #include "llwindow.h" +#include "llpostprocess.h" // newview includes #include "llagent.h" @@ -863,6 +864,9 @@ void LLPipeline::releaseGLBuffers() gBumpImageList.destroyGL(); LLVOAvatar::resetImpostors(); + + if(LLPostProcess::instanceExists()) + LLPostProcess::getInstance()->destroyGL(); } void LLPipeline::releaseLUTBuffers() @@ -6111,12 +6115,14 @@ void LLPipeline::doResetVertexBuffers() LLVOPartGroup::destroyGL(); + if(LLPostProcess::instanceExists()) + LLPostProcess::getInstance()->destroyGL(); + LLVertexBuffer::cleanupClass(); //delete all name pool caches LLGLNamePool::cleanupPools(); - if (LLVertexBuffer::sGLCount > 0) { llwarns << "VBO wipe failed -- " << LLVertexBuffer::sGLCount << " buffers remaining." << llendl; @@ -6923,7 +6929,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield, b } - if (LLRenderTarget::sUseFBO) + if (mScreen.getFBO()) { //copy depth buffer from mScreen to framebuffer LLRenderTarget::copyContentsToFramebuffer(mScreen, 0, 0, mScreen.getWidth(), mScreen.getHeight(), 0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); diff --git a/indra/newview/skins/default/xui/en-us/floater_post_process.xml b/indra/newview/skins/default/xui/en-us/floater_post_process.xml index 179a40e2e..bde532911 100644 --- a/indra/newview/skins/default/xui/en-us/floater_post_process.xml +++ b/indra/newview/skins/default/xui/en-us/floater_post_process.xml @@ -146,6 +146,25 @@ left="14" max_val="1" min_val="0" mouse_opaque="true" name="noise_strength" show_text="true" value="1.0" width="200" /> + + + + Layer Count + + +