Skip to content

Commit

Permalink
renderer: fix uniform locations
Browse files Browse the repository at this point in the history
  • Loading branch information
Vtec234 committed Dec 25, 2018
1 parent 5677f3d commit 5cc0528
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 103 deletions.
216 changes: 129 additions & 87 deletions libopenage/renderer/opengl/shader_program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "shader_program.h"

#include <algorithm>
#include <unordered_set>

#include "../../error/error.h"
#include "../../log/log.h"
Expand Down Expand Up @@ -47,7 +48,8 @@ static void check_program_status(GLuint program, GLenum what_to_check) {
}

GlShaderProgram::GlShaderProgram(const std::vector<resources::ShaderSource> &srcs, const gl_context_capabilities &caps)
: GlSimpleObject([] (GLuint handle) { glDeleteProgram(handle); } ) {
: GlSimpleObject([] (GLuint handle) { glDeleteProgram(handle); } )
, validated(false) {
GLuint handle = glCreateProgram();
this->handle = handle;

Expand All @@ -61,9 +63,6 @@ GlShaderProgram::GlShaderProgram(const std::vector<resources::ShaderSource> &src
glLinkProgram(handle);
check_program_status(handle, GL_LINK_STATUS);

glValidateProgram(handle);
check_program_status(handle, GL_VALIDATE_STATUS);

// after linking we can delete the shaders
for (auto const& shdr : shaders) {
glDetachShader(handle, shdr.get_handle());
Expand All @@ -85,44 +84,12 @@ GlShaderProgram::GlShaderProgram(const std::vector<resources::ShaderSource> &src
max_name_len = std::max(size_t(val), max_name_len);

std::vector<char> name(max_name_len);

GLuint tex_unit = 0;
for (GLuint i_unif = 0; i_unif < unif_count; ++i_unif) {
GLint count;
GLenum type;
glGetActiveUniform(
handle,
i_unif,
name.size(),
nullptr,
&count,
&type,
name.data()
);

this->uniforms.insert(std::make_pair(
name.data(),
GlUniform {
type,
GLint(i_unif),
size_t(count) * GL_SHADER_TYPE_SIZE.get(type),
{},
0, 0, size_t(count)
}
));

if (type == GL_SAMPLER_2D) {
ENSURE(tex_unit < 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(name.data(), tex_unit));
tex_unit += 1;
}
}
// Indices of uniforms within named blocks.
std::unordered_set<GLuint> in_block_unifs;

GLuint block_binding = 0;

// Extract uniform block descriptions.
for (GLuint i_unif_block = 0; i_unif_block < unif_block_count; ++i_unif_block) {
glGetActiveUniformBlockName(
handle,
Expand All @@ -141,51 +108,111 @@ GlShaderProgram::GlShaderProgram(const std::vector<resources::ShaderSource> &src
std::vector<GLint> uniform_indices(val);
glGetActiveUniformBlockiv(handle, i_unif_block, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, uniform_indices.data());

// Names of uniforms within this block
std::vector<std::string> uniform_names{};
std::unordered_map<std::string, GlInBlockUniform> uniforms;
for (GLuint const i_unif : uniform_indices) {
glGetActiveUniformName(handle, i_unif, name.size(), nullptr, name.data());
try {
auto& unif = this->uniforms.at(name.data());
unif.block_name = block_name;
uniform_names.push_back(name.data());

glGetActiveUniformsiv(handle, 1, &i_unif, GL_UNIFORM_OFFSET, &val);
unif.offset = val;
glGetActiveUniformsiv(handle, 1, &i_unif, GL_UNIFORM_ARRAY_STRIDE, &val);
unif.stride = val;
if (unif.stride == 0) {
// The uniform is not an array, but it's declared in a named block and hence might
// be a matrix whose stride we need to know.
glGetActiveUniformsiv(handle, 1, &i_unif, GL_UNIFORM_MATRIX_STRIDE, &val);
unif.stride = val;
}
} catch (std::out_of_range const&) {
throw Error(MSG(err) << "Could not find OpenGL uniform " << i_unif
<< " from block " << name.data() << " in uniform list.");
in_block_unifs.insert(i_unif);

GLenum type;
GLint offset, count, stride;

glGetActiveUniform(
handle,
i_unif,
name.size(),
nullptr,
&count,
&type,
name.data()
);

glGetActiveUniformsiv(handle, 1, &i_unif, GL_UNIFORM_OFFSET, &offset);
glGetActiveUniformsiv(handle, 1, &i_unif, GL_UNIFORM_ARRAY_STRIDE, &stride);
if (stride == 0) {
// The uniform is not an array, but it's declared in a named block and hence might
// be a matrix whose stride we need to know.
glGetActiveUniformsiv(handle, 1, &i_unif, GL_UNIFORM_MATRIX_STRIDE, &stride);
}

// We do not need to handle sampler types here like in the uniform loop below,
// because named blocks cannot contain samplers.

uniforms.insert(std::make_pair(
name.data(),
GlInBlockUniform {
type,
size_t(offset),
size_t(count) * GL_SHADER_TYPE_SIZE.get(type),
size_t(stride),
size_t(count)
}
));
}

ENSURE(block_binding < caps.max_uniform_buffer_bindings,
"Tried to create an OpenGL shader that uses more uniform blocks "
<< "than there are binding points (" << caps.max_uniform_buffer_bindings
<< " available)."
);

glUniformBlockBinding(handle, i_unif_block, block_binding);

this->uniform_blocks.insert(std::make_pair(
block_name,
GlUniformBlock {
GLint(i_unif_block),
i_unif_block,
size_t(data_size),
std::move(uniform_names),
std::move(uniforms),
block_binding
}
));

ENSURE(block_binding < caps.max_uniform_buffer_bindings,
"Tried to create an OpenGL shader that uses more uniform blocks "
<< "than there are binding points (" << caps.max_uniform_buffer_bindings
<< " available)."
block_binding += 1;
}

GLuint tex_unit = 0;

// Extract information about uniforms in the default block.
for (GLuint i_unif = 0; i_unif < unif_count; ++i_unif) {
if (in_block_unifs.count(i_unif) == 1) {
// Skip uniforms within named blocks.
continue;
}

GLint count;
GLenum type;
glGetActiveUniform(
handle,
i_unif,
name.size(),
nullptr,
&count,
&type,
name.data()
);

glUniformBlockBinding(handle, i_unif_block, block_binding);
block_binding += 1;
GLuint loc = glGetUniformLocation(handle, name.data());

this->uniforms.insert(std::make_pair(
name.data(),
GlUniform {
type,
loc
}
));

if (type == GL_SAMPLER_2D) {
ENSURE(tex_unit < 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(name.data(), tex_unit));

tex_unit += 1;
}
}

// Extract vertex attribute descriptions.
for (GLuint i_attrib = 0; i_attrib < attrib_count; ++i_attrib) {
GLint size;
GLenum type;
Expand All @@ -211,32 +238,47 @@ GlShaderProgram::GlShaderProgram(const std::vector<resources::ShaderSource> &src

log::log(MSG(info) << "Created OpenGL shader program");

log::log(MSG(dbg) << "Uniform blocks: ");
for (auto const &pair : this->uniform_blocks) {
log::log(MSG(dbg) << "(" << pair.second.location << ") " << pair.first
<< " (size " << pair.second.data_size << ") {");
for (auto const& unif_name : pair.second.uniforms) {
auto const& unif = this->uniforms[unif_name];
log::log(MSG(dbg) << "\t(" << unif.location << ") +" << unif.offset << " "
<< unif_name << ": " << GLSL_TYPE_NAME.get(unif.type));
if (!this->uniform_blocks.empty()) {
log::log(MSG(dbg) << "Uniform blocks: ");
for (auto const& pair : this->uniform_blocks) {
log::log(MSG(dbg) << "(" << pair.second.index << ") " << pair.first
<< " (size: " << pair.second.data_size << ") {");
for (auto const& unif_pair : pair.second.uniforms) {
log::log(MSG(dbg) << "\t+" << unif_pair.second.offset
<< " " << unif_pair.first << ": "
<< GLSL_TYPE_NAME.get(unif_pair.second.type));
}
log::log(MSG(dbg) << "}");
}
log::log(MSG(dbg) << "}");
}
log::log(MSG(dbg) << "Uniforms: ");
for (auto const &pair : this->uniforms) {
if (!pair.second.block_name) {

if (!this->uniforms.empty()) {
log::log(MSG(dbg) << "Uniforms: ");
for (auto const& pair : this->uniforms) {
log::log(MSG(dbg) << "(" << pair.second.location << ") " << pair.first << ": "
<< GLSL_TYPE_NAME.get(pair.second.type));
<< GLSL_TYPE_NAME.get(pair.second.type));
}
}
log::log(MSG(dbg) << "Vertex attributes: ");
for (auto const &pair : this->attribs) {
log::log(MSG(dbg) << "(" << pair.second.location << ") " << pair.first << ": "
<< GLSL_TYPE_NAME.get(pair.second.type));

if (!this->attribs.empty()) {
log::log(MSG(dbg) << "Vertex attributes: ");
for (auto const& pair : this->attribs) {
log::log(MSG(dbg) << "(" << pair.second.location << ") " << pair.first << ": "
<< GLSL_TYPE_NAME.get(pair.second.type));
}
}
}

void GlShaderProgram::use() const {
void GlShaderProgram::use() {
if (!this->validated) {
// TODO(Vtec234): validation depends on the context state, so this might be worth calling
// more than once. However, once per frame is probably too much.
glValidateProgram(*this->handle);
check_program_status(*this->handle, GL_VALIDATE_STATUS);

this->validated = true;
}

glUseProgram(*this->handle);

for (auto const &pair : this->textures_per_texunits) {
Expand Down
37 changes: 21 additions & 16 deletions libopenage/renderer/opengl/shader_program.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class GlShaderProgram final : public ShaderProgram, public GlSimpleObject {
explicit GlShaderProgram(const std::vector<resources::ShaderSource>&, const gl_context_capabilities&);

/// Bind this program as the currently used one in the OpenGL context.
void use() const;
void use();

/// Does what the description of Renderable specifies - updates the uniform values
/// and draws the Geometry if it's not nullptr. If geometry is null, only the
Expand All @@ -53,21 +53,22 @@ class GlShaderProgram final : public ShaderProgram, public GlSimpleObject {
private:
void set_unif(UniformInput*, const char*, void const*, GLenum);

/// Represents a uniform location in the shader program.
/// Represents a uniform in the default block, i.e. not within a named block.
struct GlUniform {
GLenum type;
GLint location;
/// The size in bytes of the whole uniform. If the uniform is an array,
/// the size of the whole array.
size_t size;
/// If this uniform is within a uniform block, the block name, otherwise empty.
/// Its existence (or lack thereof) can be used to check whether the uniform
/// is in a named block.
std::optional<std::string> block_name;
/// Location of the uniform for use with glUniform and glGetUniform.
/// NOT the same as the uniform index.
GLuint location;
};

// The members below are only relevant for uniforms in named uniform blocks.
/// Represents a uniform in a named block.
struct GlInBlockUniform {
GLenum type;
/// Offset from the beginning of the block at which this uniform is placed.
size_t offset;
/// The size in bytes of the whole uniform. If the uniform is an array,
/// the size of the whole array.
size_t size;
/// Only relevant for arrays and matrices.
/// In arrays, specifies the distance between the start of each element.
/// In row-major matrices, specifies the distance between the start of each row.
Expand All @@ -79,12 +80,13 @@ class GlShaderProgram final : public ShaderProgram, public GlSimpleObject {

/// Represents a uniform block in the shader program.
struct GlUniformBlock {
GLint location;
GLuint index;
/// Size of the entire block. How uniforms are packed within depends
/// on the block layout and is described in corresponding GlUniforms.
size_t data_size;
/// Names of the uniforms within this block.
std::vector<std::string> uniforms;
/// Maps uniform names within this block to their descriptions.
std::unordered_map<std::string, GlInBlockUniform> uniforms;

/// The binding point assigned to this block.
GLuint binding_point;
};
Expand All @@ -97,8 +99,8 @@ class GlShaderProgram final : public ShaderProgram, public GlSimpleObject {
GLint size;
};

/// Maps uniform names to their descriptions. Contains all
/// uniforms, both within and outside of uniform blocks.
/// Maps uniform names to their descriptions. Contains only
/// uniforms in the default block, i.e. not within named blocks.
std::unordered_map<std::string, GlUniform> uniforms;

/// Maps uniform block names to their descriptions.
Expand All @@ -111,6 +113,9 @@ class GlShaderProgram final : public ShaderProgram, public GlSimpleObject {
std::unordered_map<std::string, 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;

/// Whether this program has been validated.
bool validated;
};

}}} // openage::renderer::opengl

0 comments on commit 5cc0528

Please sign in to comment.