• Create Account

### #ActualTelios

Posted 31 July 2012 - 06:39 AM

Wow, a lot of replies - thanks!

You probably don't need a separate MaterialParameter class, your Material could hold a list of ShaderUniforms and provide an interface to set them.

I actually tried that originally, and I agree that it seems more logical. I'm using the ShaderUniform class to represent the current state of the uniform, which naturally can only have one value. The problem I found was that if several materials use the same shader but with different uniforms, then they need a way to store their own uniform values - which is why I added MaterialParameter to do this.

It might be possible to roll the MaterialParameter and ShaderUniform classes into one. If uniforms are copied to each material rather than being shared between them, then there would be no need for the parameter class at all.

Don't cast just to call SetValue(float). Use a basic interface instead example: SetValue(HardwareRegisters &set).
How do you set textures? How do you link to runtime "live" data? Do you need those features? I cannot reasonably tell anything about those by seeing your snippets.
You might need to separate your data model (MaterialParameter) from your live runtime mode (dealing with uniforms).

I'm sorry to sound clueless, but could you explain your SetValue(HardwareRegisters &set) method a little further? Would that be similar to a constant buffer or UBO?

Textures are set using a TextureUniform class derived from ShaderUniform, which have a setValue(TexturePtr texture) method. Per-frame modification of material parameters isn't necessary for this project, but I should be able to make a deep copy of a material (i.e. including parameters), and call setParameter() as needed.

I've seen almost this exact design used in more than one game engine, so you can let that be a confidence booster that you've independently invented a similar solution to a proprietary engine

However, the biggest bottleneck in my experience with these engines was the setting of shader values -- we shipped one game with 30% of the main-thread CPU time being consumed by this task... So now, I really prefer to keep these systems as simple as possible, which means more PoD and less OO-overhead.

This is interesting to me. Each time a material parameter is set in my current design, it involves a number of vtable lookups and static casts, which probably isn't ideal for something that happens very often. Is this what was causing the performance issue?

I'm guessing by 'more PoD and less OO', you mean something like this?
class ShaderUniform
{
public:
void setValue(float value)
void setValue(const Vec3& value);
void setValue(const Mat4x4& value);
void setValue(TexturePtr value);
// etc...

UniformType getType() const;
};


### #1Telios

Posted 31 July 2012 - 06:38 AM

Wow, a lot of replies - thanks!

You probably don't need a separate MaterialParameter class, your Material could hold a list of ShaderUniforms and provide an interface to set them.

I actually tried that originally, and I agree that it seems more logical. I'm using the ShaderUniform class to represent the current state of the uniform, which naturally can only have one value. The problem I found was that if several materials use the same shader but with different uniforms, then they need a way to store their own uniform values - which is why I added MaterialParameter to do this.

It might be possible to roll the MaterialParameter and ShaderUniform classes into one. If uniforms are copied to each material rather than being shared between them, then there would be no need for the parameter class at all.

In my second implementation, I steered away from this design, and tried to create something much simpler. My ShaderUniform class became a struct PoD with a type and union of parameters. I have methods to set them and bind them using a switch statement instead of hitting the vtable through virtual functions (I haven't profiled both implementations yet, so I don't know which is faster). Using a type and union may be simpler, but as I add more types, I need to touch several pieces of code to keep it updated. Using your OO approach allows you to easily create a new derived ShaderUniform and everything should just work.

Don't cast just to call SetValue(float). Use a basic interface instead example: SetValue(HardwareRegisters &set).
How do you set textures? How do you link to runtime "live" data? Do you need those features? I cannot reasonably tell anything about those by seeing your snippets.
You might need to separate your data model (MaterialParameter) from your live runtime mode (dealing with uniforms).

I'm sorry to sound clueless, but could you explain your SetValue(HardwareRegisters &set) method a little further? Would that be similar to a constant buffer or UBO?

Textures are set using a TextureUniform class derived from ShaderUniform, which have a setValue(TexturePtr texture) method. Per-frame modification of material parameters isn't necessary for this project, but I should be able to make a deep copy of a material (i.e. including parameters), and call setParameter() as needed.

I've seen almost this exact design used in more than one game engine, so you can let that be a confidence booster that you've independently invented a similar solution to a proprietary engine

However, the biggest bottleneck in my experience with these engines was the setting of shader values -- we shipped one game with 30% of the main-thread CPU time being consumed by this task... So now, I really prefer to keep these systems as simple as possible, which means more PoD and less OO-overhead.

This is interesting to me. Each time a material parameter is set in my current design, it involves a number of vtable lookups and static casts, which probably isn't ideal for something that happens very often. Is this what was causing the performance issue?

I'm guessing by 'more PoD and less OO', you mean something like this?
class ShaderUniform
{
public:
void setValue(float value)
void setValue(const Vec3& value);
void setValue(const Mat4x4& value);
void setValue(TexturePtr value);
// etc...

UniformType getType() const;
};