Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 1 of 2 12 LastLast
Results 1 to 10 of 14

Thread: Speed of glGetUniformLocation

  1. #1
    Intern Newbie
    Join Date
    Dec 2013
    Posts
    45

    Speed of glGetUniformLocation

    Is glGetUniformLocation a slow operation? I ask because I'm trying to wrap the GL functions and my variables in my program, and want to access it from another function by simply calling a function and passing the name of the uniform in a string and the value to set it to. However, I am wondering which will be faster: to directly call GetUniformLocation each time (possibly repeatedly for each uniform) or to use a std::map to map between the names and the GLints. Or alternatively I could #define names for each uniform and map it that way, but I'd rather try not to do that because that will make it really static.

  2. #2
    Intern Contributor
    Join Date
    May 2013
    Posts
    60
    You can not really map glGetUniformLocation to a string map because the function does a bit more. For example it will resolve array locations like ARRAYNAME[5]. It also resolves variables from uniformblocks and structs.
    For easy of development I just use a simple function that calls glGetUniformLocation every time like setUniform(std::string name, TYPE value);
    And as soon as you really care about performance just use some GLuint variables and initiate them once with glGetUniformLocation after loading the shader.

  3. #3
    Junior Member Regular Contributor tksuoran's Avatar
    Join Date
    Mar 2008
    Location
    Cambridge, UK
    Posts
    224
    You cannot use defines or constants because values of uniform locations are not constants. Each driver counts uniforms differently.

    Fastest is to call GetUniformLocation once, and store the value somewhere. One of the simplest and fastest storages is simply a struct, that is what I prefer. A string map is more generic, with a tiny (hard to measure) extra cost.

  4. #4
    Intern Newbie
    Join Date
    Dec 2013
    Posts
    45
    Osbios:
    That is what I was already doing. I wanted to wrap my uniform accesses, since I already wrapped my program objects.

    tksuoran:
    That is what I meant: calling it once and storing it in a map vs calling it every access. But I decided to use an enum to index a map

    Thank you.

  5. #5
    Senior Member OpenGL Pro
    Join Date
    Jan 2007
    Posts
    1,180
    The other option is GL_ARB_explicit_uniform_location.

  6. #6
    Senior Member OpenGL Pro Aleksandar's Avatar
    Join Date
    Jul 2009
    Posts
    1,136
    Quote Originally Posted by chbaker0 View Post
    Is glGetUniformLocation a slow operation? I ask because I'm trying to wrap the GL functions and my variables in my program, and want to access it from another function by simply calling a function and passing the name of the uniform in a string and the value to set it to. However, I am wondering which will be faster: to directly call GetUniformLocation each time (possibly repeatedly for each uniform) or to use a std::map to map between the names and the GLints. Or alternatively I could #define names for each uniform and map it that way, but I'd rather try not to do that because that will make it really static.
    I really don't understand using of hash tables or any other fast search structure to get uniform IDs. Is it much easier and more intuitive to have a variable (or the object's attribute) of type integer that will receive uniform's ID (at some initialization code) and be used in all subsequent accesses. So, call glGetUniformLocation() just once, and glUniform*() for all subsequent calls. The name can be the same as uniform name. Do you need anything better? Considering performance, drivers are usually optimized to eliminate superfluous glUniform*() calls (at least for NV). Btw, explicit uniform location is a pretty new feature (not supported in drivers older than a year or so).

  7. #7
    Intern Newbie
    Join Date
    Dec 2013
    Posts
    45
    I wanted that because I was trying to wrap all my objects in classes so I could set a uniform like this:
    Code :
    Program.SetUniform("name", value);
    or some similar way. And I got it to work with enums instead of strings, but I changed my mind about doing this anyway. I decided to just keep all my uniform GLints global and initialize them all at once. I was really overthinking how to simplify my program, to the point where I was mak
    Thanks

  8. #8
    Senior Member OpenGL Pro Aleksandar's Avatar
    Join Date
    Jul 2009
    Posts
    1,136
    Quote Originally Posted by chbaker0 View Post
    I decided to just keep all my uniform GLints global and initialize them all at once. I was really overthinking how to simplify my program, to the point where I was mak
    That's a very bad design! It drives me crazy when my students use globals instead of attributes for everything.
    You are using object-oriented programming language, right?
    Then make classes for all your shader-programs. Uniforms should be attributes of those classes.

  9. #9
    Intern Newbie
    Join Date
    Dec 2013
    Posts
    45
    That was what I was originally doing, and that brings me full circle: In the beginning I was trying to find a way to make a generic program class and be able to access uniforms through it, without having to change the class's code every time I change a uniform in my shader code. Object-oriented programming can be great, but it can certainly add pains as well...there's more recoding involved if I am dealing with a class.
    But now that I am thinking more clearly, perhaps I could have a "RegisterUniform" method in a program class that would take an input of a string and would output an index value that I could then use with a "SetUniform" method taking the index and what I want to set it to. However, how could I maintain type safety for a method like that, where the value could be a float, vector, matrix, or anything?

    Or maybe I'm just way overthinking the implementation. I guess I could simply have a method in the program class that would simply give me the GLint uniform location for a string passed to it. But doesn't that kinda defeat the purpose of object-oriented-ness?

  10. #10
    Intern Contributor
    Join Date
    Sep 2013
    Posts
    79
    Here is an example of how I implemented Shaders and Uniforms object-oriented in my Java engine:

    Code :
    public class Uniform {
     
    	private final ShaderProgram owner;
    	private final String name;
    	private final UniformType type;
    	private final int size;
    	private final int location;
     
    	public Uniform(ShaderProgram owner, String name, UniformType type, int size, int location) {
    		this.owner 		= owner;
    		this.name 		= name;
    		this.type 		= type;
    		this.size 		= size;
    		this.location 	= location;
    	}
     
    	public ShaderProgram getProgram() {
    		return owner;
    	}
     
    	public String getName() {
    		return name;
    	}
     
    	public UniformType getType() {
    		return type;
    	}
     
    	public int getSize() {
    		return size;
    	}
     
    	public int getLocation() {
    		return location;
    	}
     
    	public static class NoUniform extends Uniform {
    		public NoUniform(ShaderProgram owner, String name, UniformType type,
    				int size, int location) {
    			super(owner, name, type, size, location);
    		}
    	}
     
    	public static class Float extends Uniform {
     
    		public Float(ShaderProgram owner, String name, UniformType type,
    				int size, int location) {
    			super(owner, name, type, size, location);
    		}
     
    		public void set(float value) {
    			if (!getProgram().isInUse()) {
    				throw new IllegalStateException("Program must be in use.");
    			}
    			glUniform1f(getLocation(), value);
    		}
     
    	}
     
    	public static class FloatVec2 extends Uniform {
     
    		public FloatVec2(ShaderProgram owner, String name, UniformType type,
    				int size, int location) {
    			super(owner, name, type, size, location);
    		}
     
    		public void set(float x, float y) {
    			if (!getProgram().isInUse()) {
    				throw new IllegalStateException("Program must be in use.");
    			}
    			glUniform2f(getLocation(), x, y);
    		}
     
    	}
     
    	public static class FloatVec3 extends Uniform {
     
    		public FloatVec3(ShaderProgram owner, String name, UniformType type,
    				int size, int location) {
    			super(owner, name, type, size, location);
    		}
     
    		public void set(float x, float y, float z) {
    			if (!getProgram().isInUse()) {
    				throw new IllegalStateException("Program must be in use.");
    			}
    			glUniform3f(getLocation(), x, y, z);
    		}
     
    	}
     
    	public static class FloatVec4 extends Uniform {
     
    		public FloatVec4(ShaderProgram owner, String name, UniformType type,
    				int size, int location) {
    			super(owner, name, type, size, location);
    		}
     
    		public void set(float x, float y, float z, float w) {
    			if (!getProgram().isInUse()) {
    				throw new IllegalStateException("Program must be in use.");
    			}
    			glUniform4f(getLocation(), x, y, z, w);
    		}
     
    	}
     
    	/*
    	 * And many more...
    	 */
     
    }

    Code :
    public class ShaderProgram {
     
    	private static ShaderProgram inUse;
     
    	private final int glName;
    	private final Uniform[] uniforms;
     
    	public ShaderProgram() {
    		glName = glCreateProgram();
    		/*
    		 * Initialization code: Compile, link, validate shaders...
    		 */
    		int uniformCount = glGetProgrami(getGlName(), GL_ACTIVE_UNIFORMS);
    		int uniformLength = glGetProgrami(getGlName(), GL_ACTIVE_UNIFORM_MAX_LENGTH);
    		uniforms = new Uniform[uniformCount];
    		int index = 0;
    		for (int i = 0; i < uniformCount; i++) {
    			String name 		= glGetActiveUniform(getGlName(), i, uniformLength);
    			int type 			= glGetActiveUniformType(getGlName(), i);
    			int size 			= glGetActiveUniformSize(getGlName(), i);
    			int location 		= glGetUniformLocation(getGlName(), name);
     
    			Uniform uniform 	= makeUniform(name, type, size, location);
    			uniforms[index++] 	= uniform;
    		}
    	}
     
    	public void use() {
    		inUse = this;
    		glUseProgram(getGlName());
    	}
     
    	public boolean isInUse() {
    		return inUse == this;
    	}
     
    	public void delete() {
    		if (isInUse()) {
    			inUse = null;
    			glUseProgram(0);
    		}
    		glDeleteProgram(getGlName());
    	}
     
    	public int getGlName() {
    		return glName;
    	}
     
    	public Uniform getUniform(String name) {
    		for (int i = 0; i < uniforms.length; i++) {
    			if (uniforms[i].getName().equals(name)) {
    				return uniforms[i];
    			}
    		}
    		return null;
    	}
     
    	private Uniform makeUniform(String name, int type, int size, int location) {
    		UniformType enumType = UniformType.getByGlEnum(type);
    		Uniform uniform;
    		switch (enumType) {
    		case FLOAT:
    			uniform = new Uniform.Float(this, name, enumType, size, location);
    			break;
    		case FLOAT_VEC2:
    			uniform = new Uniform.FloatVec2(this, name, enumType, size, location);
    			break;
    		case FLOAT_VEC3:
    			uniform = new Uniform.FloatVec3(this, name, enumType, size, location);
    			break;
    		case FLOAT_VEC4:
    			uniform = new Uniform.FloatVec4(this, name, enumType, size, location);
    			break;
    			/*
    			 * And many more...
    			 */
    		default:
    			uniform = new Uniform.NoUniform(this, name, enumType, size, location);
    			break;
    		}
    		return uniform;
    	}
     
    }

    Code :
    public class SomeCustomShader extends ShaderProgram {
     
    	private final Uniform.Float uniformBias;
    	private final Uniform.FloatVec2 uniformOffset;
     
    	private float bias;
    	private float offsetX;
    	private float offsetY;
     
    	public SomeCustomShader() {
    		super();
     
    		uniformBias = (Uniform.Float) getUniform("bias");
    		uniformOffset = (Uniform.FloatVec2) getUniform("offset");
    	}
     
    	public void setBias(float value) {
    		bias = value;
    		uniformBias.set(value);
    	}
     
    	public float getBias() {
    		return bias;
    	}
     
    	public void setOffset(float x, float y) {
    		offsetX = x;
    		offsetY = y;
    		uniformOffset.set(x, y);
    	}
     
    	public float getOffsetX() {
    		return offsetX;
    	}
     
    	public float getOffsetY() {
    		return offsetY;
    	}
     
    }


    As you can see, if you do it this way you get Type-Safety, good performance, and good design.
    I am very satisfied with my solution and it has always worked out very well for me.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •