Add support for generating shaders at runtime
This adds new API in ShaderManager that makes it possible to request a shader based on a set of traits. ShaderManager generates these shaders on demand and caches them in a hash table.
This commit is contained in:
parent
623ac3faa3
commit
afcc8337de
2 changed files with 193 additions and 0 deletions
|
@ -661,6 +661,162 @@ ShaderManager::~ShaderManager()
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
delete m_shader[i];
|
delete m_shader[i];
|
||||||
|
|
||||||
|
qDeleteAll(m_shaderHash);
|
||||||
|
m_shaderHash.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ShaderManager::generateVertexSource(ShaderTraits traits) const
|
||||||
|
{
|
||||||
|
QByteArray source;
|
||||||
|
QTextStream stream(&source);
|
||||||
|
|
||||||
|
GLPlatform * const gl = GLPlatform::instance();
|
||||||
|
QByteArray attribute, varying;
|
||||||
|
|
||||||
|
if (!gl->isGLES()) {
|
||||||
|
const bool glsl_140 = gl->glslVersion() >= kVersionNumber(1, 40);
|
||||||
|
|
||||||
|
attribute = glsl_140 ? QByteArrayLiteral("in") : QByteArrayLiteral("attribute");
|
||||||
|
varying = glsl_140 ? QByteArrayLiteral("out") : QByteArrayLiteral("varying");
|
||||||
|
|
||||||
|
if (glsl_140)
|
||||||
|
stream << "#version 140\n\n";
|
||||||
|
} else {
|
||||||
|
const bool glsl_es_300 = gl->glslVersion() >= kVersionNumber(3, 0);
|
||||||
|
|
||||||
|
attribute = glsl_es_300 ? QByteArrayLiteral("in") : QByteArrayLiteral("attribute");
|
||||||
|
varying = glsl_es_300 ? QByteArrayLiteral("out") : QByteArrayLiteral("varying");
|
||||||
|
|
||||||
|
if (glsl_es_300)
|
||||||
|
stream << "#version 300 es\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << attribute << " vec4 position;\n";
|
||||||
|
if (traits & ShaderTrait::MapTexture) {
|
||||||
|
stream << attribute << " vec4 texcoord;\n\n";
|
||||||
|
stream << varying << " vec2 texcoord0;\n\n";
|
||||||
|
} else
|
||||||
|
stream << "\n";
|
||||||
|
|
||||||
|
stream << "uniform mat4 modelViewProjectionMatrix;\n\n";
|
||||||
|
|
||||||
|
stream << "void main()\n{\n";
|
||||||
|
if (traits & ShaderTrait::MapTexture)
|
||||||
|
stream << " texcoord0 = texcoord.st;\n";
|
||||||
|
|
||||||
|
stream << " gl_Position = modelViewProjectionMatrix * position;\n";
|
||||||
|
stream << "}\n";
|
||||||
|
|
||||||
|
stream.flush();
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ShaderManager::generateFragmentSource(ShaderTraits traits) const
|
||||||
|
{
|
||||||
|
QByteArray source;
|
||||||
|
QTextStream stream(&source);
|
||||||
|
|
||||||
|
GLPlatform * const gl = GLPlatform::instance();
|
||||||
|
QByteArray varying, output, textureLookup;
|
||||||
|
|
||||||
|
if (!gl->isGLES()) {
|
||||||
|
const bool glsl_140 = gl->glslVersion() >= kVersionNumber(1, 40);
|
||||||
|
|
||||||
|
if (glsl_140)
|
||||||
|
stream << "#version 140\n\n";
|
||||||
|
|
||||||
|
varying = glsl_140 ? QByteArrayLiteral("in") : QByteArrayLiteral("varying");
|
||||||
|
textureLookup = glsl_140 ? QByteArrayLiteral("texture") : QByteArrayLiteral("texture2D");
|
||||||
|
output = glsl_140 ? QByteArrayLiteral("fragColor") : QByteArrayLiteral("gl_FragColor");
|
||||||
|
} else {
|
||||||
|
const bool glsl_es_300 = GLPlatform::instance()->glslVersion() >= kVersionNumber(3, 0);
|
||||||
|
|
||||||
|
if (glsl_es_300)
|
||||||
|
stream << "#version 300 es\n\n";
|
||||||
|
|
||||||
|
// From the GLSL ES specification:
|
||||||
|
//
|
||||||
|
// "The fragment language has no default precision qualifier for floating point types."
|
||||||
|
stream << "precision highp float;\n\n";
|
||||||
|
|
||||||
|
varying = glsl_es_300 ? QByteArrayLiteral("in") : QByteArrayLiteral("varying");
|
||||||
|
textureLookup = glsl_es_300 ? QByteArrayLiteral("texture") : QByteArrayLiteral("texture2D");
|
||||||
|
output = glsl_es_300 ? QByteArrayLiteral("fragColor") : QByteArrayLiteral("gl_FragColor");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traits & ShaderTrait::MapTexture) {
|
||||||
|
stream << "uniform sampler2D sampler;\n";
|
||||||
|
|
||||||
|
if (traits & ShaderTrait::Modulate)
|
||||||
|
stream << "uniform vec4 modulation;\n";
|
||||||
|
if (traits & ShaderTrait::AdjustSaturation)
|
||||||
|
stream << "uniform float saturation;\n";
|
||||||
|
|
||||||
|
stream << "\n" << varying << " vec2 texcoord0;\n";
|
||||||
|
|
||||||
|
} else if (traits & ShaderTrait::UniformColor)
|
||||||
|
stream << "uniform vec4 geometryColor;\n";
|
||||||
|
|
||||||
|
if (output != QByteArrayLiteral("gl_FragColor"))
|
||||||
|
stream << "\nout vec4 " << output << ";\n";
|
||||||
|
|
||||||
|
stream << "\nvoid main(void)\n{\n";
|
||||||
|
if (traits & ShaderTrait::MapTexture) {
|
||||||
|
if (traits & (ShaderTrait::Modulate | ShaderTrait::AdjustSaturation)) {
|
||||||
|
stream << " vec4 texel = " << textureLookup << "(sampler, texcoord0);\n";
|
||||||
|
|
||||||
|
if (traits & ShaderTrait::Modulate)
|
||||||
|
stream << " texel *= modulation;\n";
|
||||||
|
if (traits & ShaderTrait::AdjustSaturation)
|
||||||
|
stream << " texel.rgb = mix(vec3(dot(texel.rgb, vec3(0.2126, 0.7152, 0.0722))), texel.rgb, saturation);\n";
|
||||||
|
|
||||||
|
stream << " " << output << " = texel;\n";
|
||||||
|
} else {
|
||||||
|
stream << " " << output << " = " << textureLookup << "(sampler, texcoord0);\n";
|
||||||
|
}
|
||||||
|
} else if (traits & ShaderTrait::UniformColor)
|
||||||
|
stream << " " << output << " = geometryColor;\n";
|
||||||
|
|
||||||
|
stream << "}";
|
||||||
|
stream.flush();
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLShader *ShaderManager::generateShader(ShaderTraits traits)
|
||||||
|
{
|
||||||
|
const QByteArray vertex = generateVertexSource(traits);
|
||||||
|
const QByteArray fragment = generateFragmentSource(traits);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
qDebug() << "**************";
|
||||||
|
qDebug() << vertex;
|
||||||
|
qDebug() << "**************";
|
||||||
|
qDebug() << fragment;
|
||||||
|
qDebug() << "**************";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GLShader *shader = new GLShader(GLShader::ExplicitLinking);
|
||||||
|
shader->load(vertex, fragment);
|
||||||
|
|
||||||
|
shader->bindAttributeLocation("position", VA_Position);
|
||||||
|
shader->bindAttributeLocation("texcoord", VA_TexCoord);
|
||||||
|
shader->bindFragDataLocation("fragColor", 0);
|
||||||
|
|
||||||
|
shader->link();
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLShader *ShaderManager::shader(ShaderTraits traits)
|
||||||
|
{
|
||||||
|
GLShader *shader = m_shaderHash.value(traits);
|
||||||
|
|
||||||
|
if (!shader) {
|
||||||
|
shader = generateShader(traits);
|
||||||
|
m_shaderHash.insert(traits, shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLShader *ShaderManager::getBoundShader() const
|
GLShader *ShaderManager::getBoundShader() const
|
||||||
|
@ -687,6 +843,13 @@ bool ShaderManager::isShaderDebug() const
|
||||||
return m_debug;
|
return m_debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLShader *ShaderManager::pushShader(ShaderTraits traits)
|
||||||
|
{
|
||||||
|
GLShader *shader = this->shader(traits);
|
||||||
|
pushShader(shader);
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
GLShader *ShaderManager::pushShader(ShaderType type, bool reset)
|
GLShader *ShaderManager::pushShader(ShaderType type, bool reset)
|
||||||
{
|
{
|
||||||
if (m_inited && !m_valid) {
|
if (m_inited && !m_valid) {
|
||||||
|
|
|
@ -201,6 +201,17 @@ private:
|
||||||
friend class ShaderManager;
|
friend class ShaderManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class ShaderTrait {
|
||||||
|
MapTexture = (1 << 0),
|
||||||
|
UniformColor = (1 << 1),
|
||||||
|
Modulate = (1 << 2),
|
||||||
|
AdjustSaturation = (1 << 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_FLAGS(ShaderTraits, ShaderTrait)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @short Manager for Shaders.
|
* @short Manager for Shaders.
|
||||||
*
|
*
|
||||||
|
@ -249,6 +260,11 @@ public:
|
||||||
ColorShader
|
ColorShader
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a shader with the given traits, creating it if necessary.
|
||||||
|
*/
|
||||||
|
GLShader *shader(ShaderTraits traits);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The currently bound shader or @c null if no shader is bound.
|
* @return The currently bound shader or @c null if no shader is bound.
|
||||||
**/
|
**/
|
||||||
|
@ -282,6 +298,13 @@ public:
|
||||||
* @see popShader
|
* @see popShader
|
||||||
**/
|
**/
|
||||||
GLShader *pushShader(ShaderType type, bool reset = false);
|
GLShader *pushShader(ShaderType type, bool reset = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes the current shader onto the stack and binds a shader
|
||||||
|
* with the given traits.
|
||||||
|
*/
|
||||||
|
GLShader *pushShader(ShaderTraits traits);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds the @p shader.
|
* Binds the @p shader.
|
||||||
* To unbind the shader use @link popShader. A previous bound shader will be rebound.
|
* To unbind the shader use @link popShader. A previous bound shader will be rebound.
|
||||||
|
@ -363,8 +386,13 @@ private:
|
||||||
void bindFragDataLocations(GLShader *shader);
|
void bindFragDataLocations(GLShader *shader);
|
||||||
void bindAttributeLocations(GLShader *shader) const;
|
void bindAttributeLocations(GLShader *shader) const;
|
||||||
|
|
||||||
|
QByteArray generateVertexSource(ShaderTraits traits) const;
|
||||||
|
QByteArray generateFragmentSource(ShaderTraits traits) const;
|
||||||
|
GLShader *generateShader(ShaderTraits traits);
|
||||||
|
|
||||||
QStack<GLShader*> m_boundShaders;
|
QStack<GLShader*> m_boundShaders;
|
||||||
GLShader *m_shader[3];
|
GLShader *m_shader[3];
|
||||||
|
QHash<ShaderTraits, GLShader *> m_shaderHash;
|
||||||
bool m_inited;
|
bool m_inited;
|
||||||
bool m_valid;
|
bool m_valid;
|
||||||
bool m_debug;
|
bool m_debug;
|
||||||
|
@ -779,6 +807,8 @@ private:
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::ShaderTraits)
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue