mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
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:
@@ -151,6 +151,9 @@ Canvas::~Canvas()
|
||||
|
||||
bool Canvas::loadVolatile()
|
||||
{
|
||||
if (texture != 0)
|
||||
return true;
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("Canvas load");
|
||||
|
||||
fbo = texture = 0;
|
||||
|
||||
@@ -281,6 +281,9 @@ void Image::loadFromImageData()
|
||||
|
||||
bool Image::loadVolatile()
|
||||
{
|
||||
if (texture != 0)
|
||||
return true;
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("Image load");
|
||||
|
||||
if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB))
|
||||
|
||||
@@ -106,8 +106,8 @@ public:
|
||||
|
||||
enum FramebufferTarget
|
||||
{
|
||||
FRAMEBUFFER_READ = (1 << 1),
|
||||
FRAMEBUFFER_DRAW = (1 << 2),
|
||||
FRAMEBUFFER_READ = (1 << 0),
|
||||
FRAMEBUFFER_DRAW = (1 << 1),
|
||||
FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW),
|
||||
};
|
||||
|
||||
|
||||
@@ -41,11 +41,12 @@ namespace
|
||||
// reattaches the originally active program when destroyed
|
||||
struct TemporaryAttacher
|
||||
{
|
||||
TemporaryAttacher(Shader *shader)
|
||||
TemporaryAttacher(Shader *shader, bool attachNow)
|
||||
: curShader(shader)
|
||||
, prevShader(Shader::current)
|
||||
{
|
||||
curShader->attach(true);
|
||||
if (attachNow)
|
||||
attach();
|
||||
}
|
||||
|
||||
~TemporaryAttacher()
|
||||
@@ -56,6 +57,11 @@ namespace
|
||||
curShader->detach();
|
||||
}
|
||||
|
||||
void attach()
|
||||
{
|
||||
curShader->attach(true);
|
||||
}
|
||||
|
||||
Shader *curShader;
|
||||
Shader *prevShader;
|
||||
};
|
||||
@@ -69,8 +75,6 @@ Shader *Shader::defaultVideoShader = nullptr;
|
||||
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM][2];
|
||||
Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM][2];
|
||||
|
||||
std::vector<int> Shader::textureCounters;
|
||||
|
||||
Shader::Shader(const ShaderSource &source)
|
||||
: shaderSource(source)
|
||||
, program(0)
|
||||
@@ -84,10 +88,6 @@ Shader::Shader(const ShaderSource &source)
|
||||
if (source.vertex.empty() && source.pixel.empty())
|
||||
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
|
||||
loadVolatile();
|
||||
}
|
||||
@@ -97,12 +97,25 @@ Shader::~Shader()
|
||||
if (current == this)
|
||||
detach();
|
||||
|
||||
for (const auto &retainable : boundRetainables)
|
||||
retainable.second->release();
|
||||
|
||||
boundRetainables.clear();
|
||||
|
||||
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)
|
||||
@@ -176,8 +189,6 @@ void Shader::mapActiveUniforms()
|
||||
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
|
||||
builtinUniforms[i] = -1;
|
||||
|
||||
uniforms.clear();
|
||||
|
||||
GLint activeprogram = 0;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram);
|
||||
|
||||
@@ -189,6 +200,9 @@ void Shader::mapActiveUniforms()
|
||||
GLchar cname[256];
|
||||
const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar));
|
||||
|
||||
std::map<std::string, UniformInfo> olduniforms = uniforms;
|
||||
uniforms.clear();
|
||||
|
||||
for (int i = 0; i < numuniforms; i++)
|
||||
{
|
||||
GLsizei namelen = 0;
|
||||
@@ -206,12 +220,6 @@ void Shader::mapActiveUniforms()
|
||||
else
|
||||
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...
|
||||
if (u.name.length() > 3)
|
||||
{
|
||||
@@ -225,8 +233,135 @@ void Shader::mapActiveUniforms()
|
||||
if (builtinNames.find(u.name.c_str(), builtin))
|
||||
builtinUniforms[int(builtin)] = u.location;
|
||||
|
||||
if (u.location != -1)
|
||||
uniforms[u.name] = u;
|
||||
if (u.location == -1)
|
||||
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);
|
||||
@@ -251,8 +386,8 @@ bool Shader::loadVolatile()
|
||||
videoTextureUnits[i] = 0;
|
||||
|
||||
// zero out active texture list
|
||||
activeTexUnits.clear();
|
||||
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
|
||||
textureUnits.clear();
|
||||
textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
|
||||
|
||||
std::vector<GLuint> shaderids;
|
||||
|
||||
@@ -348,22 +483,12 @@ void Shader::unloadVolatile()
|
||||
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
|
||||
activeTexUnits.clear();
|
||||
activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0);
|
||||
textureUnits.clear();
|
||||
textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit());
|
||||
|
||||
attributes.clear();
|
||||
|
||||
// same with uniform location list
|
||||
uniforms.clear();
|
||||
|
||||
// And the locations of any built-in uniform variables.
|
||||
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
|
||||
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
|
||||
// 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)
|
||||
gl.bindTextureToUnit(activeTexUnits[i], i + 1, false);
|
||||
if (textureUnits[i].active)
|
||||
gl.bindTextureToUnit(textureUnits[i].texture, i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -455,160 +580,148 @@ const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
|
||||
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)
|
||||
return;
|
||||
|
||||
TemporaryAttacher attacher(this);
|
||||
TemporaryAttacher attacher(this, !internalUpdate);
|
||||
|
||||
int location = info->location;
|
||||
UniformType type = info->baseType;
|
||||
|
||||
switch (info->components)
|
||||
if (type == UNIFORM_FLOAT)
|
||||
{
|
||||
case 4:
|
||||
glUniform4iv(location, count, vec);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3iv(location, count, vec);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2iv(location, count, vec);
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
glUniform1iv(location, count, vec);
|
||||
break;
|
||||
switch (info->components)
|
||||
{
|
||||
case 1:
|
||||
glUniform1fv(location, count, info->floats);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2fv(location, count, info->floats);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3fv(location, count, info->floats);
|
||||
break;
|
||||
case 4:
|
||||
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)
|
||||
return;
|
||||
int startunit = -1;
|
||||
|
||||
TemporaryAttacher attacher(this);
|
||||
|
||||
int location = info->location;
|
||||
|
||||
switch (info->components)
|
||||
// Ignore the first texture unit for Shader-local texture bindings.
|
||||
for (int i = 1; i < (int) textureUnits.size(); i++)
|
||||
{
|
||||
case 4:
|
||||
glUniform4fv(location, count, vec);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3fv(location, count, vec);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2fv(location, count, vec);
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
glUniform1fv(location, count, vec);
|
||||
break;
|
||||
if (!textureUnits[i].active && i + count <= (int) textureUnits.size())
|
||||
{
|
||||
startunit = i;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate)
|
||||
{
|
||||
if (info->baseType != UNIFORM_SAMPLER)
|
||||
return;
|
||||
|
||||
GLuint gltex = *(GLuint *) texture->getHandle();
|
||||
count = std::min(count, info->count);
|
||||
bool updateuniform = false;
|
||||
|
||||
TemporaryAttacher attacher(this);
|
||||
|
||||
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())
|
||||
// Make sure the shader's samplers are associated with texture units.
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// we don't want to use unit 0
|
||||
texunit = (int) std::distance(textureCounters.begin(), freeunit_it) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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 (info->ints[i] == 0 && textures[i] != nullptr)
|
||||
{
|
||||
int texunit = getFreeTextureUnits(1);
|
||||
textureUnits[texunit].active = true;
|
||||
|
||||
if (nextunit_it == activeTexUnits.end())
|
||||
throw love::Exception("No more texture units available for shader.");
|
||||
|
||||
// we don't want to use unit 0
|
||||
texunit = (int) std::distance(activeTexUnits.begin(), nextunit_it) + 1;
|
||||
info->ints[i] = texunit;
|
||||
updateuniform = true;
|
||||
}
|
||||
}
|
||||
|
||||
texUnitPool[name] = texunit;
|
||||
return texunit;
|
||||
if (updateuniform)
|
||||
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
|
||||
@@ -635,12 +748,12 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
|
||||
|
||||
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
|
||||
// the textures, if they haven't been set up yet.
|
||||
if (videoTextureUnits[0] == 0)
|
||||
{
|
||||
TemporaryAttacher attacher(this, true);
|
||||
|
||||
const GLint locs[3] = {
|
||||
builtinUniforms[BUILTIN_VIDEO_Y_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)
|
||||
{
|
||||
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
|
||||
// unit, if we haven't already.
|
||||
if (activeTexUnits[videoTextureUnits[i] - 1] == 0)
|
||||
++textureCounters[videoTextureUnits[i] - 1];
|
||||
|
||||
glUniform1i(locs[i], videoTextureUnits[i]);
|
||||
info->ints[0] = videoTextureUnits[i];
|
||||
updateUniform(info, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -676,7 +790,7 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur
|
||||
if (videoTextureUnits[i] != 0)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -718,7 +832,7 @@ void Shader::checkSetScreenParams()
|
||||
|
||||
if (location >= 0)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
TemporaryAttacher attacher(this, true);
|
||||
glUniform4fv(location, 1, params);
|
||||
}
|
||||
|
||||
@@ -735,7 +849,7 @@ void Shader::checkSetPointSize(float size)
|
||||
|
||||
if (location >= 0)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
TemporaryAttacher attacher(this, true);
|
||||
glUniform1f(location, size);
|
||||
}
|
||||
|
||||
@@ -755,7 +869,7 @@ void Shader::checkSetBuiltinUniforms()
|
||||
const Matrix4 &curxform = gl.matrices.transform.back();
|
||||
const Matrix4 &curproj = gl.matrices.projection;
|
||||
|
||||
TemporaryAttacher attacher(this);
|
||||
TemporaryAttacher attacher(this, true);
|
||||
|
||||
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()
|
||||
{
|
||||
const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
|
||||
@@ -96,13 +96,24 @@ public:
|
||||
{
|
||||
int location;
|
||||
int count;
|
||||
|
||||
union
|
||||
{
|
||||
int components;
|
||||
MatrixSize matrix;
|
||||
};
|
||||
|
||||
UniformType baseType;
|
||||
std::string name;
|
||||
|
||||
union
|
||||
{
|
||||
void *data;
|
||||
float *floats;
|
||||
int *ints;
|
||||
};
|
||||
|
||||
Texture **textures;
|
||||
};
|
||||
|
||||
// Pointer to currently active Shader.
|
||||
@@ -147,11 +158,9 @@ public:
|
||||
std::string getWarnings() 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 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);
|
||||
void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false);
|
||||
|
||||
/**
|
||||
* Gets whether a uniform with the specified name exists and is actively
|
||||
@@ -171,24 +180,11 @@ public:
|
||||
void checkSetPointSize(float size);
|
||||
void checkSetBuiltinUniforms();
|
||||
|
||||
const std::map<std::string, Object *> &getBoundRetainables() const;
|
||||
|
||||
GLuint getProgram() const
|
||||
{
|
||||
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 bool isSupported();
|
||||
|
||||
@@ -200,6 +196,12 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
struct TextureUnit
|
||||
{
|
||||
GLuint texture = 0;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
// Map active uniform names to their locations.
|
||||
void mapActiveUniforms();
|
||||
|
||||
@@ -209,9 +211,7 @@ private:
|
||||
|
||||
GLuint compileCode(ShaderStage stage, const std::string &code);
|
||||
|
||||
int getTextureUnit(const std::string &name);
|
||||
|
||||
void retainObject(const std::string &name, Object *object);
|
||||
int getFreeTextureUnits(int count);
|
||||
|
||||
// Get any warnings or errors generated only by the shader program object.
|
||||
std::string getProgramWarnings() const;
|
||||
@@ -237,11 +237,7 @@ private:
|
||||
std::map<std::string, UniformInfo> uniforms;
|
||||
|
||||
// Texture unit pool for setting images
|
||||
std::map<std::string, GLint> texUnitPool; // texUnitPool[name] = textureunit
|
||||
std::vector<GLuint> activeTexUnits; // activeTexUnits[textureunit-1] = textureid
|
||||
|
||||
// Uniform name to retainable objects
|
||||
std::map<std::string, Object*> boundRetainables;
|
||||
std::vector<TextureUnit> textureUnits;
|
||||
|
||||
bool canvasWasActive;
|
||||
OpenGL::Viewport lastViewport;
|
||||
@@ -253,11 +249,6 @@ private:
|
||||
|
||||
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> stageNames;
|
||||
|
||||
|
||||
@@ -52,10 +52,8 @@ static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors)
|
||||
{
|
||||
int count = _getCount(L, startidx, info);
|
||||
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())
|
||||
{
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
|
||||
{
|
||||
int count = _getCount(L, startidx, info);
|
||||
int *values = _getNumbers<int>(L, startidx, shader, info->components, count);
|
||||
luax_catchexcept(L, [&]() { shader->sendInts(info, values, count); });
|
||||
_updateNumbers(L, startidx, info->ints, info->components, count);
|
||||
luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
|
||||
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 components = info->components;
|
||||
|
||||
// We have to send booleans as ints or floats.
|
||||
float *values = shader->getScratchBuffer<float>(components * count);
|
||||
// We have to send booleans as ints.
|
||||
int *values = info->ints;
|
||||
|
||||
if (components == 1)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
luaL_checktype(L, startidx + i, LUA_TBOOLEAN);
|
||||
values[i] = (float) lua_toboolean(L, startidx + i);
|
||||
values[i] = (int) lua_toboolean(L, startidx + i);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -137,14 +134,14 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad
|
||||
{
|
||||
lua_rawgeti(L, startidx + i, k);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); });
|
||||
luax_catchexcept(L, [&]() { shader->updateUniform(info, count); });
|
||||
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 elements = columns * rows;
|
||||
|
||||
float *values = shader->getScratchBuffer<float>(elements * count);
|
||||
float *values = info->floats;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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).
|
||||
Texture *texture = luax_checktexture(L, startidx);
|
||||
luax_catchexcept(L, [&]() { shader->sendTexture(info, texture); });
|
||||
int count = _getCount(L, startidx, info);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -277,7 +280,7 @@ int w_Shader_send(lua_State *L)
|
||||
case Shader::UNIFORM_BOOL:
|
||||
return w_Shader_sendBooleans(L, startidx, shader, info);
|
||||
case Shader::UNIFORM_SAMPLER:
|
||||
return w_Shader_sendTexture(L, startidx, shader, info);
|
||||
return w_Shader_sendTextures(L, startidx, shader, info);
|
||||
default:
|
||||
return luaL_error(L, "Unknown variable type for shader uniform '%s", name);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user