KhronosGroup / SPIRV-Cross

SPIRV-Cross is a practical tool and library for performing reflection on SPIR-V and disassembling SPIR-V back to high level languages.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

HLSL: link error: Signatures between stages are incompatible.

Try opened this issue · comments

commented

Very much same case as: #1645
In my case problem is scoped to vertex shader, yet I'm expecting similar cases on every-else shader, save fragment.

// vertex
#version 450
#extension GL_ARB_separate_shader_objects : enable
out gl_PerVertex {
  vec4 gl_Position;
  };
layout(location = 0) in vec2 inPos;
layout(location = 0) out VsData {
  vec4 array[2];
  vec2 pos;
  } shOut;
void main() {
  shOut.pos   = inPos;
  gl_Position = vec4(inPos, 1.0, 1.0);
  }

Spir-v cross generated this code:

// vertex
static float4 gl_Position;
static float2 inPos;
struct VsData
{
    float4 array[2] : TEXCOORD0;
    float2 pos : TEXCOORD2;
};
static VsData shOut;
struct SPIRV_Cross_Input
{
    float2 inPos : TEXCOORD0;
};
struct SPIRV_Cross_Output
{
    float4 gl_Position : SV_Position;
};
void vert_main()
{
    shOut.pos = inPos;
    gl_Position = float4(inPos, 1.0f, 1.0f);
    gl_Position.y = -gl_Position.y;
}
/**
 * This signature mixing return and out schemes, what seems 
 * to be a root cause of following issue in DX compiller
*/
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input, out VsData stage_outputshOut)  
{
    inPos = stage_input.inPos;
    vert_main();
    stage_outputshOut = shOut;
    SPIRV_Cross_Output stage_output;
    stage_output.gl_Position = gl_Position;
    return stage_output;
}

Signature generated by DirectX compiler:

// vertex
// Input signature:
//
// Name                 Index   Mask Register SysValue  Format   Used
// -------------------- ----- ------ -------- -------- ------- ------
// TEXCOORD                 0   xy          0     NONE   float   xy  
//
//
// Output signature:
//
// Name                 Index   Mask Register SysValue  Format   Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_Position              0   xyzw        0      POS   float   xyzw
// TEXCOORD                 0   xyzw        1     NONE   float   xyzw  <--- all registers are shifted by one
// TEXCOORD                 1   xyzw        2     NONE   float   xyzw
// TEXCOORD                 2   xy          3     NONE   float   xy  
// fragment
// Input signature:
//
// Name                 Index   Mask Register SysValue  Format   Used
// -------------------- ----- ------ -------- -------- ------- ------
// TEXCOORD                 0   xyzw        0     NONE   float       
// TEXCOORD                 1   xyzw        1     NONE   float       
// TEXCOORD                 2   xy          2     NONE   float   xy  
//
//
// Output signature:
//
// Name                 Index   Mask Register SysValue  Format   Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_Target                0   xyzw        0   TARGET   float   xyzw

Root cause:
I'm pretty sure that the root cause is in mix of return+out schemes, working on a patch with out-only code generator.
Also: I don't know for sure if it's a illegal code for DX or just DX compiler bug.

commented

Here is a patch that changes shader interface to use only return scheme.
It works locally, but cons is: it alters output for every HLSL test case, making it hard to verify. I don't know for sure what is process in that case: should I create a pull-request anyway or I need to do something before creating PR? Please advice, thanks!

diff --git a/Engine/thirdparty/spirv_cross/spirv_hlsl.cpp b/Engine/thirdparty/spirv_cross/spirv_hlsl.cpp
index 1dfb96a..d6cba2e 100644
--- a/Engine/thirdparty/spirv_cross/spirv_hlsl.cpp
+++ b/Engine/thirdparty/spirv_cross/spirv_hlsl.cpp
@@ -1400,7 +1400,7 @@ void CompilerHLSL::emit_resources()
 		// Do not emit I/O blocks here.
 		// I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders
 		// and tessellation down the line.
-		if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
+		if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
 		    interface_variable_exists_in_entry_point(var.self))
 		{
 			if (var.storage == StorageClassInput)
@@ -2393,27 +2393,6 @@ void CompilerHLSL::emit_hlsl_entry_point()
 	if (require_input)
 		arguments.push_back("SPIRV_Cross_Input stage_input");
 
-	// Add I/O blocks as separate arguments with appropriate storage qualifier.
-	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
-		auto &type = this->get<SPIRType>(var.basetype);
-		bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
-
-		if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
-			return;
-
-		if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
-		{
-			if (var.storage == StorageClassInput)
-			{
-				arguments.push_back(join("in ", variable_decl(type, join("stage_input", to_name(var.self)))));
-			}
-			else if (var.storage == StorageClassOutput)
-			{
-				arguments.push_back(join("out ", variable_decl(type, join("stage_output", to_name(var.self)))));
-			}
-		}
-	});
-
 	auto &execution = get_entry_point();
 
 	switch (execution.model)
