This commit is contained in:
Bill Hollings 2017-06-30 19:11:46 -04:00
commit 5f42746389
15 changed files with 329 additions and 89 deletions

View File

@ -173,6 +173,12 @@ You can make use of the reflection interface to force the name of the struct typ
compiler.set_name(varying_resource.base_type_id, "VertexFragmentLinkage");
```
Some platform may require identical variable name for both vertex outputs and fragment inputs. (for example MacOSX)
to rename varaible base on location, please add
```
--rename-interface-variable <in|out> <location> <new_variable_name>
```
#### HLSL source to legacy GLSL/ESSL
HLSL tends to emit varying struct types to pass data between vertex and fragment.

View File

@ -417,6 +417,13 @@ struct VariableTypeRemap
string new_variable_type;
};
struct InterfaceVariableRename
{
StorageClass storageClass;
uint32_t location;
string variable_name;
};
struct CLIArguments
{
const char *input = nullptr;
@ -438,6 +445,7 @@ struct CLIArguments
vector<Remap> remaps;
vector<string> extensions;
vector<VariableTypeRemap> variable_type_remaps;
vector<InterfaceVariableRename> interface_variable_renames;
string entry;
uint32_t iterations = 1;
@ -463,7 +471,9 @@ static void print_help()
"[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name "
"components] [--extension ext] [--entry name] [--remove-unused-variables] "
"[--flatten-multidimensional-arrays] "
"[--remap-variable-type <variable_name> <new_variable_type>]\n");
"[--remap-variable-type <variable_name> <new_variable_type>] "
"[--rename-interface-variable <in|out> <location> <new_variable_name>] "
"\n");
}
static bool remap_generic(Compiler &compiler, const vector<Resource> &resources, const Remap &remap)
@ -550,6 +560,21 @@ static PlsFormat pls_format(const char *str)
return PlsNone;
}
void rename_interface_variable(Compiler &compiler, const vector<Resource> &resources, const InterfaceVariableRename &rename)
{
for (auto &v : resources)
{
if (!compiler.has_decoration(v.id, spv::DecorationLocation))
continue;
auto loc = compiler.get_decoration(v.id, spv::DecorationLocation);
if (loc != rename.location)
continue;
compiler.set_name(v.id, rename.variable_name);
}
}
int main(int argc, char *argv[])
{
CLIArguments args;
@ -602,6 +627,19 @@ int main(int argc, char *argv[])
args.variable_type_remaps.push_back({ move(var_name), move(new_type) });
});
cbs.add("--rename-interface-variable", [&args](CLIParser &parser) {
StorageClass cls = StorageClassMax;
string clsStr = parser.next_string();
if (clsStr == "in")
cls = StorageClassInput;
else if (clsStr == "out")
cls = StorageClassOutput;
uint32_t loc = parser.next_uint();
string var_name = parser.next_string();
args.interface_variable_renames.push_back({ cls, loc, move(var_name) });
});
cbs.add("--pls-in", [&args](CLIParser &parser) {
auto fmt = pls_format(parser.next_string());
auto name = parser.next_string();
@ -758,6 +796,19 @@ int main(int argc, char *argv[])
continue;
}
for (auto &rename : args.interface_variable_renames)
{
if (rename.storageClass == StorageClassInput)
rename_interface_variable(*compiler, res.stage_inputs, rename);
else if (rename.storageClass == StorageClassOutput)
rename_interface_variable(*compiler, res.stage_outputs, rename);
else
{
fprintf(stderr, "error at --rename-interface-variable <in|out> ...\n");
return EXIT_FAILURE;
}
}
if (args.dump_resources)
{
print_resources(*compiler, res);

View File

@ -0,0 +1,51 @@
struct _CBO
{
float4 a;
float4 b;
float4 c;
float4 d;
};
cbuffer CBO : register(c4)
{
_CBO cbo[2][4];
};
struct _PushMe
{
float4 a;
float4 b;
float4 c;
float4 d;
};
cbuffer PushMe
{
_PushMe push;
};
static float4 FragColor;
struct SPIRV_Cross_Output
{
float4 FragColor : SV_Target0;
};
void frag_main()
{
FragColor = cbo[1][2].a;
FragColor += cbo[1][2].b;
FragColor += cbo[1][2].c;
FragColor += cbo[1][2].d;
FragColor += push.a;
FragColor += push.b;
FragColor += push.c;
FragColor += push.d;
}
SPIRV_Cross_Output main()
{
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.FragColor = FragColor;
return stage_output;
}

View File

@ -0,0 +1,45 @@
struct CBO
{
float4 a;
float4 b;
float4 c;
float4 d;
};
ConstantBuffer<CBO> cbo[2][4] : register(b4);
struct PushMe
{
float4 a;
float4 b;
float4 c;
float4 d;
};
ConstantBuffer<PushMe> push;
static float4 FragColor;
struct SPIRV_Cross_Output
{
float4 FragColor : SV_Target0;
};
void frag_main()
{
FragColor = cbo[1][2].a;
FragColor += cbo[1][2].b;
FragColor += cbo[1][2].c;
FragColor += cbo[1][2].d;
FragColor += push.a;
FragColor += push.b;
FragColor += push.c;
FragColor += push.d;
}
SPIRV_Cross_Output main()
{
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.FragColor = FragColor;
return stage_output;
}

View File

@ -7,15 +7,6 @@ cbuffer CBuffer : register(c3)
{
_CBuffer cbuf;
};
struct _UAV0
{
float4 a;
};
cbuffer UAV0 : register(u1)
{
_UAV0 uav0;
};
struct _PushMe
{
float4 d;
@ -47,7 +38,7 @@ void frag_main()
{
float4 c0 = uSampledImage.Sample(_uSampledImage_sampler, vTex);
float4 c1 = uTexture.Sample(uSampler, vTex);
float4 c2 = (cbuf.a + uav0.a) + registers.d;
float4 c2 = cbuf.a + registers.d;
FragColor = (c0 + c1) + c2;
}

View File

@ -0,0 +1,16 @@
#version 310 es
precision mediump float;
precision highp int;
layout(binding = 0) uniform mediump sampler2D uTex;
layout(location = 0) out vec4 FragColor;
layout(location = 0) in vec4 vTex;
void main()
{
highp vec4 _19 = vTex;
_19.z = vTex.w;
FragColor = textureProj(uTex, _19.xyz);
}

View File

@ -0,0 +1,32 @@
#version 450
layout(std140, binding = 4) uniform CBO
{
vec4 a;
vec4 b;
vec4 c;
vec4 d;
} cbo[2][4];
layout(std430, push_constant) uniform PushMe
{
vec4 a;
vec4 b;
vec4 c;
vec4 d;
} push;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = cbo[1][2].a;
FragColor += cbo[1][2].b;
FragColor += cbo[1][2].c;
FragColor += cbo[1][2].d;
FragColor += push.a;
FragColor += push.b;
FragColor += push.c;
FragColor += push.d;
}

View File

@ -0,0 +1,32 @@
#version 450
layout(std140, binding = 4) uniform CBO
{
vec4 a;
vec4 b;
vec4 c;
vec4 d;
} cbo[2][4];
layout(std430, push_constant) uniform PushMe
{
vec4 a;
vec4 b;
vec4 c;
vec4 d;
} push;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = cbo[1][2].a;
FragColor += cbo[1][2].b;
FragColor += cbo[1][2].c;
FragColor += cbo[1][2].d;
FragColor += push.a;
FragColor += push.b;
FragColor += push.c;
FragColor += push.d;
}

View File

@ -1,11 +1,6 @@
#version 310 es
precision mediump float;
layout(binding = 1, std430) readonly buffer UAV0
{
vec4 a;
} uav0;
layout(binding = 3, std140) uniform CBuffer
{
vec4 a;
@ -27,6 +22,6 @@ void main()
{
vec4 c0 = texture(uSampledImage, vTex);
vec4 c1 = texture(sampler2D(uTexture, uSampler), vTex);
vec4 c2 = cbuf.a + uav0.a + registers.d;
vec4 c2 = cbuf.a + registers.d;
FragColor = c0 + c1 + c2;
}

View File

@ -0,0 +1,12 @@
#version 310 es
precision mediump float;
layout(location = 0) in vec4 vTex;
layout(binding = 0) uniform sampler2D uTex;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = textureProj(uTex, vTex);
}

View File

@ -394,9 +394,6 @@ struct SPIRExpression : IVariant
// it is assumed that this is true almost always.
bool immutable = false;
// If this expression has been used while invalidated.
bool used_while_invalidated = false;
// Before use, this expression must be transposed.
// This is needed for targets which don't support row_major layouts.
bool need_transpose = false;

View File

@ -1194,6 +1194,7 @@ void Compiler::parse(const Instruction &instruction)
case OpSourceExtension:
case OpNop:
case OpLine:
case OpString:
break;
case OpSource:

View File

@ -1327,28 +1327,28 @@ void CompilerGLSL::replace_illegal_names()
{
// clang-format off
static const unordered_set<string> keywords = {
"active", "asm", "atomic_uint", "attribute", "bool", "break",
"bvec2", "bvec3", "bvec4", "case", "cast", "centroid", "class", "coherent", "common", "const", "continue", "default", "discard",
"dmat2", "dmat2x2", "dmat2x3", "dmat2x4", "dmat3", "dmat3x2", "dmat3x3", "dmat3x4", "dmat4", "dmat4x2", "dmat4x3", "dmat4x4",
"do", "double", "dvec2", "dvec3", "dvec4", "else", "enum", "extern", "external", "false", "filter", "fixed", "flat", "float",
"for", "fvec2", "fvec3", "fvec4", "goto", "half", "highp", "hvec2", "hvec3", "hvec4", "if", "iimage1D", "iimage1DArray",
"iimage2D", "iimage2DArray", "iimage2DMS", "iimage2DMSArray", "iimage2DRect", "iimage3D", "iimageBuffer", "iimageCube",
"iimageCubeArray", "image1D", "image1DArray", "image2D", "image2DArray", "image2DMS", "image2DMSArray", "image2DRect",
"image3D", "imageBuffer", "imageCube", "imageCubeArray", "in", "inline", "inout", "input", "int", "interface", "invariant",
"isampler1D", "isampler1DArray", "isampler2D", "isampler2DArray", "isampler2DMS", "isampler2DMSArray", "isampler2DRect",
"isampler3D", "isamplerBuffer", "isamplerCube", "isamplerCubeArray", "ivec2", "ivec3", "ivec4", "layout", "long", "lowp",
"mat2", "mat2x2", "mat2x3", "mat2x4", "mat3", "mat3x2", "mat3x3", "mat3x4", "mat4", "mat4x2", "mat4x3", "mat4x4", "mediump",
"namespace", "noinline", "noperspective", "out", "output", "packed", "partition", "patch", "precision", "public", "readonly",
"resource", "restrict", "return", "row_major", "sample", "sampler1D", "sampler1DArray", "sampler1DArrayShadow",
"sampler1DShadow", "sampler2D", "sampler2DArray", "sampler2DArrayShadow", "sampler2DMS", "sampler2DMSArray",
"sampler2DRect", "sampler2DRectShadow", "sampler2DShadow", "sampler3D", "sampler3DRect", "samplerBuffer",
"samplerCube", "samplerCubeArray", "samplerCubeArrayShadow", "samplerCubeShadow", "short", "sizeof", "smooth", "static",
"struct", "subroutine", "superp", "switch", "template", "this", "true", "typedef", "uimage1D", "uimage1DArray", "uimage2D",
"uimage2DArray", "uimage2DMS", "uimage2DMSArray", "uimage2DRect", "uimage3D", "uimageBuffer", "uimageCube",
"uimageCubeArray", "uint", "uniform", "union", "unsigned", "usampler1D", "usampler1DArray", "usampler2D", "usampler2DArray",
"usampler2DMS", "usampler2DMSArray", "usampler2DRect", "usampler3D", "usamplerBuffer", "usamplerCube",
"usamplerCubeArray", "using", "uvec2", "uvec3", "uvec4", "varying", "vec2", "vec3", "vec4", "void", "volatile", "volatile",
"while", "writeonly"
"active", "asm", "atomic_uint", "attribute", "bool", "break",
"bvec2", "bvec3", "bvec4", "case", "cast", "centroid", "class", "coherent", "common", "const", "continue", "default", "discard",
"dmat2", "dmat2x2", "dmat2x3", "dmat2x4", "dmat3", "dmat3x2", "dmat3x3", "dmat3x4", "dmat4", "dmat4x2", "dmat4x3", "dmat4x4",
"do", "double", "dvec2", "dvec3", "dvec4", "else", "enum", "extern", "external", "false", "filter", "fixed", "flat", "float",
"for", "fvec2", "fvec3", "fvec4", "goto", "half", "highp", "hvec2", "hvec3", "hvec4", "if", "iimage1D", "iimage1DArray",
"iimage2D", "iimage2DArray", "iimage2DMS", "iimage2DMSArray", "iimage2DRect", "iimage3D", "iimageBuffer", "iimageCube",
"iimageCubeArray", "image1D", "image1DArray", "image2D", "image2DArray", "image2DMS", "image2DMSArray", "image2DRect",
"image3D", "imageBuffer", "imageCube", "imageCubeArray", "in", "inline", "inout", "input", "int", "interface", "invariant",
"isampler1D", "isampler1DArray", "isampler2D", "isampler2DArray", "isampler2DMS", "isampler2DMSArray", "isampler2DRect",
"isampler3D", "isamplerBuffer", "isamplerCube", "isamplerCubeArray", "ivec2", "ivec3", "ivec4", "layout", "long", "lowp",
"mat2", "mat2x2", "mat2x3", "mat2x4", "mat3", "mat3x2", "mat3x3", "mat3x4", "mat4", "mat4x2", "mat4x3", "mat4x4", "mediump",
"namespace", "noinline", "noperspective", "out", "output", "packed", "partition", "patch", "precision", "public", "readonly",
"resource", "restrict", "return", "row_major", "sample", "sampler1D", "sampler1DArray", "sampler1DArrayShadow",
"sampler1DShadow", "sampler2D", "sampler2DArray", "sampler2DArrayShadow", "sampler2DMS", "sampler2DMSArray",
"sampler2DRect", "sampler2DRectShadow", "sampler2DShadow", "sampler3D", "sampler3DRect", "samplerBuffer",
"samplerCube", "samplerCubeArray", "samplerCubeArrayShadow", "samplerCubeShadow", "short", "sizeof", "smooth", "static",
"struct", "subroutine", "superp", "switch", "template", "this", "true", "typedef", "uimage1D", "uimage1DArray", "uimage2D",
"uimage2DArray", "uimage2DMS", "uimage2DMSArray", "uimage2DRect", "uimage3D", "uimageBuffer", "uimageCube",
"uimageCubeArray", "uint", "uniform", "union", "unsigned", "usampler1D", "usampler1DArray", "usampler2D", "usampler2DArray",
"usampler2DMS", "usampler2DMSArray", "usampler2DRect", "usampler3D", "usamplerBuffer", "usamplerCube",
"usamplerCubeArray", "using", "uvec2", "uvec3", "uvec4", "varying", "vec2", "vec3", "vec4", "void", "volatile", "volatile",
"while", "writeonly", "texture"
};
// clang-format on
@ -1768,13 +1768,6 @@ string CompilerGLSL::to_func_call_arg(uint32_t id)
void CompilerGLSL::handle_invalid_expression(uint32_t id)
{
auto &expr = get<SPIRExpression>(id);
// This expression has been invalidated in the past.
// Be careful with this expression next pass ...
// Used for OpCompositeInsert forwarding atm.
expr.used_while_invalidated = true;
// We tried to read an invalidated expression.
// This means we need another pass at compilation, but next time, force temporary variables so that they cannot be invalidated.
forced_temporaries.insert(id);
@ -4477,18 +4470,13 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
auto lhs = to_expression(ops[0]);
auto rhs = to_expression(ops[1]);
// It is possible with OpLoad/OpCompositeInsert/OpStore that we get <expr> = <same-expr>.
// For this case, we don't need to invalidate anything and emit any opcode.
if (lhs != rhs)
{
// Tries to optimize assignments like "<lhs> = <lhs> op expr".
// While this is purely cosmetic, this is important for legacy ESSL where loop
// variable increments must be in either i++ or i += const-expr.
// Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0.
if (!optimize_read_modify_write(lhs, rhs))
statement(lhs, " = ", rhs, ";");
register_write(ops[0]);
}
// Tries to optimize assignments like "<lhs> = <lhs> op expr".
// While this is purely cosmetic, this is important for legacy ESSL where loop
// variable increments must be in either i++ or i += const-expr.
// Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0.
if (!optimize_read_modify_write(lhs, rhs))
statement(lhs, " = ", rhs, ";");
register_write(ops[0]);
}
break;
}
@ -4732,26 +4720,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
flush_variable_declaration(composite);
auto *expr = maybe_get<SPIRExpression>(id);
if ((expr && expr->used_while_invalidated) || !should_forward(composite))
{
// Make a copy, then use access chain to store the variable.
statement(declare_temporary(result_type, id), to_expression(composite), ";");
set<SPIRExpression>(id, to_name(id), result_type, true);
auto chain = access_chain_internal(id, elems, length, true);
statement(chain, " = ", to_expression(obj), ";");
}
else
{
auto chain = access_chain_internal(composite, elems, length, true);
statement(chain, " = ", to_expression(obj), ";");
set<SPIRExpression>(id, to_expression(composite), result_type, true);
// Make a copy, then use access chain to store the variable.
statement(declare_temporary(result_type, id), to_expression(composite), ";");
set<SPIRExpression>(id, to_name(id), result_type, true);
auto chain = access_chain_internal(id, elems, length, true);
statement(chain, " = ", to_expression(obj), ";");
register_write(composite);
register_read(id, composite, true);
// Invalidate the old expression we inserted into.
invalid_expressions.insert(composite);
}
break;
}

View File

@ -879,9 +879,20 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
{
auto &type = get<SPIRType>(var.basetype);
bool is_uav = has_decoration(type.self, DecorationBufferBlock);
if (is_uav)
SPIRV_CROSS_THROW("Buffer is SSBO (UAV). This is currently unsupported.");
add_resource_name(type.self);
statement("struct _", to_name(type.self));
string struct_name;
if (options.shader_model >= 51)
struct_name = to_name(type.self);
else
struct_name = join("_", to_name(type.self));
// First, declare the struct of the UBO.
statement("struct ", struct_name);
begin_scope();
type.member_name_cache.clear();
@ -896,11 +907,17 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
end_scope_decl();
statement("");
// TODO: UAV?
statement("cbuffer ", to_name(type.self), to_resource_binding(var));
begin_scope();
statement("_", to_name(type.self), " ", to_name(var.self), ";");
end_scope_decl();
if (options.shader_model >= 51) // SM 5.1 uses ConstantBuffer<T> instead of cbuffer.
{
statement("ConstantBuffer<", struct_name, "> ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";");
}
else
{
statement("cbuffer ", to_name(type.self), to_resource_binding(var));
begin_scope();
statement(struct_name, " ", to_name(var.self), type_to_array_glsl(type), ";");
end_scope_decl();
}
}
void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var)
@ -1586,10 +1603,20 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
if (has_decoration(type.self, DecorationBufferBlock))
space = "u"; // UAV
else if (has_decoration(type.self, DecorationBlock))
space = "c"; // Constant buffers
{
if (options.shader_model >= 51)
space = "b"; // Constant buffers
else
space = "c"; // Constant buffers
}
}
else if (storage == StorageClassPushConstant)
space = "c"; // Constant buffers
{
if (options.shader_model >= 51)
space = "b"; // Constant buffers
else
space = "c"; // Constant buffers
}
break;
}

View File

@ -102,6 +102,14 @@ def validate_shader_hlsl(shader):
if (not force_no_external_validation) and os.path.exists('fxc'):
subprocess.check_call(['fxc', shader])
def shader_to_sm(shader):
if '.sm51.' in shader:
return '51'
elif '.sm20.' in shader:
return '20'
else:
return '50'
def cross_compile_hlsl(shader):
spirv_f, spirv_path = tempfile.mkstemp()
hlsl_f, hlsl_path = tempfile.mkstemp(suffix = os.path.basename(shader))
@ -109,7 +117,9 @@ def cross_compile_hlsl(shader):
os.close(hlsl_f)
subprocess.check_call(['glslangValidator', '-V', '-o', spirv_path, shader])
spirv_cross_path = './spirv-cross'
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl-enable-compat', '--hlsl', '--shader-model', '50'])
sm = shader_to_sm(shader)
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl-enable-compat', '--hlsl', '--shader-model', sm])
subprocess.check_call(['spirv-val', spirv_path])
validate_shader_hlsl(hlsl_path)