Skip to content

Commit

Permalink
renderer: Vectorize tex unit bindings in shader program.
Browse files Browse the repository at this point in the history
  • Loading branch information
heinezen committed Jul 29, 2024
1 parent 0224b9b commit 87db61e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 20 deletions.
10 changes: 10 additions & 0 deletions libopenage/renderer/opengl/shader_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,14 @@ struct GlVertexAttrib {
GLint size;
};

/**
* Represents a texture unit binding in a shader program.
*/
struct TexunitBinding {
/// Texture bound to the texture unit.
GLuint tex = 0;
/// true if the texture unit is currently bound to a texture.
bool used = false;
};

} // namespace openage::renderer::opengl
48 changes: 30 additions & 18 deletions libopenage/renderer/opengl/shader_program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ GlShaderProgram::GlShaderProgram(const std::shared_ptr<GlContext> &context,
block_binding}));
}

GLuint tex_unit = 0;
GLuint tex_unit_id = 0;

// Extract information about uniforms in the default block.

Expand Down Expand Up @@ -211,19 +211,22 @@ GlShaderProgram::GlShaderProgram(const std::shared_ptr<GlContext> &context,
unif_id));

if (type == GL_SAMPLER_2D) {
ENSURE(tex_unit < caps.max_texture_slots,
ENSURE(tex_unit_id < caps.max_texture_slots,
"Tried to create an OpenGL shader that uses more texture sampler uniforms "
<< "than there are texture unit slots (" << caps.max_texture_slots << " available).");

this->texunits_per_unifs.insert(std::make_pair(unif_id, tex_unit));
this->texunits_per_unifs.insert(std::make_pair(unif_id, tex_unit_id));

tex_unit += 1;
tex_unit_id += 1;
}

// Increment uniform ID
unif_id += 1;
}

// Initialize the texture unit bindings
this->textures_per_texunits.resize(this->texunits_per_unifs.size());

// Extract vertex attribute descriptions.
for (GLuint i_attrib = 0; i_attrib < attrib_count; ++i_attrib) {
GLint size;
Expand Down Expand Up @@ -304,21 +307,28 @@ void GlShaderProgram::use() {
std::static_pointer_cast<GlShaderProgram>(
this->shared_from_this()));

for (auto const &pair : this->textures_per_texunits) {
for (size_t i = 0; i < this->textures_per_texunits.size(); ++i) {
auto &tex_unit = this->textures_per_texunits[i];
if (not tex_unit.used) {
continue;
}

if (not glIsTexture(tex_unit.tex)) {
// By the time we use the shader again, the texture may have been deleted
// but if it's fixed afterwards using update_uniforms, the render state
// will still be fine
// We can free the texture unit in this case
tex_unit.used = false;
continue;
}

// We have to bind the texture to their texture units here because
// the texture unit bindings are global to the context. Each time
// the shader switches, it is possible that some other shader overwrote
// these, and since we want the uniform values to persist across update_uniforms
// calls, we have to set them more often than just on update_uniforms.
glActiveTexture(GL_TEXTURE0 + pair.first);
glBindTexture(GL_TEXTURE_2D, pair.second);

// By the time we call bind, the texture may have been deleted, but if it's fixed
// afterwards using update_uniforms, the render state will still be fine, so we can ignore
// this error.
// TODO this will swallow actual errors elsewhere, and should be avoided. how?
// probably by holding the texture object as shared_ptr as long as it is bound
glGetError();
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, tex_unit.tex);
}
}

Expand Down Expand Up @@ -395,13 +405,15 @@ void GlShaderProgram::update_uniforms(std::shared_ptr<GlUniformInput> const &uni
glUniformMatrix4fv(loc, 1, GLboolean(false), reinterpret_cast<const float *>(ptr));
break;
case GL_SAMPLER_2D: {
GLuint tex_unit = this->texunits_per_unifs.at(pair.first);
GLuint tex_unit_id = this->texunits_per_unifs.at(pair.first);
GLuint tex = *reinterpret_cast<const GLuint *>(ptr);
glActiveTexture(GL_TEXTURE0 + tex_unit);
glActiveTexture(GL_TEXTURE0 + tex_unit_id);
glBindTexture(GL_TEXTURE_2D, tex);
// TODO: maybe call this at a more appropriate position
glUniform1i(loc, tex_unit);
this->textures_per_texunits[tex_unit] = tex;
glUniform1i(loc, tex_unit_id);
auto &tex_unit = this->textures_per_texunits[tex_unit_id];
tex_unit.tex = tex;
tex_unit.used = true;
break;
}
default:
Expand Down
4 changes: 2 additions & 2 deletions libopenage/renderer/opengl/shader_program.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ class GlShaderProgram final : public ShaderProgram
/// Maps sampler uniform names to their assigned texture units.
std::unordered_map<uniform_id_t, GLuint> texunits_per_unifs;

/// Maps texture units to the texture handles that are currently bound to them.
std::unordered_map<GLuint, GLuint> textures_per_texunits;
/// Store which texture handles are currently bound to the shader's texture units.
std::vector<TexunitBinding> textures_per_texunits;

/// Whether this program has been validated.
bool validated;
Expand Down

0 comments on commit 87db61e

Please sign in to comment.