New ShaderManager

The ShaderManager provides the built-in shaders for scene and effects.
Scene and effects can simply push one of those built-in shaders or a
custom one on a stack ensuring that the previously bound shader will be
rebound when the shader is poped again.

The class will be extended to not only have built-in shaders but also to
load custom shaders and being the only way to bind a shader at all.
This commit is contained in:
Martin Gräßlin 2010-12-11 10:25:46 +01:00
parent 1911a81cd7
commit 3c6e7309f2
2 changed files with 332 additions and 0 deletions

View file

@ -36,6 +36,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QVector4D>
#include <QMatrix4x4>
#include <math.h>
#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;

View file

@ -31,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QtGui/QImage>
#include <QtCore/QSize>
#include <QtCore/QSharedData>
#include <QtCore/QStack>
/** @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 <kde@martin-graesslin.com>
* @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<GLShader*> 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
*