stanoddly / GameKit

Apache License 2.0
1 stars 0 forks source link

Improve handling of shaders #1

Closed stanoddly closed 2 weeks ago

stanoddly commented 2 weeks ago

Currently the shaders use a builder pattern, e.g.:

Shader vertexShader = gameKitApp.ShaderBuilder.UseVertexStage().FromFileBasedOnFormat("PositionColor").Build();
Shader fragmentShader = gameKitApp.ShaderBuilder.UseFragmentStage().FromFileBasedOnFormat("SolidColor").Build();

It is used to allow specify multiple things: stage, entry-point, resources and the way to load it. However, it's awkward. 😅 This information is available during the shader creation, nobody should specify it at compile time. On top of that, entry-point could be different per each platform.

I think that it would be more beneficial to treat it as a content (read this one) and require an additional file which would contain all the required metadata to be able to configure shader properly. For example MyShader.shadermeta:

{
    "stage": "fragment",
    "resources": {
        "uniformBuffers": 42,
        "...": "..."
    },
    "paths": {
        "vulkan": "vulkan/MyShader.spirv",
        "...": "..."
    },
    "...": "..."
}

(the json schema is stupid I know, just to get the idea)

Then in the code I could just:

Shader vertexShader = gameKitApp.LoadShader("MyShader");

And all the shader metadata would be handled as a content and the code wouldn't expose it. Looks simple and stupid.

stanoddly commented 2 weeks ago

Ok so this is my first proposal. MyShader.shadermeta:

{
    "stage": "fragment",
    "resources": {
        "samplers": 0,
        "storageTextures": 0,
        "storageBuffers": 0,
        "uniformBuffers": 0
    },
    "shaders": [
      {
        "format": "spirv",
        "path": "vulkan/MyShader.spirv",
        "entryPoint": "main"
      }
    ]
}

shaders as a list is future proof, I can potentially add version later. entryPoint is part of the shader because it may be different for each format.

I could also embed a binary shader into shadermeta too:

{
    "format": "spirv",
    "content": "BASE64 VERY LONG CONTENT",
    "entryPoint": "main"
}

But that's a different story.

stanoddly commented 2 weeks ago

In the end I took a slightly different direction.

Instead of shader metadata I will store shaders themselves. ShaderPack with an extension .spak (or .spak.json for json). JSON looks like this:

{
    "stage": "vertex",
    "resources": {
        "samplers": 0,
        "storageTextures": 0,
        "storageBuffers": 0,
        "uniformBuffers": 0
    },
    "shaders": [
        {
            "format": "spirv",
            "entryPoint": "main",
            "content": "..."
        }
    ]
}

For JSON content is either text based shader code (e.g. .msl) or binary in base64 (all other shaders basically).


In future .spak could be generated by a command line tool which would take a source shader language program. The source language could be any language that could be compiled or/and transpiled into different shader languages/bytecodes. That means GLSL, HLSL or even slang (but no Metal and god forbid WGSL).

Before I have the command line I have to do things manually and create base64 via command line:

cat shader.spv | base64 -w 0

Not great, not terrible.