@@ -2574,14 +2553,13 @@ void CompilerHLSL::emit_hlsl_entry_point()
 	// Copy from stage input struct to globals.
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
 		auto &type = this->get<SPIRType>(var.basetype);
-		bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
 
 		if (var.storage != StorageClassInput)
 			return;
 
 		bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
 
-		if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
+		if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
 		    interface_variable_exists_in_entry_point(var.self))
 		{
 			auto name = to_name(var.self);
@@ -2597,13 +2575,6 @@ void CompilerHLSL::emit_hlsl_entry_point()
 				statement(name, " = stage_input.", name, ";");
 			}
 		}
-
-		// I/O blocks don't use the common stage input/output struct, but separate outputs.
-		if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
-		{
-			auto name = to_name(var.self);
-			statement(name, " = stage_input", name, ";");
-		}
 	});
 
 	// Run the shader.
@@ -2616,22 +2587,6 @@ void CompilerHLSL::emit_hlsl_entry_point()
 	else
 		SPIRV_CROSS_THROW("Unsupported shader stage.");
 
-	// Copy block outputs.
-	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
-		auto &type = this->get<SPIRType>(var.basetype);
-		bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
-
-		if (var.storage != StorageClassOutput)
-			return;
-
-		// I/O blocks don't use the common stage input/output struct, but separate outputs.
-		if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
-		{
-			auto name = to_name(var.self);
-			statement("stage_output", name, " = ", name, ";");
-		}
-	});
-
 	// Copy stage outputs.
 	if (require_output)
 	{
@@ -2668,12 +2623,11 @@ void CompilerHLSL::emit_hlsl_entry_point()
 
 		ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
 			auto &type = this->get<SPIRType>(var.basetype);
-			bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
 
 			if (var.storage != StorageClassOutput)
 				return;
 
-			if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
+			if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
 			    !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
 			{
 				auto name = to_name(var.self);

commented

Here is the output for case in ticket description:

static float4 gl_Position;
static float2 inPos;
struct VsData
{
    float4 array[2] : TEXCOORD0;
    float2 pos : TEXCOORD2;
};
static VsData shOut;
struct SPIRV_Cross_Input
{
    float2 inPos : TEXCOORD0;
};
struct SPIRV_Cross_Output
{
    VsData shOut : TEXCOORD0;
    float4 gl_Position : SV_Position;
};
void vert_main()
{
    shOut.pos = inPos;
    gl_Position = float4(inPos, 1.0f, 1.0f);
    gl_Position.y = -gl_Position.y;
}

SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
    inPos = stage_input.inPos;
    vert_main();
    SPIRV_Cross_Output stage_output;
    stage_output.gl_Position = gl_Position;
    stage_output.shOut = shOut;
    return stage_output;
}
static float4 outColor;
struct VsData
{
    float4 array[2] : TEXCOORD0;
    float2 pos : TEXCOORD2;
};
static VsData shInp;
struct SPIRV_Cross_Input
{
    VsData shInp : TEXCOORD0;
};
struct SPIRV_Cross_Output
{
    float4 outColor : SV_Target0;
};
void frag_main()
{
    float2 v = (shInp.pos * 0.5f.xx) + 0.5f.xx;
    outColor = float4(v, 0.0f, 1.0f);
}
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
    shInp = stage_input.shInp;
    frag_main();
    SPIRV_Cross_Output stage_output;
    stage_output.outColor = outColor;
    return stage_output;
}

The ideal case is that the output only changes for shaders which actually do have IO blocks. I'll test the patch when I have some time to try coming up with a suggestion.

Ok, tried running the patch, but the diff is very reasonable. Sure you used the correct glslang/spirv-tools reference?

 reference/opt/shaders-hlsl/frag/io-block.frag                             |  9 +++++++--
 reference/opt/shaders-hlsl/vert/locations.vert                            |  5 +++--
 reference/opt/shaders-hlsl/vert/qualifiers.vert                           |  5 +++--
 reference/shaders-hlsl-no-opt/asm/vert/block-struct-initializer.asm.vert  |  5 +++--
 reference/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert      |  5 +++--
 reference/shaders-hlsl-no-opt/vert/block-io-auto-location-assignment.vert | 11 +++++++++--
 reference/shaders-hlsl/frag/io-block.frag                                 |  9 +++++++--
 reference/shaders-hlsl/vert/locations.vert                                |  5 +++--
 reference/shaders-hlsl/vert/qualifiers.vert                               |  5 +++--

Ok, looking some more. I think there are some things to consider here:

  • Block members have their own non-contiguous locations, and thus we should flatten the members individually into the IO structs before we sort.
  • Doesn't seem like we're handling the intricacies of block locations here either.

I think we'll basically need to rewrite this thing to make it work well. I'll just do it.

commented

Ok, tried running the patch, but the diff is very reasonable.

Thanks, Hans! Actually I've never tried to run - large diff was my assumption basically.

I think we'll basically need to rewrite this thing to make it work well. I'll just do it.

I've tested your patch in my game engine - the issue is gone, now shading works.
Eh, and I was up to add extra line in my resume :D