diff --git a/lib/kwinglutils.cpp b/lib/kwinglutils.cpp index 8db291397d..50593af815 100644 --- a/lib/kwinglutils.cpp +++ b/lib/kwinglutils.cpp @@ -36,6 +36,7 @@ along with this program. If not, see . #include #include +#include #define DEBUG_GLRENDERTARGET 0 @@ -1081,6 +1082,221 @@ void GLShader::bindAttributeLocation(int index, const char* name) // TODO: relink the shader } +//**************************************** +// ShaderManager +//**************************************** +ShaderManager *ShaderManager::s_shaderManager = NULL; + +ShaderManager *ShaderManager::instance() +{ + if (!s_shaderManager) { + s_shaderManager = new ShaderManager(); + } + return s_shaderManager; +} + +void ShaderManager::cleanup() +{ + delete s_shaderManager; +} + +ShaderManager::ShaderManager() + : m_orthoShader(NULL) + , m_genericShader(NULL) + , m_colorShader(NULL) + , m_inited(false) + , m_valid(false) +{ + initShaders(); + m_inited = true; +} + +ShaderManager::~ShaderManager() +{ + while (!m_boundShaders.isEmpty()) { + popShader(); + } + delete m_orthoShader; + delete m_genericShader; + delete m_colorShader; +} + +GLShader *ShaderManager::getBoundShader() const +{ + if (m_boundShaders.isEmpty()) { + return NULL; + } else { + return m_boundShaders.top(); + } +} + +bool ShaderManager::isShaderBound() const +{ + return !m_boundShaders.isEmpty(); +} + +bool ShaderManager::isValid() const +{ + return m_valid; +} + +GLShader *ShaderManager::pushShader(ShaderType type, bool reset) +{ + if (m_inited && !m_valid) { + return NULL; + } + GLShader *shader; + switch (type) { + case SimpleShader: + shader = m_orthoShader; + break; + case GenericShader: + shader = m_genericShader; + break; + case ColorShader: + shader = m_colorShader; + break; + default: + return NULL; + } + + pushShader(shader); + if (reset) { + resetShader(type); + } + + return shader; +} + +void ShaderManager::pushShader(GLShader *shader) +{ + // only bind shader if it is not already bound + if (shader != getBoundShader()) { + shader->bind(); + } + m_boundShaders.push(shader); +} + +void ShaderManager::popShader() +{ + if (m_boundShaders.isEmpty()) { + return; + } + GLShader *shader = m_boundShaders.pop(); + if (m_boundShaders.isEmpty()) { + // no more shader bound - unbind + shader->unbind(); + } else if (shader != m_boundShaders.top()) { + // only rebind if a different shader is on top of stack + m_boundShaders.top()->bind(); + } +} + +void ShaderManager::initShaders() +{ + m_orthoShader = new GLShader(":/resources/scene-vertex.glsl", ":/resources/scene-fragment.glsl"); + if (m_orthoShader->isValid()) { + pushShader(SimpleShader, true); + popShader(); + kDebug(1212) << "Ortho Shader is valid"; + } + else { + delete m_orthoShader; + m_orthoShader = NULL; + kDebug(1212) << "Orho Shader is not valid"; + return; + } + m_genericShader = new GLShader( ":/resources/scene-generic-vertex.glsl", ":/resources/scene-fragment.glsl" ); + if (m_genericShader->isValid()) { + pushShader(GenericShader, true); + popShader(); + kDebug(1212) << "Generic Shader is valid"; + } + else { + delete m_genericShader; + m_genericShader = NULL; + delete m_orthoShader; + m_orthoShader = NULL; + kDebug(1212) << "Generic Shader is not valid"; + return; + } + m_colorShader = new GLShader(":/resources/scene-color-vertex.glsl", ":/resources/scene-color-fragment.glsl"); + if (m_colorShader->isValid()) { + pushShader(ColorShader, true); + popShader(); + kDebug(1212) << "Color Shader is valid"; + } else { + delete m_genericShader; + m_genericShader = NULL; + delete m_orthoShader; + m_orthoShader = NULL; + delete m_colorShader; + m_colorShader = NULL; + kDebug(1212) << "Color Scene Shader is not valid"; + return; + } + m_valid = true; +} + +void ShaderManager::resetShader(ShaderType type) +{ + // resetShader is either called from init or from push, we know that a built-in shader is bound + GLShader *shader = getBoundShader(); + switch (type) { + case SimpleShader: + shader->setUniform("displaySize", QVector2D(displayWidth(), displayHeight())); + // TODO: has to become offset + shader->setUniform("geometry", QVector4D(0, 0, 0, 0)); + shader->setUniform("debug", 0); + shader->setUniform("sample", 0); + // TODO: has to become textureSize + shader->setUniform("textureWidth", 1.0f); + shader->setUniform("textureHeight", 1.0f); + // TODO: has to become colorManiuplation + shader->setUniform("opacity", 1.0f); + shader->setUniform("brightness", 1.0f); + shader->setUniform("saturation", 1.0f); + break; + case GenericShader: { + shader->setUniform("debug", 0); + shader->setUniform("sample", 0); + QMatrix4x4 projection; + float fovy = 60.0f; + float aspect = 1.0f; + float zNear = 0.1f; + float zFar = 100.0f; + float ymax = zNear * tan(fovy * M_PI / 360.0f); + float ymin = -ymax; + float xmin = ymin * aspect; + float xmax = ymax * aspect; + projection.frustum(xmin, xmax, ymin, ymax, zNear, zFar); + shader->setUniform("projection", projection); + QMatrix4x4 modelview; + float scaleFactor = 1.1 * tan( fovy * M_PI / 360.0f )/ymax; + modelview.translate(xmin*scaleFactor, ymax*scaleFactor, -1.1); + modelview.scale((xmax-xmin)*scaleFactor/displayWidth(), -(ymax-ymin)*scaleFactor/displayHeight(), 0.001); + shader->setUniform("modelview", modelview); + const QMatrix4x4 identity; + shader->setUniform("screenTransformation", identity); + shader->setUniform("windowTransformation", identity); + // TODO: has to become textureSize + shader->setUniform("textureWidth", 1.0f); + shader->setUniform("textureHeight", 1.0f); + // TODO: has to become colorManiuplation + shader->setUniform("opacity", 1.0f); + shader->setUniform("brightness", 1.0f); + shader->setUniform("saturation", 1.0f); + break; + } + case ColorShader: + shader->setUniform("displaySize", QVector2D(displayWidth(), displayHeight())); + // TODO: has to become offset + shader->setUniform("geometry", QVector4D(0, 0, 0, 0)); + shader->setUniform("geometryColor", QVector4D(0, 0, 0, 1)); + break; + } +} + /*** GLRenderTarget ***/ bool GLRenderTarget::mSupported = false; diff --git a/lib/kwinglutils.h b/lib/kwinglutils.h index 761a0cc5fb..cc928848d0 100644 --- a/lib/kwinglutils.h +++ b/lib/kwinglutils.h @@ -31,6 +31,7 @@ along with this program. If not, see . #include #include #include +#include /** @addtogroup kwineffects */ /** @{ */ @@ -293,6 +294,121 @@ class KWIN_EXPORT GLShader float mTextureHeight; }; +/** + * @short Manager for Shaders. + * + * This class provides some built-in shaders to be used by both compositing scene and effects. + * The ShaderManager provides methods to bind a built-in or a custom shader and keeps track of + * the shaders which have been bound. When a shader is unbound the previously bound shader + * will be rebound. + * + * @author Martin Gräßlin + * @since 4.7 + **/ +class KWIN_EXPORT ShaderManager +{ + public: + /** + * Identifiers for built-in shaders available for effects and scene + **/ + enum ShaderType { + /** + * An orthographic projection shader able to render textured geometries. + * Expects a @c vec2 uniform @c offset describing the offset from top-left corner + * and defaults to @c (0/0). Expects a @c vec2 uniform @c textureSize to calculate + * normalized texture coordinates. Defaults to @c (1.0/1.0). And expects a @c vec3 + * uniform @c colorManiuplation, with @c x being opacity, @c y being brightness and + * @c z being saturation. All three values default to @c 1.0. + * The sampler uniform is @c sample and defaults to @c 0. + * The shader uses two vertex attributes @c vertex and @c texCoord. + **/ + SimpleShader, + /** + * A generic shader able to render transformed, textured geometries. + * This shader is mostly needed by the scene and not of much interest for effects. + * Effects can influence this shader through @link ScreenPaintData and @link WindowPaintData. + * The shader expects four @c mat4 uniforms @c projection, @c modelview, + * @c screenTransformation and @c windowTransformation. The fragment shader expect the + * same uniforms as the SimpleShader and the same vertex attributes are used. + **/ + GenericShader, + /** + * An orthographic shader to render simple colored geometries without texturing. + * Expects a @c vec2 uniform @c offset describing the offset from top-left corner + * and defaults to @c (0/0). The fragment shader expects a single @c vec4 uniform + * @c geometryColor, which defaults to fully opaque black. + * The Shader uses one vertex attribute @c vertex. + **/ + ColorShader + }; + + /** + * @return The currently bound shader or @c null if no shader is bound. + **/ + GLShader *getBoundShader() const; + + /** + * @return @c true if a shader is bound, @c false otherwise + **/ + bool isShaderBound() const; + /** + * @return @c true if the built-in shaders are valid, @c false otherwise + **/ + bool isValid() const; + + /** + * Binds the shader of specified @p type. + * To unbind the shader use @link popShader. A previous bound shader will be rebound. + * @param type The built-in shader to bind + * @param reset Whether all uniforms should be reset to their default values + * @return The bound shader or @c NULL if shaders are not valid + * @see popShader + **/ + GLShader *pushShader(ShaderType type, bool reset = false); + /** + * Binds the @p shader. + * To unbind the shader use @link popShader. A previous bound shader will be rebound. + * To bind a built-in shader use the more specific method. + * @param shader The shader to be bound + * @see popShader + **/ + void pushShader(GLShader *shader); + + /** + * Unbinds the currently bound shader and rebinds a previous stored shader. + * If there is no previous shader, no shader will be rebound. + * It is not safe to call this method if there is no bound shader. + * @see pushShader + * @see getBoundShader + **/ + void popShader(); + + /** + * @return a pointer to the ShaderManager instance + **/ + static ShaderManager *instance(); + + /** + * @internal + **/ + static void cleanup(); + + private: + ShaderManager(); + ~ShaderManager(); + + void initShaders(); + void resetShader(ShaderType type); + + QStack m_boundShaders; + GLShader *m_orthoShader; + GLShader *m_genericShader; + GLShader *m_colorShader; + bool m_inited; + bool m_valid; + static ShaderManager *s_shaderManager; +}; + /** * @short Render target object *