From c4bbbe1aafc38e6621380d9320b191544961df75 Mon Sep 17 00:00:00 2001 From: cutealien Date: Mon, 24 Apr 2023 14:46:09 +0000 Subject: [PATCH] Add IMaterialRendererServices::startUseProgram/stopUseProgram This makes it possible to set high-level shader constants (which are attached to shader programs) to be set outside of OnSetConstants. IShaderConstantSetCallBack::OnCreate has it set automatically now so it works the same as OnSetConstants. D3D9 and burnings both work different, so there hadn't been a problem with those. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6469 dfc29bdd-3216-0410-991c-e03cc46cb475 --- examples/10.Shaders/main.cpp | 33 ++++++++++++------- include/IMaterialRendererServices.h | 14 ++++++++ source/Irrlicht/COpenGLDriver.cpp | 6 ++++ source/Irrlicht/COpenGLDriver.h | 6 ++++ source/Irrlicht/COpenGLSLMaterialRenderer.cpp | 28 ++++++++++++---- source/Irrlicht/COpenGLSLMaterialRenderer.h | 2 ++ 6 files changed, 71 insertions(+), 18 deletions(-) diff --git a/examples/10.Shaders/main.cpp b/examples/10.Shaders/main.cpp index d426fbcf..a17f0e46 100644 --- a/examples/10.Shaders/main.cpp +++ b/examples/10.Shaders/main.cpp @@ -56,7 +56,7 @@ public: { if (UseHighLevelShaders) { - // get shader constants id. + // Get shader constants id. WorldViewProjID = services->getVertexShaderConstantID("mWorldViewProj"); TransWorldID = services->getVertexShaderConstantID("mTransWorld"); InvWorldID = services->getVertexShaderConstantID("mInvWorld"); @@ -68,6 +68,27 @@ public: if(driver->getDriverType() == video::EDT_OPENGL) TextureID = services->getVertexShaderConstantID("myTexture"); } + + // Set light color + // That could be set as well in OnSetConstants, but there's some cost to setting shader constants + // So when we have non-changing shader constants it's more performant to set them only once. + video::SColorf col(0.0f,1.0f,1.0f,0.0f); + if (UseHighLevelShaders) + { + services->setVertexShaderConstant(ColorID, reinterpret_cast(&col), 4); + + // Note: Since Irrlicht 1.9 it's possible to call setVertexShaderConstant + // from anywhere. To do that save the services pointer here in OnCreate, it + // won't change as long as you use one IShaderConstantSetCallBack per shader + // material. But when calling it ouside of IShaderConstantSetCallBack functions + // you have to call services->startUseProgram()stopUseProgram() before/after doing so. + // At least for high-level shader constants, low level constants are not attached + // to programs, so for those it doesn't matter. + // Doing that sometimes makes sense for performance reasons, like for constants which + // do only change once per frame or even less. + } + else + services->setVertexShaderConstant(reinterpret_cast(&col), 9, 1); } virtual void OnSetConstants(video::IMaterialRendererServices* services, @@ -109,16 +130,6 @@ public: else services->setVertexShaderConstant(reinterpret_cast(&pos), 8, 1); - // set light color - - video::SColorf col(0.0f,1.0f,1.0f,0.0f); - - if (UseHighLevelShaders) - services->setVertexShaderConstant(ColorID, - reinterpret_cast(&col), 4); - else - services->setVertexShaderConstant(reinterpret_cast(&col), 9, 1); - // set transposed world matrix core::matrix4 world = driver->getTransform(video::ETS_WORLD); diff --git a/include/IMaterialRendererServices.h b/include/IMaterialRendererServices.h index 7dcf342f..164926b7 100644 --- a/include/IMaterialRendererServices.h +++ b/include/IMaterialRendererServices.h @@ -41,6 +41,20 @@ public: //! Return an index constant for the vertex shader based on a name. virtual s32 getVertexShaderConstantID(const c8* name) = 0; + //! Call when you set shader constants outside of IShaderConstantSetCallBack + /** Only for high-level shader functions, aka those using an index instead of + an register. Shader constants are attached to shader programs, so if you want + to set them you have to make sure the correct shader program is in use. + IShaderConstantSetCallBack functions like OnSetConstants do that for you, + but if you want to set shader constants outside of those (usually for performance + reasons) call startUseProgram() before doing so and stopUseProgram() afterwards. + Note: Currently only necessary in OpenGL, but no real calling costs on other drivers. + */ + virtual void startUseProgram() {} + + //! Call this when you are done setting shader constants outside of OnCreate or OnSetConstants + virtual void stopUseProgram() {} + //! Sets a constant for the vertex shader based on a name. /** This can be used if you used a high level shader language like GLSL or HLSL to create a shader. Example: If you created a shader which has diff --git a/source/Irrlicht/COpenGLDriver.cpp b/source/Irrlicht/COpenGLDriver.cpp index 46c43ec5..fb33e09e 100644 --- a/source/Irrlicht/COpenGLDriver.cpp +++ b/source/Irrlicht/COpenGLDriver.cpp @@ -3727,7 +3727,9 @@ s32 COpenGLDriver::addShaderMaterial(const c8* vertexShaderProgram, r->drop(); if (callback && nr >= 0) + { callback->OnCreate(this, userData); + } return nr; } @@ -3764,7 +3766,11 @@ s32 COpenGLDriver::addHighLevelShaderMaterial( r->drop(); if (callback && nr >= 0) + { + r->startUseProgram(); callback->OnCreate(r, userData); + r->stopUseProgram(); + } return nr; } diff --git a/source/Irrlicht/COpenGLDriver.h b/source/Irrlicht/COpenGLDriver.h index 350a49cd..9df01afb 100644 --- a/source/Irrlicht/COpenGLDriver.h +++ b/source/Irrlicht/COpenGLDriver.h @@ -415,6 +415,12 @@ namespace video //! Get current material. const SMaterial& getCurrentMaterial() const; + //! Rest renderstates forcing stuff like OnSetMaterial to be called + void DoResetRenderStates() + { + ResetRenderStates = true; + } + COpenGLCacheHandler* getCacheHandler() const; private: diff --git a/source/Irrlicht/COpenGLSLMaterialRenderer.cpp b/source/Irrlicht/COpenGLSLMaterialRenderer.cpp index b2ba9f67..f506a458 100644 --- a/source/Irrlicht/COpenGLSLMaterialRenderer.cpp +++ b/source/Irrlicht/COpenGLSLMaterialRenderer.cpp @@ -237,13 +237,10 @@ void COpenGLSLMaterialRenderer::OnSetMaterial(const video::SMaterial& material, COpenGLCacheHandler* cacheHandler = Driver->getCacheHandler(); - if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) - { - if (Program2) - Driver->irrGlUseProgram(Program2); - else if (Program) - Driver->extGlUseProgramObject(Program); - } + if (Program2) + Driver->irrGlUseProgram(Program2); + else if (Program) + Driver->extGlUseProgramObject(Program); Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); @@ -572,6 +569,23 @@ void COpenGLSLMaterialRenderer::setBasicRenderStates(const SMaterial& material, Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); } +void COpenGLSLMaterialRenderer::startUseProgram() +{ + if (Program2) + Driver->irrGlUseProgram(Program2); + else if (Program) + Driver->extGlUseProgramObject(Program); +} + +void COpenGLSLMaterialRenderer::stopUseProgram() +{ + // Not going to reset irrGlUseProgram/extGlUseProgramObject as it shouldn't really matter + + // Force reset of material to ensure OnSetMaterial will be called or we can miss + // the next UseProgram call + Driver->DoResetRenderStates(); +} + s32 COpenGLSLMaterialRenderer::getVertexShaderConstantID(const c8* name) { return getPixelShaderConstantID(name); diff --git a/source/Irrlicht/COpenGLSLMaterialRenderer.h b/source/Irrlicht/COpenGLSLMaterialRenderer.h index 549e27cd..92605a73 100644 --- a/source/Irrlicht/COpenGLSLMaterialRenderer.h +++ b/source/Irrlicht/COpenGLSLMaterialRenderer.h @@ -73,6 +73,8 @@ public: // implementations for IMaterialRendererServices virtual void setBasicRenderStates(const SMaterial& material, const SMaterial& lastMaterial, bool resetAllRenderstates) IRR_OVERRIDE; + virtual void startUseProgram() IRR_OVERRIDE; + virtual void stopUseProgram() IRR_OVERRIDE; virtual s32 getVertexShaderConstantID(const c8* name) IRR_OVERRIDE; virtual s32 getPixelShaderConstantID(const c8* name) IRR_OVERRIDE; virtual void setVertexShaderConstant(const f32* data, s32 startRegister, s32 constantAmount=1) IRR_OVERRIDE;