Shader uniforms keep their values after love.window.setMode (resolves issue #1174).

Also allow arrays of images/samplers in shaders, although driver support is iffy and you have to index into the array using a compile-time constant (which might exclude loop indexing).

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2016-11-21 18:28:16 -04:00
parent 639c229bd7
commit 9d2ee6d909
6 changed files with 351 additions and 242 deletions
+3
View File
@@ -151,6 +151,9 @@ Canvas::~Canvas()
bool Canvas::loadVolatile() bool Canvas::loadVolatile()
{ {
if (texture != 0)
return true;
OpenGL::TempDebugGroup debuggroup("Canvas load"); OpenGL::TempDebugGroup debuggroup("Canvas load");
fbo = texture = 0; fbo = texture = 0;
+3
View File
@@ -281,6 +281,9 @@ void Image::loadFromImageData()
bool Image::loadVolatile() bool Image::loadVolatile()
{ {
if (texture != 0)
return true;
OpenGL::TempDebugGroup debuggroup("Image load"); OpenGL::TempDebugGroup debuggroup("Image load");
if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB)) if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB))
+2 -2
View File
@@ -106,8 +106,8 @@ public:
enum FramebufferTarget enum FramebufferTarget
{ {
FRAMEBUFFER_READ = (1 << 1), FRAMEBUFFER_READ = (1 << 0),
FRAMEBUFFER_DRAW = (1 << 2), FRAMEBUFFER_DRAW = (1 << 1),
FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW), FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW),
}; };
+298 -189
View File
@@ -41,11 +41,12 @@ namespace
// reattaches the originally active program when destroyed // reattaches the originally active program when destroyed
struct TemporaryAttacher struct TemporaryAttacher
{ {
TemporaryAttacher(Shader *shader) TemporaryAttacher(Shader *shader, bool attachNow)
: curShader(shader) : curShader(shader)
, prevShader(Shader::current) , prevShader(Shader::current)
{ {
curShader->attach(true); if (attachNow)
attach();
} }
~TemporaryAttacher() ~TemporaryAttacher()
@@ -56,6 +57,11 @@ namespace
curShader->detach(); curShader->detach();
} }
void attach()
{
curShader->attach(true);
}
Shader *curShader; Shader *curShader;
Shader *prevShader; Shader *prevShader;
}; };
@@ -69,8 +75,6 @@ Shader *Shader::defaultVideoShader = nullptr;
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM][2]; Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM][2];
Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM][2]; Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM][2];
std::vector<int> Shader::textureCounters;
Shader::Shader(const ShaderSource &source) Shader::Shader(const ShaderSource &source)
: shaderSource(source) : shaderSource(source)
, program(0) , program(0)
@@ -84,10 +88,6 @@ Shader::Shader(const ShaderSource &source)
if (source.vertex.empty() && source.pixel.empty()) if (source.vertex.empty() && source.pixel.empty())
throw love::Exception("Cannot create shader: no source code!"); throw love::Exception("Cannot create shader: no source code!");
// initialize global texture id counters if needed
if ((int) textureCounters.size() < gl.getMaxTextureUnits() - 1)
textureCounters.resize(gl.getMaxTextureUnits() - 1, 0);
// load shader source and create program object // load shader source and create program object
loadVolatile(); loadVolatile();
} }
@@ -97,12 +97,25 @@ Shader::~Shader()
if (current == this) if (current == this)
detach(); detach();
for (const auto &retainable : boundRetainables)
retainable.second->release();
boundRetainables.clear();
unloadVolatile(); unloadVolatile();
for (const auto &p : uniforms)
{
// Allocated with malloc().
if (p.second.data != nullptr)
free(p.second.data);
if (p.second.baseType == UNIFORM_SAMPLER)
{
for (int i = 0; i < p.second.count; i++)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
delete[] p.second.textures;
}
}
} }
GLuint Shader::compileCode(ShaderStage stage, const std::string &code) GLuint Shader::compileCode(ShaderStage stage, const std::string &code)
@@ -176,8 +189,6 @@ void Shader::mapActiveUniforms()
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++) for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1; builtinUniforms[i] = -1;
uniforms.clear();
GLint activeprogram = 0; GLint activeprogram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram); glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram);
@@ -189,6 +200,9 @@ void Shader::mapActiveUniforms()
GLchar cname[256]; GLchar cname[256];
const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar)); const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar));
std::map<std::string, UniformInfo> olduniforms = uniforms;
uniforms.clear();
for (int i = 0; i < numuniforms; i++) for (int i = 0; i < numuniforms; i++)
{ {
GLsizei namelen = 0; GLsizei namelen = 0;
@@ -206,12 +220,6 @@ void Shader::mapActiveUniforms()
else else
u.components = getUniformTypeComponents(gltype); u.components = getUniformTypeComponents(gltype);
// Initialize all samplers to 0. Both GLSL and GLSL ES are supposed to
// do this themselves, but some Android devices (galaxy tab 3 and 4)
// don't seem to do it...
if (u.baseType == UNIFORM_SAMPLER)
glUniform1i(u.location, 0);
// glGetActiveUniform appends "[0]" to the end of array uniform names... // glGetActiveUniform appends "[0]" to the end of array uniform names...
if (u.name.length() > 3) if (u.name.length() > 3)
{ {
@@ -225,8 +233,135 @@ void Shader::mapActiveUniforms()
if (builtinNames.find(u.name.c_str(), builtin)) if (builtinNames.find(u.name.c_str(), builtin))
builtinUniforms[int(builtin)] = u.location; builtinUniforms[int(builtin)] = u.location;
if (u.location != -1) if (u.location == -1)
uniforms[u.name] = u; continue;
// Make sure previously set uniform data is preserved, and shader-
// initialized values are retrieved.
auto oldu = olduniforms.find(u.name);
if (oldu != olduniforms.end())
{
u.data = oldu->second.data;
u.textures = oldu->second.textures;
updateUniform(&u, u.count, true);
if (u.baseType == UNIFORM_SAMPLER)
{
// Make sure all stored textures have their Volatiles loaded
// before the sendTextures call, since it calls getHandle().
for (int i = 0; i < u.count; i++)
{
if (u.textures[i] == nullptr)
continue;
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
if (v != nullptr)
v->loadVolatile();
}
sendTextures(&u, u.textures, u.count, true);
}
}
else
{
size_t datasize = 0;
switch (u.baseType)
{
case UNIFORM_FLOAT:
datasize = sizeof(float) * u.components * u.count;
u.data = malloc(datasize);
break;
case UNIFORM_INT:
case UNIFORM_BOOL:
case UNIFORM_SAMPLER:
datasize = sizeof(int) * u.components * u.count;
u.data = malloc(datasize);
break;
case UNIFORM_MATRIX:
datasize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count;
u.data = malloc(datasize);
break;
default:
break;
}
if (datasize > 0)
{
memset(u.data, 0, datasize);
if (u.baseType == UNIFORM_SAMPLER)
{
// Initialize all samplers to 0. Both GLSL and GLSL ES are
// supposed to do this themselves, but some Android devices
// (galaxy tab 3 and 4) don't seem to do it...
glUniform1iv(u.location, u.count, u.ints);
u.textures = new Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
size_t offset = 0;
// Store any shader-initialized values in our own memory.
for (int i = 0; i < u.count; i++)
{
GLint location = u.location;
if (u.count > 1)
{
std::string indexname = u.name + "[" + std::to_string(i) + "]";
location = glGetUniformLocation(program, indexname.c_str());
}
if (location == -1)
continue;
switch (u.baseType)
{
case UNIFORM_FLOAT:
glGetUniformfv(program, location, &u.floats[offset]);
offset += u.components;
break;
case UNIFORM_INT:
case UNIFORM_BOOL:
glGetUniformiv(program, location, &u.ints[offset]);
offset += u.components;
break;
case UNIFORM_MATRIX:
glGetUniformfv(program, location, &u.floats[offset]);
offset += u.matrix.rows * u.matrix.columns;
break;
default:
break;
}
}
}
uniforms[u.name] = u;
}
// Make sure uniforms that existed before but don't exist anymore are
// cleaned up. This theoretically shouldn't happen, but...
for (const auto &p : olduniforms)
{
if (uniforms.find(p.first) == uniforms.end())
{
free(p.second.data);
if (p.second.baseType != UNIFORM_SAMPLER)
continue;
for (int i = 0; i < p.second.count; i++)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
delete[] p.second.textures;
}
} }
gl.useProgram(activeprogram); gl.useProgram(activeprogram);
@@ -251,8 +386,8 @@ bool Shader::loadVolatile()
videoTextureUnits[i] = 0; videoTextureUnits[i] = 0;
// zero out active texture list // zero out active texture list
activeTexUnits.clear(); textureUnits.clear();
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0); textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
std::vector<GLuint> shaderids; std::vector<GLuint> shaderids;
@@ -348,22 +483,12 @@ void Shader::unloadVolatile()
program = 0; program = 0;
} }
// decrement global texture id counters for texture units which had textures bound from this shader
for (size_t i = 0; i < activeTexUnits.size(); ++i)
{
if (activeTexUnits[i] > 0)
textureCounters[i] = std::max(textureCounters[i] - 1, 0);
}
// active texture list is probably invalid, clear it // active texture list is probably invalid, clear it
activeTexUnits.clear(); textureUnits.clear();
activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0); textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
attributes.clear(); attributes.clear();
// same with uniform location list
uniforms.clear();
// And the locations of any built-in uniform variables. // And the locations of any built-in uniform variables.
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++) for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1; builtinUniforms[i] = -1;
@@ -420,10 +545,10 @@ void Shader::attach(bool temporary)
{ {
// make sure all sent textures are properly bound to their respective texture units // make sure all sent textures are properly bound to their respective texture units
// note: list potentially contains texture ids of deleted/invalid textures! // note: list potentially contains texture ids of deleted/invalid textures!
for (int i = 0; i < (int) activeTexUnits.size(); ++i) for (int i = 1; i < (int) textureUnits.size(); ++i)
{ {
if (activeTexUnits[i] > 0) if (textureUnits[i].active)
gl.bindTextureToUnit(activeTexUnits[i], i + 1, false); gl.bindTextureToUnit(textureUnits[i].texture, i, false);
} }
} }
} }
@@ -455,160 +580,148 @@ const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
return &(it->second); return &(it->second);
} }
void Shader::sendInts(const UniformInfo *info, const int *vec, int count) void Shader::updateUniform(const UniformInfo *info, int count, bool internalUpdate)
{ {
if (info->baseType != UNIFORM_INT && info->baseType != UNIFORM_BOOL) TemporaryAttacher attacher(this, !internalUpdate);
return;
TemporaryAttacher attacher(this);
int location = info->location; int location = info->location;
UniformType type = info->baseType;
switch (info->components) if (type == UNIFORM_FLOAT)
{ {
case 4: switch (info->components)
glUniform4iv(location, count, vec); {
break; case 1:
case 3: glUniform1fv(location, count, info->floats);
glUniform3iv(location, count, vec); break;
break; case 2:
case 2: glUniform2fv(location, count, info->floats);
glUniform2iv(location, count, vec); break;
break; case 3:
case 1: glUniform3fv(location, count, info->floats);
default: break;
glUniform1iv(location, count, vec); case 4:
break; glUniform4fv(location, count, info->floats);
break;
}
}
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER)
{
switch (info->components)
{
case 1:
glUniform1iv(location, count, info->ints);
break;
case 2:
glUniform2iv(location, count, info->ints);
break;
case 3:
glUniform3iv(location, count, info->ints);
break;
case 4:
glUniform4iv(location, count, info->ints);
break;
}
}
else if (type == UNIFORM_MATRIX)
{
int columns = info->matrix.columns;
int rows = info->matrix.rows;
if (columns == 2 && rows == 2)
glUniformMatrix2fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 3)
glUniformMatrix3fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 4)
glUniformMatrix4fv(location, count, GL_FALSE, info->floats);
else if (columns == 2 && rows == 3)
glUniformMatrix2x3fv(location, count, GL_FALSE, info->floats);
else if (columns == 2 && rows == 4)
glUniformMatrix2x4fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 2)
glUniformMatrix3x2fv(location, count, GL_FALSE, info->floats);
else if (columns == 3 && rows == 4)
glUniformMatrix3x4fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 2)
glUniformMatrix4x2fv(location, count, GL_FALSE, info->floats);
else if (columns == 4 && rows == 3)
glUniformMatrix4x3fv(location, count, GL_FALSE, info->floats);
} }
} }
void Shader::sendFloats(const UniformInfo *info, const float *vec, int count) int Shader::getFreeTextureUnits(int count)
{ {
if (info->baseType != UNIFORM_FLOAT && info->baseType != UNIFORM_BOOL) int startunit = -1;
return;
TemporaryAttacher attacher(this); // Ignore the first texture unit for Shader-local texture bindings.
for (int i = 1; i < (int) textureUnits.size(); i++)
int location = info->location;
switch (info->components)
{ {
case 4: if (!textureUnits[i].active && i + count <= (int) textureUnits.size())
glUniform4fv(location, count, vec); {
break; startunit = i;
case 3: break;
glUniform3fv(location, count, vec); }
break;
case 2:
glUniform2fv(location, count, vec);
break;
case 1:
default:
glUniform1fv(location, count, vec);
break;
} }
if (startunit == -1)
throw love::Exception("No more texture units available for shader.");
return startunit;
} }
void Shader::sendMatrices(const UniformInfo *info, const float *m, int count) void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_MATRIX)
return;
TemporaryAttacher attacher(this);
int location = info->location;
int columns = info->matrix.columns;
int rows = info->matrix.rows;
if (columns == 2 && rows == 2)
glUniformMatrix2fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 3)
glUniformMatrix3fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 4)
glUniformMatrix4fv(location, count, GL_FALSE, m);
else if (columns == 2 && rows == 3)
glUniformMatrix2x3fv(location, count, GL_FALSE, m);
else if (columns == 2 && rows == 4)
glUniformMatrix2x4fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 2)
glUniformMatrix3x2fv(location, count, GL_FALSE, m);
else if (columns == 3 && rows == 4)
glUniformMatrix3x4fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 2)
glUniformMatrix4x2fv(location, count, GL_FALSE, m);
else if (columns == 4 && rows == 3)
glUniformMatrix4x3fv(location, count, GL_FALSE, m);
}
void Shader::sendTexture(const UniformInfo *info, Texture *texture)
{ {
if (info->baseType != UNIFORM_SAMPLER) if (info->baseType != UNIFORM_SAMPLER)
return; return;
GLuint gltex = *(GLuint *) texture->getHandle(); count = std::min(count, info->count);
bool updateuniform = false;
TemporaryAttacher attacher(this); // Make sure the shader's samplers are associated with texture units.
for (int i = 0; i < count; i++)
int texunit = getTextureUnit(info->name);
// bind texture to assigned texture unit and send uniform to shader program
gl.bindTextureToUnit(gltex, texunit, false);
glUniform1i(info->location, texunit);
// increment global shader texture id counter for this texture unit, if we haven't already
if (activeTexUnits[texunit-1] == 0)
++textureCounters[texunit-1];
// store texture id so it can be re-bound to the proper texture unit later
activeTexUnits[texunit-1] = gltex;
retainObject(info->name, texture);
}
void Shader::retainObject(const std::string &name, Object *object)
{
object->retain();
auto it = boundRetainables.find(name);
if (it != boundRetainables.end())
it->second->release();
boundRetainables[name] = object;
}
int Shader::getTextureUnit(const std::string &name)
{
auto it = texUnitPool.find(name);
if (it != texUnitPool.end())
return it->second;
int texunit = 1;
// prefer texture units which are unused by all other shaders
auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0);
if (freeunit_it != textureCounters.end())
{ {
// we don't want to use unit 0 if (info->ints[i] == 0 && textures[i] != nullptr)
texunit = (int) std::distance(textureCounters.begin(), freeunit_it) + 1; {
} int texunit = getFreeTextureUnits(1);
else textureUnits[texunit].active = true;
{
// no completely unused texture units exist, try to use next free slot in our own list
auto nextunit_it = std::find(activeTexUnits.begin(), activeTexUnits.end(), 0);
if (nextunit_it == activeTexUnits.end()) info->ints[i] = texunit;
throw love::Exception("No more texture units available for shader."); updateuniform = true;
}
// we don't want to use unit 0
texunit = (int) std::distance(activeTexUnits.begin(), nextunit_it) + 1;
} }
texUnitPool[name] = texunit; if (updateuniform)
return texunit; updateUniform(info, count, internalUpdate);
// Bind the textures to the texture units.
for (int i = 0; i < count; i++)
{
if (textures[i] != nullptr)
textures[i]->retain();
if (info->textures[i] != nullptr)
info->textures[i]->release();
info->textures[i] = textures[i];
int texunit = info->ints[i];
if (textures[i] != nullptr)
{
GLuint gltex = *(GLuint *) textures[i]->getHandle();
gl.bindTextureToUnit(gltex, texunit, false);
// store texture id so it can be re-bound to the proper texture unit later
textureUnits[texunit].texture = gltex;
}
else
{
gl.bindTextureToUnit(0, texunit, false);
textureUnits[texunit].texture = 0;
textureUnits[texunit].active = false;
}
}
} }
bool Shader::hasUniform(const std::string &name) const bool Shader::hasUniform(const std::string &name) const
@@ -635,12 +748,12 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture) void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture)
{ {
TemporaryAttacher attacher(this);
// Set up the texture units that will be used by the shader to sample from // Set up the texture units that will be used by the shader to sample from
// the textures, if they haven't been set up yet. // the textures, if they haven't been set up yet.
if (videoTextureUnits[0] == 0) if (videoTextureUnits[0] == 0)
{ {
TemporaryAttacher attacher(this, true);
const GLint locs[3] = { const GLint locs[3] = {
builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL], builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL],
builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL], builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL],
@@ -656,14 +769,15 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur
{ {
if (locs[i] >= 0 && names[i] != nullptr) if (locs[i] >= 0 && names[i] != nullptr)
{ {
videoTextureUnits[i] = getTextureUnit(names[i]); const UniformInfo *info = getUniformInfo(names[i]);
if (info != nullptr)
{
videoTextureUnits[i] = getFreeTextureUnits(1);
textureUnits[videoTextureUnits[i]].active = true;
// Increment global shader texture id counter for this texture info->ints[0] = videoTextureUnits[i];
// unit, if we haven't already. updateUniform(info, 1);
if (activeTexUnits[videoTextureUnits[i] - 1] == 0) }
++textureCounters[videoTextureUnits[i] - 1];
glUniform1i(locs[i], videoTextureUnits[i]);
} }
} }
} }
@@ -676,7 +790,7 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur
if (videoTextureUnits[i] != 0) if (videoTextureUnits[i] != 0)
{ {
// Store texture id so it can be re-bound later. // Store texture id so it can be re-bound later.
activeTexUnits[videoTextureUnits[i] - 1] = textures[i]; textureUnits[videoTextureUnits[i]].texture = textures[i];
gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false); gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false);
} }
} }
@@ -718,7 +832,7 @@ void Shader::checkSetScreenParams()
if (location >= 0) if (location >= 0)
{ {
TemporaryAttacher attacher(this); TemporaryAttacher attacher(this, true);
glUniform4fv(location, 1, params); glUniform4fv(location, 1, params);
} }
@@ -735,7 +849,7 @@ void Shader::checkSetPointSize(float size)
if (location >= 0) if (location >= 0)
{ {
TemporaryAttacher attacher(this); TemporaryAttacher attacher(this, true);
glUniform1f(location, size); glUniform1f(location, size);
} }
@@ -755,7 +869,7 @@ void Shader::checkSetBuiltinUniforms()
const Matrix4 &curxform = gl.matrices.transform.back(); const Matrix4 &curxform = gl.matrices.transform.back();
const Matrix4 &curproj = gl.matrices.projection; const Matrix4 &curproj = gl.matrices.projection;
TemporaryAttacher attacher(this); TemporaryAttacher attacher(this, true);
bool tpmatrixneedsupdate = false; bool tpmatrixneedsupdate = false;
@@ -802,11 +916,6 @@ void Shader::checkSetBuiltinUniforms()
} }
} }
const std::map<std::string, Object *> &Shader::getBoundRetainables() const
{
return boundRetainables;
}
std::string Shader::getGLSLVersion() std::string Shader::getGLSLVersion()
{ {
const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION); const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
+21 -30
View File
@@ -96,13 +96,24 @@ public:
{ {
int location; int location;
int count; int count;
union union
{ {
int components; int components;
MatrixSize matrix; MatrixSize matrix;
}; };
UniformType baseType; UniformType baseType;
std::string name; std::string name;
union
{
void *data;
float *floats;
int *ints;
};
Texture **textures;
}; };
// Pointer to currently active Shader. // Pointer to currently active Shader.
@@ -147,11 +158,9 @@ public:
std::string getWarnings() const; std::string getWarnings() const;
const UniformInfo *getUniformInfo(const std::string &name) const; const UniformInfo *getUniformInfo(const std::string &name) const;
void updateUniform(const UniformInfo *info, int count, bool internalUpdate = false);
void sendInts(const UniformInfo *info, const int *vec, int count); void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false);
void sendFloats(const UniformInfo *info, const float *vec, int count);
void sendMatrices(const UniformInfo *info, const float *m, int count);
void sendTexture(const UniformInfo *info, Texture *texture);
/** /**
* Gets whether a uniform with the specified name exists and is actively * Gets whether a uniform with the specified name exists and is actively
@@ -171,24 +180,11 @@ public:
void checkSetPointSize(float size); void checkSetPointSize(float size);
void checkSetBuiltinUniforms(); void checkSetBuiltinUniforms();
const std::map<std::string, Object *> &getBoundRetainables() const;
GLuint getProgram() const GLuint getProgram() const
{ {
return program; return program;
} }
template <typename T>
T *getScratchBuffer(size_t count)
{
size_t bytes = sizeof(T) * count;
if (scratchBuffer.size() < bytes)
scratchBuffer.resize(bytes);
return (T *) scratchBuffer.data();
}
static std::string getGLSLVersion(); static std::string getGLSLVersion();
static bool isSupported(); static bool isSupported();
@@ -200,6 +196,12 @@ public:
private: private:
struct TextureUnit
{
GLuint texture = 0;
bool active = false;
};
// Map active uniform names to their locations. // Map active uniform names to their locations.
void mapActiveUniforms(); void mapActiveUniforms();
@@ -209,9 +211,7 @@ private:
GLuint compileCode(ShaderStage stage, const std::string &code); GLuint compileCode(ShaderStage stage, const std::string &code);
int getTextureUnit(const std::string &name); int getFreeTextureUnits(int count);
void retainObject(const std::string &name, Object *object);
// Get any warnings or errors generated only by the shader program object. // Get any warnings or errors generated only by the shader program object.
std::string getProgramWarnings() const; std::string getProgramWarnings() const;
@@ -237,11 +237,7 @@ private:
std::map<std::string, UniformInfo> uniforms; std::map<std::string, UniformInfo> uniforms;
// Texture unit pool for setting images // Texture unit pool for setting images
std::map<std::string, GLint> texUnitPool; // texUnitPool[name] = textureunit std::vector<TextureUnit> textureUnits;
std::vector<GLuint> activeTexUnits; // activeTexUnits[textureunit-1] = textureid
// Uniform name to retainable objects
std::map<std::string, Object*> boundRetainables;
bool canvasWasActive; bool canvasWasActive;
OpenGL::Viewport lastViewport; OpenGL::Viewport lastViewport;
@@ -253,11 +249,6 @@ private:
GLuint videoTextureUnits[3]; GLuint videoTextureUnits[3];
std::vector<char> scratchBuffer;
// Counts total number of textures bound to each texture unit in all shaders
static std::vector<int> textureCounters;
static StringMap<ShaderStage, STAGE_MAX_ENUM>::Entry stageNameEntries[]; static StringMap<ShaderStage, STAGE_MAX_ENUM>::Entry stageNameEntries[];
static StringMap<ShaderStage, STAGE_MAX_ENUM> stageNames; static StringMap<ShaderStage, STAGE_MAX_ENUM> stageNames;
+24 -21
View File
@@ -52,10 +52,8 @@ static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info
} }
template <typename T> template <typename T>
static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components, int count) static void _updateNumbers(lua_State *L, int startidx, T *values, int components, int count)
{ {
T *values = shader->getScratchBuffer<T>(components * count);
if (components == 1) if (components == 1)
{ {
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
@@ -76,16 +74,15 @@ static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components
lua_pop(L, components); lua_pop(L, components);
} }
} }
return values;
} }
int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors) int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors)
{ {
int count = _getCount(L, startidx, info); int count = _getCount(L, startidx, info);
int components = info->components; int components = info->components;
float *values = info->floats;
float *values = _getNumbers<float>(L, startidx, shader, components, count); _updateNumbers(L, startidx, values, components, count);
if (colors && graphics::isGammaCorrect()) if (colors && graphics::isGammaCorrect())
{ {
@@ -99,15 +96,15 @@ int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader
} }
} }
luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); }); luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0; return 0;
} }
int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
{ {
int count = _getCount(L, startidx, info); int count = _getCount(L, startidx, info);
int *values = _getNumbers<int>(L, startidx, shader, info->components, count); _updateNumbers(L, startidx, info->ints, info->components, count);
luax_catchexcept(L, [&]() { shader->sendInts(info, values, count); }); luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0; return 0;
} }
@@ -116,15 +113,15 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad
int count = _getCount(L, startidx, info); int count = _getCount(L, startidx, info);
int components = info->components; int components = info->components;
// We have to send booleans as ints or floats. // We have to send booleans as ints.
float *values = shader->getScratchBuffer<float>(components * count); int *values = info->ints;
if (components == 1) if (components == 1)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
luaL_checktype(L, startidx + i, LUA_TBOOLEAN); luaL_checktype(L, startidx + i, LUA_TBOOLEAN);
values[i] = (float) lua_toboolean(L, startidx + i); values[i] = (int) lua_toboolean(L, startidx + i);
} }
} }
else else
@@ -137,14 +134,14 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad
{ {
lua_rawgeti(L, startidx + i, k); lua_rawgeti(L, startidx + i, k);
luaL_checktype(L, -1, LUA_TBOOLEAN); luaL_checktype(L, -1, LUA_TBOOLEAN);
values[i * components + k - 1] = (float) lua_toboolean(L, -1); values[i * components + k - 1] = (int) lua_toboolean(L, -1);
} }
lua_pop(L, components); lua_pop(L, components);
} }
} }
luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); }); luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
return 0; return 0;
} }
@@ -163,7 +160,7 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
int rows = info->matrix.rows; int rows = info->matrix.rows;
int elements = columns * rows; int elements = columns * rows;
float *values = shader->getScratchBuffer<float>(elements * count); float *values = info->floats;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@@ -243,15 +240,21 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
} }
} }
shader->sendMatrices(info, values, count); shader->updateUniform(info, count);
return 0; return 0;
} }
int w_Shader_sendTexture(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
{ {
// We don't support arrays of textures (yet). int count = _getCount(L, startidx, info);
Texture *texture = luax_checktexture(L, startidx);
luax_catchexcept(L, [&]() { shader->sendTexture(info, texture); }); std::vector<Texture *> textures;
textures.reserve(count);
for (int i = 0; i < count; i++)
textures.push_back(luax_checktexture(L, startidx + i));
luax_catchexcept(L, [&]() { shader->sendTextures(info, textures.data(), count); });
return 0; return 0;
} }
@@ -277,7 +280,7 @@ int w_Shader_send(lua_State *L)
case Shader::UNIFORM_BOOL: case Shader::UNIFORM_BOOL:
return w_Shader_sendBooleans(L, startidx, shader, info); return w_Shader_sendBooleans(L, startidx, shader, info);
case Shader::UNIFORM_SAMPLER: case Shader::UNIFORM_SAMPLER:
return w_Shader_sendTexture(L, startidx, shader, info); return w_Shader_sendTextures(L, startidx, shader, info);
default: default:
return luaL_error(L, "Unknown variable type for shader uniform '%s", name); return luaL_error(L, "Unknown variable type for shader uniform '%s", name);
} }