Configuration scripts - part 3

In Part 1 I described the configuration system in the engine. In Part 2 I described the reason and thoughts behind the topic: Designing intuitive API that makes sense. Now time for Part 3. This time we will analyze changes in script and reason behind it. 

Compute shader script

Let's start with the simplest compute shader script that doing anything :
Compute{
    ShaderFile =  "sys://Shaders/ComputeShader.glsl"}
New version:
Compute{
    Shader.Bind("sys://Shaders/ComputeShader.glsl");
}
This may look like a small change but it brings consistency to script. From now on there is only function style calls with ';' at the end.

Defines

Let's now add Define to script. In original format this would look like this:
Compute{
    Define("MY_DEFINE")
    ShaderFile =  "sys://Shaders/ComputeShader.glsl"}
The new one is more flexible and allows to do it in 3 ways:

1st:
Compute{
    Shader.Define("MY_DEFINE");
    Shader.Bind("sys://Shaders/ComputeShader.glsl");
}
2nd:
Compute{
    Shader        .Define("MY_DEFINE")
        .Bind("sys://Shaders/ComputeShader.glsl");
}
3rd:
Compute{
    Shader    {
        Define("MY_DEFINE")
        Bind("sys://Shaders/ComputeShader.glsl");
     }
}
This is the first big improvement. Thanks to a new format I have a lot more flexibility in a way how I write scripts and bringing context to calls. OpenGL compute program is built using a combination of defines and shader files. The new format reflects that by syntax.

Registers

This was the biggest change in the whole script. Originally this would look like this:
 Compute{
    Define("MY_DEFINE")
    Reg(0) = UserImage(0, "Write", 0)
    Reg(1:4) = UserTexture(4)
    Reg(4) = Texture("sys://Textures/Texture.dds")
    Reg(5) = UserTexture(7)
    ShaderFile "sys://Shaders/ComputeShader.glsl"}
The new version:
Compute{
    Shader        .Define("MY_DEFINE")
        .Bind("sys://Shaders/ComputeShader.glsl");
    Registers[0]
        .BindUserImage(0, "Write", 0);
    Registers[1:4]
        .BindUserTexture([4:7]);
    Registers[4]
        .BindTexture("sys://Textures/Texture.dds");
    Registers[5]
        .BindUserTexture(7);
}
As you can see once again I switched to version that gives you context and introduced python styles multi indexing. Thanks to that there is no more confusion between:

Registers[1:4].BindUserTexture(4); -- Assign user texture 4 to registers [1,2,3]Registers[1:4].BindUserTexture([4:8]); -- Assign user texture [4,5,6] to registers [1,2,3]

Summary

All of this may sound like a trivial change but it makes a big difference. This was also a "gate" to further thoughts about coding in general.

When we creating API we spend so much thinking about what it should do and not so much how we want to use it. This is cool you now have the most efficient system that does all this cool stuff... but ... Yeah if there is no good API that is intuitive to use and kind of lead coder what it should do then all your work may be wasted. 

From my experience awaiting you one of a few scenarios:
  1. This system will look scary to people and they will want to avoid it as much as they can. You will be one of the few chosen ones that touch this code. For everybody else, this will be a black box that they don't want to use.   
  2. People will start using your code but won't fully understand what they do. A simple mistake here and there, some wrong use cases and your system will always be at fault. Awaiting you a lot of discussing that this is not a fault of your system and support of all kinds of weird use cases. 
  3. People will decide that this is unusable and will start creating a custom wrapper around your code. This wrapper often will add a lot of overhead that will be blamed on your system. Often the reason behind this is that wrapper does more than just making your API simpler. It mixes the use of your code with their use cases. You will be lucky if in codebase there will be only one of them. 
  4. This code is black magic but people still want its functionality. Answer: write own version of it. Sadly, in the end, there will be probably a few versions of it and none of them will be as efficient or secured like yours.
You may, of course, argue that this is not your fault and responsible are these incompetent programmers that cannot use your beautiful system. Then good luck in life, the reality is that in the work environment we deal with different people. Some are great programmers other not so much (They may be at the same time grate in math, shaders or UX). 

From my personal experience: 

Some systems that I wrote in the past I relearn every time I work with them. They feel somehow wrong, I need to think about what to fill into them, they are really easy to make mistakes or they just have inconsistent API which forces me to think how to use some function and not just code. 

Remember: I write here about my own code. Yeah ... this is sad ... But the reality is that all of us wrote code like that. These days my priority is to have fun with coding. That is why when I encounter code like that I try to improve it. When I write a new code I try to make it a way that is fun to use it even if it will affect in some way the underlying system. 

For clarification in no means I promoting here making bad code. What I mean is to make good code with a nice API. 

To next time.
Greg


Comments

Popular posts from this blog

Query commands execution

W.U. 0x20

Lets play : Good code / Bad code