mirror of
https://github.com/love2d/love.git
synced 2026-08-13 09:00:54 +02:00
Improved error messages when sending bad values to a shader (also issue #587)
This commit is contained in:
@@ -50,7 +50,7 @@ namespace
|
||||
if (prevShader != NULL)
|
||||
prevShader->attach();
|
||||
else
|
||||
Shader::detach();
|
||||
curShader->detach();
|
||||
}
|
||||
|
||||
Shader *curShader;
|
||||
@@ -188,6 +188,39 @@ void Shader::createProgram(const std::vector<GLuint> &shaderids)
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::mapActiveUniforms()
|
||||
{
|
||||
uniforms.clear();
|
||||
|
||||
GLint numuniforms;
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numuniforms);
|
||||
|
||||
GLsizei bufsize;
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, (GLint *) &bufsize);
|
||||
|
||||
for (int i = 0; i < numuniforms; i++)
|
||||
{
|
||||
GLchar *cname = new GLchar[bufsize];
|
||||
GLsizei namelength;
|
||||
|
||||
Uniform u;
|
||||
|
||||
glGetActiveUniform(program, (GLuint) i, bufsize, &namelength, &u.count, &u.type, cname);
|
||||
|
||||
u.name = std::string(cname, (size_t) namelength);
|
||||
u.location = glGetUniformLocation(program, u.name.c_str());
|
||||
|
||||
delete[] cname;
|
||||
|
||||
// glGetActiveUniform appends "[0]" to the end of array uniform names...
|
||||
if (u.name.find("[0]") == u.name.length() - 3)
|
||||
u.name.erase(u.name.length() - 3);
|
||||
|
||||
if (u.location != -1)
|
||||
uniforms[u.name] = u;
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::loadVolatile()
|
||||
{
|
||||
// zero out active texture list
|
||||
@@ -208,6 +241,8 @@ bool Shader::loadVolatile()
|
||||
|
||||
createProgram(shaderids);
|
||||
|
||||
mapActiveUniforms();
|
||||
|
||||
if (current == this)
|
||||
{
|
||||
current = NULL; // make sure glUseProgram gets called
|
||||
@@ -307,39 +342,127 @@ void Shader::detach()
|
||||
current = NULL;
|
||||
}
|
||||
|
||||
const Shader::Uniform &Shader::getUniform(const std::string &name) const
|
||||
{
|
||||
std::map<std::string, Uniform>::const_iterator it = uniforms.find(name);
|
||||
|
||||
if (it == uniforms.end())
|
||||
throw love::Exception("Variable '%s' does not exist.\n"
|
||||
"A common error is to define but not use the variable.", name.c_str());
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int Shader::getUniformTypeSize(GLenum type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_INT:
|
||||
case GL_FLOAT:
|
||||
case GL_BOOL:
|
||||
case GL_SAMPLER_1D:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
return 1;
|
||||
case GL_INT_VEC2:
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_BOOL_VEC2:
|
||||
return 2;
|
||||
case GL_INT_VEC3:
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_BOOL_VEC3:
|
||||
return 3;
|
||||
case GL_INT_VEC4:
|
||||
case GL_FLOAT_VEC4:
|
||||
case GL_BOOL_VEC4:
|
||||
return 4;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Shader::UniformType Shader::getUniformBaseType(GLenum type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_INT:
|
||||
case GL_INT_VEC2:
|
||||
case GL_INT_VEC3:
|
||||
case GL_INT_VEC4:
|
||||
return UNIFORM_INT;
|
||||
case GL_FLOAT:
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_FLOAT_VEC4:
|
||||
return UNIFORM_FLOAT;
|
||||
case GL_BOOL:
|
||||
case GL_BOOL_VEC2:
|
||||
case GL_BOOL_VEC3:
|
||||
case GL_BOOL_VEC4:
|
||||
return UNIFORM_BOOL;
|
||||
case GL_SAMPLER_1D:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
return UNIFORM_SAMPLER;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return UNIFORM_UNKNOWN;
|
||||
}
|
||||
|
||||
void Shader::checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const
|
||||
{
|
||||
if (!program)
|
||||
throw love::Exception("No active shader program.");
|
||||
|
||||
int realsize = getUniformTypeSize(u.type);
|
||||
|
||||
if (size != realsize)
|
||||
throw love::Exception("Value size of %d does not match variable size of %d.", size, realsize);
|
||||
|
||||
if ((u.count == 1 && count > 1) || count < 0)
|
||||
throw love::Exception("Invalid number of values (expected %d, got %d).", u.count, count);
|
||||
|
||||
UniformType basetype = getUniformBaseType(u.type);
|
||||
|
||||
if (basetype == UNIFORM_SAMPLER && sendtype != UNIFORM_SAMPLER)
|
||||
throw love::Exception("Cannot send a value of this type to an Image variable.");
|
||||
|
||||
if (sendtype == UNIFORM_FLOAT && basetype == UNIFORM_INT)
|
||||
throw love::Exception("Cannot convert between float and int.");
|
||||
}
|
||||
|
||||
void Shader::sendFloat(const std::string &name, int size, const GLfloat *vec, int count)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
GLint location = getUniformLocation(name);
|
||||
|
||||
if (size < 1 || size > 4)
|
||||
throw love::Exception("Invalid variable size: %d (expected 1-4).", size);
|
||||
const Uniform &u = getUniform(name);
|
||||
checkSetUniformError(u, size, count, UNIFORM_FLOAT);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 4:
|
||||
glUniform4fv(location, count, vec);
|
||||
glUniform4fv(u.location, count, vec);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3fv(location, count, vec);
|
||||
glUniform3fv(u.location, count, vec);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2fv(location, count, vec);
|
||||
glUniform2fv(u.location, count, vec);
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
glUniform1fv(location, count, vec);
|
||||
glUniform1fv(u.location, count, vec);
|
||||
break;
|
||||
}
|
||||
|
||||
// throw error if needed
|
||||
checkSetUniformError();
|
||||
}
|
||||
|
||||
void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int count)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
GLint location = getUniformLocation(name);
|
||||
|
||||
if (size < 2 || size > 4)
|
||||
{
|
||||
@@ -347,40 +470,41 @@ void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int
|
||||
"(can only set 2x2, 3x3 or 4x4 matrices).", size,size);
|
||||
}
|
||||
|
||||
const Uniform &u = getUniform(name);
|
||||
checkSetUniformError(u, size, count, UNIFORM_FLOAT);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 4:
|
||||
glUniformMatrix4fv(location, count, GL_FALSE, m);
|
||||
glUniformMatrix4fv(u.location, count, GL_FALSE, m);
|
||||
break;
|
||||
case 3:
|
||||
glUniformMatrix3fv(location, count, GL_FALSE, m);
|
||||
glUniformMatrix3fv(u.location, count, GL_FALSE, m);
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
glUniformMatrix2fv(location, count, GL_FALSE, m);
|
||||
glUniformMatrix2fv(u.location, count, GL_FALSE, m);
|
||||
break;
|
||||
}
|
||||
|
||||
// throw error if needed
|
||||
checkSetUniformError();
|
||||
}
|
||||
|
||||
void Shader::sendTexture(const std::string &name, GLuint texture)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
GLint location = getUniformLocation(name);
|
||||
|
||||
int textureunit = getTextureUnit(name);
|
||||
|
||||
const Uniform &u = getUniform(name);
|
||||
checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
|
||||
|
||||
// bind texture to assigned texture unit and send uniform to shader program
|
||||
gl.bindTextureToUnit(texture, textureunit, false);
|
||||
glUniform1i(location, textureunit);
|
||||
|
||||
glUniform1i(u.location, textureunit);
|
||||
|
||||
// reset texture unit
|
||||
gl.setActiveTextureUnit(0);
|
||||
|
||||
// throw error if needed
|
||||
checkSetUniformError();
|
||||
|
||||
// increment global shader texture id counter for this texture unit, if we haven't already
|
||||
if (activeTextureUnits[textureunit-1] == 0)
|
||||
++textureCounters[textureunit-1];
|
||||
@@ -399,24 +523,6 @@ void Shader::sendCanvas(const std::string &name, const Canvas &canvas)
|
||||
sendTexture(name, canvas.getTextureName());
|
||||
}
|
||||
|
||||
GLint Shader::getUniformLocation(const std::string &name)
|
||||
{
|
||||
std::map<std::string, GLint>::const_iterator it = uniforms.find(name);
|
||||
if (it != uniforms.end())
|
||||
return it->second;
|
||||
|
||||
GLint location = glGetUniformLocation(program, name.c_str());
|
||||
if (location == -1)
|
||||
{
|
||||
throw love::Exception(
|
||||
"Cannot get location of shader variable `%s'.\n"
|
||||
"A common error is to define but not use the variable.", name.c_str());
|
||||
}
|
||||
|
||||
uniforms[name] = location;
|
||||
return location;
|
||||
}
|
||||
|
||||
int Shader::getTextureUnit(const std::string &name)
|
||||
{
|
||||
std::map<std::string, GLint>::const_iterator it = textureUnitPool.find(name);
|
||||
@@ -446,19 +552,6 @@ int Shader::getTextureUnit(const std::string &name)
|
||||
return textureunit;
|
||||
}
|
||||
|
||||
void Shader::checkSetUniformError()
|
||||
{
|
||||
GLenum error_code = glGetError();
|
||||
if (GL_INVALID_OPERATION == error_code)
|
||||
{
|
||||
throw love::Exception(
|
||||
"Invalid operation:\n"
|
||||
"- Trying to send the wrong value type to shader variable, or\n"
|
||||
"- Trying to send array values with wrong dimension, or\n"
|
||||
"- Invalid variable name.");
|
||||
}
|
||||
}
|
||||
|
||||
std::string Shader::getGLSLVersion()
|
||||
{
|
||||
// GL_SHADING_LANGUAGE_VERSION may not be available in OpenGL < 2.0.
|
||||
|
||||
@@ -127,8 +127,33 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
GLint getUniformLocation(const std::string &name);
|
||||
void checkSetUniformError();
|
||||
// Represents a single uniform/extern shader variable.
|
||||
struct Uniform
|
||||
{
|
||||
GLint location;
|
||||
GLint count;
|
||||
GLenum type;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Types of potential uniform variables used in love's shaders.
|
||||
enum UniformType
|
||||
{
|
||||
UNIFORM_FLOAT,
|
||||
UNIFORM_INT,
|
||||
UNIFORM_BOOL,
|
||||
UNIFORM_SAMPLER,
|
||||
UNIFORM_UNKNOWN
|
||||
};
|
||||
|
||||
// Map active uniform names to their locations.
|
||||
void mapActiveUniforms();
|
||||
|
||||
const Uniform &getUniform(const std::string &name) const;
|
||||
|
||||
int getUniformTypeSize(GLenum type) const;
|
||||
UniformType getUniformBaseType(GLenum type) const;
|
||||
void checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const;
|
||||
|
||||
GLuint compileCode(ShaderType type, const std::string &code);
|
||||
void createProgram(const std::vector<GLuint> &shaderids);
|
||||
@@ -146,10 +171,11 @@ private:
|
||||
// Shader compiler warning strings for individual shader stages.
|
||||
std::map<ShaderType, std::string> shaderWarnings;
|
||||
|
||||
GLuint program; // volatile
|
||||
// volatile
|
||||
GLuint program;
|
||||
|
||||
// Uniform location buffer map
|
||||
std::map<std::string, GLint> uniforms;
|
||||
std::map<std::string, Uniform> uniforms;
|
||||
|
||||
// Texture unit pool for setting images
|
||||
std::map<std::string, GLint> textureUnitPool; // textureUnitPool[name] = textureunit
|
||||
|
||||
@@ -1496,6 +1496,16 @@ void main() {
|
||||
end
|
||||
end
|
||||
|
||||
local function shader_send_protected(self, name, value, ...)
|
||||
local success, err = pcall(shader_dispatch_send, self, name, value, ...)
|
||||
if not success then
|
||||
if err and type(err) == "string" then
|
||||
err = err:gsub("^(.-):(%d+): ", "")
|
||||
end
|
||||
error(err, 2)
|
||||
end
|
||||
end
|
||||
|
||||
local newShader = love.graphics.newShader
|
||||
function love.graphics.newShader(vertexcode, pixelcode)
|
||||
love.graphics.newShader = newShader
|
||||
@@ -1503,7 +1513,7 @@ void main() {
|
||||
local success, shader = pcall(love.graphics.newShader, vertexcode, pixelcode)
|
||||
if success then
|
||||
local meta = getmetatable(shader)
|
||||
meta.send = shader_dispatch_send
|
||||
meta.send = shader_send_protected
|
||||
meta.sendBoolean = meta.sendFloat
|
||||
return shader
|
||||
else
|
||||
|
||||
@@ -6679,6 +6679,27 @@ const unsigned char graphics_lua[] =
|
||||
0x65, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x2e, 0x22, 0x29, 0x2e, 0x22, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x68,
|
||||
0x61, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65,
|
||||
0x64, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x2c, 0x20, 0x2e, 0x2e, 0x2e, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x65,
|
||||
0x72, 0x72, 0x20, 0x3d, 0x20, 0x70, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5f,
|
||||
0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x2c, 0x20, 0x73, 0x65, 0x6c,
|
||||
0x66, 0x2c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x2e, 0x2e,
|
||||
0x2e, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x74,
|
||||
0x68, 0x65, 0x6e, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x65, 0x72, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65,
|
||||
0x28, 0x65, 0x72, 0x72, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20,
|
||||
0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x09, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x72, 0x72, 0x3a, 0x67, 0x73, 0x75, 0x62,
|
||||
0x28, 0x22, 0x5e, 0x28, 0x2e, 0x2d, 0x29, 0x3a, 0x28, 0x25, 0x64, 0x2b, 0x29, 0x3a, 0x20, 0x22, 0x2c, 0x20,
|
||||
0x22, 0x22, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x2c, 0x20, 0x32, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6e, 0x65, 0x77, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x3d,
|
||||
0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6e, 0x65, 0x77,
|
||||
0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x0a,
|
||||
@@ -6698,13 +6719,13 @@ const unsigned char graphics_lua[] =
|
||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x67, 0x65,
|
||||
0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x73, 0x68, 0x61,
|
||||
0x64, 0x65, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61,
|
||||
0x6e, 0x20, 0x3d, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x0a,
|
||||
0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x32,
|
||||
0x29, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x73, 0x68,
|
||||
0x61, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x32, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x65, 0x6e, 0x64, 0x0a,
|
||||
|
||||
Reference in New Issue
Block a user