Closed ykafia closed 1 year ago
Can you suggest an example of how this would look in user defined shader (currently vs after changing it to match SPV)? I would also note that this would likely mean a new SDSL language version and require some process of migrating from the old one, ideally automated, but likely manual if the differences are big enough to require language changes outside of syntactic sugar.
Sure! I'll add some explanations to make sure I understood the issue well too.
Let's look at the the ShaderBaseStream file for example.
SV_Position
, SV_TargetX
, TEXTURECOORDX
etc are all semantics that are used by HLSL to simplify/automatically handle data passed between shader stages. Meaning If we put a semantic of AAA
to a vertex shader output variable, in the pixel shader stage, creating an input variable with the AAA
semantic will automatically bind the input variable of the PS to the output of the VS.
In GLSL/Vulkan this is handled by layout with locations. It also uses glsl built in values (like Position is bound to the predefined variable gl_Position). Using builtin variables with spirv makes the translation (for both naga and spirv-cross) more correct than if we don't. I've tried to work around it by using extensions of spirv but it's a pain in the neck to make spirv-cross and naga generate correct semantic names. I also started recreating the location thingy that glslangValidator does, but this might be wasteful in cpu and memory.
So for the sake of having a more coherent, simple and faster compilation and translation, i feel it would be best to make SDSL and Spirv share the same logic instead of bending spirv to work with SDSL.
So in SDSL instead of having :
shader ShaderBaseStream {
stage stream float4 ShadingPosition : SV_Position;
stage stream float4 ColorTarget : SV_Target0;
stage stream float4 ColorTarget1 : SV_Target1;
}
We would have :
shader ShaderBaseStream {
// mandatory builtin definition because this will help spirv-cross and naga enormously while saving ourselves some processing power
stage stream float4 ShadingPosition : builtin(Position);
// For non-builtin, either keep the semantic and waste a tidsy bits of processing power and memory to generate layout locations
stage stream float4 ColorTarget : SV_Target0;
// Or use the layout location directly
stage stream float4 ColorTarget1 : layout(1);
}
Builtin
Example of hlsl compiled to spirv here :
I would definitely go with semantic SV_TargetX
notation in order to remove the need for user to calculate layout manually. I'm curious about your comment on "waste processing power and memory to generate layout locations". If SDSL is only a frontend for SPV in this new design then we could perform the conversion at compile time and thus making it easier for the user even for a slightly higher processing cost seems like a good idea.
If it's not too much trouble, can you elaborate why
it's a pain in the neck to make spirv-cross and naga generate correct semantic names
and if there's an existing convention in SDSL for semantic names? If I understand the problem correctly, HLSL doesn't define SV_Position
and instead it's just a user defined semantic nickname which could be anything. Since SPV has a set of predefined concepts we would like to define a direct mapping between certain SDSL semantic names and those SPV built-ins. So when user writes SV_Position
that would be treated as builtin(Position)
for example - thus we could drive enforcement of semantic convention with the transpiler doing the appropriate mapping and the language definition wouldn't have to be augmented. This still may be a breaking change but at least a smaller one IMO.
SPV being extensible, the idea was:
If it's not too much trouble, can you elaborate why
Edit : This is better explained here
keeping my previous answer here
and if there's an existing convention in SDSL for semantic names?
SDSL semantics follow the exact same rules as HLSL, where some semantics starting with SV are predefined to make sure some shader stages get read/write access to only specific variables.
Edit : MSFT did that.
We could make our own semantic system thinking of it.
SPV being extensible, the idea was:
- Extend SPV with additional metadata/opcode to store whatever info required by SDSL mixing/storage, including our own format to store semantic, etc. Let's call this the SDSL-SPV.
- Only when generating final Vulkan shaders to be loaded can this stuff be quickly processed/transformed to actual locations. Basically transform from SDSL-SPV -> SPV. (it might have been slightly different, but just to give an idea of how it could be done)
Yes, this is true, i hadn't thought about it. I assume we should define our own SDSL semantic logic for that too
Just found that Lots of info, it's incredible, So apparently all my questions are answered in the document and on top of that msft allow some decorations for vulkan locations in case anyone wants to.
struct VSInput {
[[vk::location(0)]] float4 pos : POSITION;
[[vk::location(1)]] float3 norm : NORMAL;
};
[[vk::location(1)]]
float4 VSMain(in VSInput input,
[[vk::location(2)]]
in float4 tex : TEXCOORD,
out float4 pos : SV_Position) : TEXCOORD {
pos = input.pos;
return tex;
}
I think ultimately i will follow the same guidelines that MSFT used for HLSL. I'll do the best i can to emulate the semantics and in bug cases then we'd have to advise users to specify locations. I'll close this issue since the DXC doc is answering most of my design questions and that means we're going to make SDSL work like HLSL as it's supposed to be.
Related to #14
For sdsl->spirv compilation I've hit a design issue. Since SDSL is supposed to be a superset of HLSL and spirv a kind of low level vulkan GLSL, the many hsls features that we were using (semantics, packing and such) are not going to be the same in SPV, or might not even exist.
On top of that Spirv-Cross and Naga behave very differently when translating SPV to HLSL which might cause issues with shaders.
I propose we make SDSL closely match SPV features to avoid translation confusion.
It should only affect shader I/O (eg : defining layout position and built-in variables instead of using semantics), not the core of the language (everything that's inside a function.