Merge pull request #136 from KhronosGroup/hlsl-builtin

Rewrite HLSL backend handling of inputs and outputs.
This commit is contained in:
Hans-Kristian Arntzen 2017-03-21 13:56:50 +01:00 committed by GitHub
commit ba09b0e1a0
30 changed files with 1137 additions and 241 deletions

View File

@ -4,15 +4,15 @@ static float4 FragColor;
static float4 vColor;
static float2 vTex;
struct InputFrag
struct SPIRV_Cross_Input
{
float4 vColor : TEXCOORD0;
float2 vTex : TEXCOORD1;
};
struct OutputFrag
struct SPIRV_Cross_Output
{
float4 FragColor : COLOR;
float4 FragColor : SV_Target0;
};
void frag_main()
@ -20,12 +20,12 @@ void frag_main()
FragColor = vColor * tex2D(uTex, vTex);
}
OutputFrag main(InputFrag input)
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
vColor = input.vColor;
vTex = input.vTex;
vColor = stage_input.vColor;
vTex = stage_input.vTex;
frag_main();
OutputFrag output;
output.FragColor = FragColor;
return output;
SPIRV_Cross_Output stage_output;
stage_output.FragColor = FragColor;
return stage_output;
}

View File

@ -0,0 +1,33 @@
static float4 gl_FragCoord;
static float gl_FragDepth;
static float4 FragColor;
static float4 vColor;
struct SPIRV_Cross_Input
{
float4 gl_FragCoord : SV_Position;
float4 vColor : TEXCOORD0;
};
struct SPIRV_Cross_Output
{
float gl_FragDepth : SV_Depth;
float4 FragColor : SV_Target0;
};
void frag_main()
{
FragColor = gl_FragCoord + vColor;
gl_FragDepth = 0.5f;
}
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
gl_FragCoord = stage_input.gl_FragCoord;
vColor = stage_input.vColor;
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.gl_FragDepth = gl_FragDepth;
stage_output.FragColor = FragColor;
return stage_output;
}

View File

@ -0,0 +1,28 @@
static float4 FragColor;
struct VertexOut
{
float4 a : TEXCOORD1;
float4 b : TEXCOORD2;
};
static VertexOut _12;
struct SPIRV_Cross_Output
{
float4 FragColor : SV_Target0;
};
void frag_main()
{
FragColor = _12.a + _12.b;
}
SPIRV_Cross_Output main(in VertexOut stage_input_12)
{
_12 = stage_input_12;
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.FragColor = FragColor;
return stage_output;
}

View File

@ -0,0 +1,31 @@
static float4 RT0;
static float4 RT1;
static float4 RT2;
static float4 RT3;
struct SPIRV_Cross_Output
{
float4 RT0 : SV_Target0;
float4 RT1 : SV_Target1;
float4 RT2 : SV_Target2;
float4 RT3 : SV_Target3;
};
void frag_main()
{
RT0 = float4(1.0f, 1.0f, 1.0f, 1.0f);
RT1 = float4(2.0f, 2.0f, 2.0f, 2.0f);
RT2 = float4(3.0f, 3.0f, 3.0f, 3.0f);
RT3 = float4(4.0f, 4.0f, 4.0f, 4.0f);
}
SPIRV_Cross_Output main()
{
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.RT0 = RT0;
stage_output.RT1 = RT1;
stage_output.RT2 = RT2;
stage_output.RT3 = RT3;
return stage_output;
}

View File

@ -0,0 +1,8 @@
void frag_main()
{
}
void main()
{
frag_main();
}

View File

@ -0,0 +1,17 @@
static float4 vColor;
struct SPIRV_Cross_Input
{
float4 vColor : TEXCOORD0;
};
void frag_main()
{
float4 v = vColor;
}
void main(SPIRV_Cross_Input stage_input)
{
vColor = stage_input.vColor;
frag_main();
}

View File

@ -7,23 +7,22 @@ cbuffer UBO
{
_UBO _16;
};
uniform float4 gl_HalfPixel;
static float4 gl_Position;
static float4 aVertex;
static float3 vNormal;
static float3 aNormal;
struct InputVert
struct SPIRV_Cross_Input
{
float3 aNormal : TEXCOORD0;
float4 aVertex : TEXCOORD1;
};
struct OutputVert
struct SPIRV_Cross_Output
{
float4 gl_Position : SV_Position;
float3 vNormal : TEXCOORD0;
float4 gl_Position : POSITION;
};
void vert_main()
@ -32,15 +31,13 @@ void vert_main()
vNormal = aNormal;
}
OutputVert main(InputVert input)
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
aVertex = input.aVertex;
aNormal = input.aNormal;
aVertex = stage_input.aVertex;
aNormal = stage_input.aNormal;
vert_main();
OutputVert output;
output.gl_Position = gl_Position;
output.vNormal = vNormal;
output.gl_Position.x = output.gl_Position.x - gl_HalfPixel.x * output.gl_Position.w;
output.gl_Position.y = output.gl_Position.y + gl_HalfPixel.y * output.gl_Position.w;
return output;
SPIRV_Cross_Output stage_output;
stage_output.gl_Position = gl_Position;
stage_output.vNormal = vNormal;
return stage_output;
}

View File

@ -0,0 +1,29 @@
static float4 gl_Position;
static int gl_VertexIndex;
static int gl_InstanceIndex;
struct SPIRV_Cross_Input
{
uint gl_VertexIndex : SV_VertexID;
uint gl_InstanceIndex : SV_InstanceID;
};
struct SPIRV_Cross_Output
{
float4 gl_Position : SV_Position;
};
void vert_main()
{
float _19 = float(gl_VertexIndex + gl_InstanceIndex);
gl_Position = float4(_19, _19, _19, _19);
}
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
gl_VertexIndex = int(stage_input.gl_VertexIndex);
gl_InstanceIndex = int(stage_input.gl_InstanceIndex);
vert_main();
SPIRV_Cross_Output stage_output;
stage_output.gl_Position = gl_Position;
return stage_output;
}

View File

@ -0,0 +1,75 @@
struct Foo
{
float3 a;
float3 b;
float3 c;
};
static float4 gl_Position;
static float4 Input2;
static float4 Input4;
static float4 Input0;
static float vLocation0;
static float vLocation1;
static float vLocation2[2];
static Foo vLocation4;
static float vLocation9;
struct VertexOut
{
float3 color : TEXCOORD7;
float3 foo : TEXCOORD8;
};
static VertexOut vout;
struct SPIRV_Cross_Input
{
float4 Input2 : TEXCOORD2;
float4 Input4 : TEXCOORD4;
float4 Input0 : TEXCOORD0;
};
struct SPIRV_Cross_Output
{
float4 gl_Position : SV_Position;
float vLocation0 : TEXCOORD0;
float vLocation1 : TEXCOORD1;
float vLocation2[2] : TEXCOORD2;
Foo vLocation4 : TEXCOORD4;
float vLocation9 : TEXCOORD9;
};
void vert_main()
{
gl_Position = ((float4(1.0f, 1.0f, 1.0f, 1.0f) + Input2) + Input4) + Input0;
vLocation0 = 0.0f;
vLocation1 = 1.0f;
vLocation2[0] = 2.0f;
vLocation2[1] = 2.0f;
Foo foo;
foo.a = float3(1.0f, 1.0f, 1.0f);
foo.b = float3(1.0f, 1.0f, 1.0f);
foo.c = float3(1.0f, 1.0f, 1.0f);
vLocation4 = foo;
vLocation9 = 9.0f;
vout.color = float3(2.0f, 2.0f, 2.0f);
vout.foo = float3(4.0f, 4.0f, 4.0f);
}
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input, out VertexOut stage_outputvout)
{
Input2 = stage_input.Input2;
Input4 = stage_input.Input4;
Input0 = stage_input.Input0;
vert_main();
stage_outputvout = vout;
SPIRV_Cross_Output stage_output;
stage_output.gl_Position = gl_Position;
stage_output.vLocation0 = vLocation0;
stage_output.vLocation1 = vLocation1;
stage_output.vLocation2 = vLocation2;
stage_output.vLocation4 = vLocation4;
stage_output.vLocation9 = vLocation9;
return stage_output;
}

View File

@ -0,0 +1,18 @@
static float4 gl_Position;
struct SPIRV_Cross_Output
{
float4 gl_Position : SV_Position;
};
void vert_main()
{
gl_Position = float4(1.0f, 1.0f, 1.0f, 1.0f);
}
SPIRV_Cross_Output main()
{
vert_main();
SPIRV_Cross_Output stage_output;
stage_output.gl_Position = gl_Position;
return stage_output;
}

View File

@ -0,0 +1,50 @@
static float4 gl_Position;
static float vFlat;
static float vCentroid;
static float vSample;
static float vNoperspective;
struct Block
{
nointerpolation float vFlat : TEXCOORD4;
centroid float vCentroid : TEXCOORD5;
sample float vSample : TEXCOORD6;
noperspective float vNoperspective : TEXCOORD7;
};
static Block vout;
struct SPIRV_Cross_Output
{
float4 gl_Position : SV_Position;
nointerpolation float vFlat : TEXCOORD0;
centroid float vCentroid : TEXCOORD1;
sample float vSample : TEXCOORD2;
noperspective float vNoperspective : TEXCOORD3;
};
void vert_main()
{
gl_Position = float4(1.0f, 1.0f, 1.0f, 1.0f);
vFlat = 0.0f;
vCentroid = 1.0f;
vSample = 2.0f;
vNoperspective = 3.0f;
vout.vFlat = 0.0f;
vout.vCentroid = 1.0f;
vout.vSample = 2.0f;
vout.vNoperspective = 3.0f;
}
SPIRV_Cross_Output main(out Block stage_outputvout)
{
vert_main();
stage_outputvout = vout;
SPIRV_Cross_Output stage_output;
stage_output.gl_Position = gl_Position;
stage_output.vFlat = vFlat;
stage_output.vCentroid = vCentroid;
stage_output.vSample = vSample;
stage_output.vNoperspective = vNoperspective;
return stage_output;
}

View File

@ -0,0 +1,11 @@
#version 310 es
precision mediump float;
layout(location = 0) out vec4 FragColor;
layout(location = 0) in vec4 vColor;
void main()
{
FragColor = gl_FragCoord + vColor;
gl_FragDepth = 0.5;
}

View File

@ -0,0 +1,16 @@
#version 310 es
#extension GL_EXT_shader_io_blocks : require
precision mediump float;
layout(location = 1) in VertexOut
{
vec4 a;
vec4 b;
};
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = a + b;
}

View File

@ -0,0 +1,15 @@
#version 310 es
precision mediump float;
layout(location = 0) out vec4 RT0;
layout(location = 1) out vec4 RT1;
layout(location = 2) out vec4 RT2;
layout(location = 3) out vec4 RT3;
void main()
{
RT0 = vec4(1.0);
RT1 = vec4(2.0);
RT2 = vec4(3.0);
RT3 = vec4(4.0);
}

View File

@ -0,0 +1,5 @@
#version 310 es
void main()
{
}

View File

@ -0,0 +1,9 @@
#version 310 es
precision mediump float;
layout(location = 0) in vec4 vColor;
void main()
{
vec4 v = vColor;
}

View File

@ -0,0 +1,6 @@
#version 310 es
void main()
{
gl_Position = vec4(float(gl_VertexIndex + gl_InstanceIndex));
}

View File

@ -0,0 +1,51 @@
#version 310 es
#extension GL_EXT_shader_io_blocks : require
struct Foo
{
vec3 a;
vec3 b;
vec3 c;
};
// This will lock to input location 2.
layout(location = 2) in vec4 Input2;
// This will lock to input location 4.
layout(location = 4) in vec4 Input4;
// This will pick first available, which is 0.
in vec4 Input0;
// Locks output 0.
layout(location = 0) out float vLocation0;
// Locks output 1.
layout(location = 1) out float vLocation1;
// Picks first available two locations, so, 2 and 3.
out float vLocation2[2];
// Picks first available location, 4.
out Foo vLocation4;
// Picks first available location 9.
out float vLocation9;
// Locks location 7 and 8.
layout(location = 7) out VertexOut
{
vec3 color;
vec3 foo;
} vout;
void main()
{
gl_Position = vec4(1.0) + Input2 + Input4 + Input0;
vLocation0 = 0.0;
vLocation1 = 1.0;
vLocation2[0] = 2.0;
vLocation2[1] = 2.0;
Foo foo;
foo.a = vec3(1.0);
foo.b = vec3(1.0);
foo.c = vec3(1.0);
vLocation4 = foo;
vLocation9 = 9.0;
vout.color = vec3(2.0);
vout.foo = vec3(4.0);
}

View File

@ -0,0 +1,6 @@
#version 310 es
void main()
{
gl_Position = vec4(1.0);
}

View File

@ -0,0 +1,27 @@
#version 450
layout(location = 0) flat out float vFlat;
layout(location = 1) centroid out float vCentroid;
layout(location = 2) sample out float vSample;
layout(location = 3) noperspective out float vNoperspective;
layout(location = 4) out Block
{
flat float vFlat;
centroid float vCentroid;
sample float vSample;
noperspective float vNoperspective;
} vout;
void main()
{
gl_Position = vec4(1.0);
vFlat = 0.0;
vCentroid = 1.0;
vSample = 2.0;
vNoperspective = 3.0;
vout.vFlat = 0.0;
vout.vCentroid = 1.0;
vout.vSample = 2.0;
vout.vNoperspective = 3.0;
}

View File

@ -297,6 +297,8 @@ string CompilerCPP::compile()
backend.explicit_struct_type = true;
backend.use_initializer_list = true;
update_active_builtins();
uint32_t pass_count = 0;
do
{

View File

@ -136,7 +136,7 @@ bool Compiler::block_is_pure(const SPIRBlock &block)
return true;
}
string Compiler::to_name(uint32_t id, bool allow_alias)
string Compiler::to_name(uint32_t id, bool allow_alias) const
{
if (allow_alias && ids.at(id).get_type() == TypeType)
{
@ -491,7 +491,7 @@ bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t
case OpCopyMemory:
{
if (length < 3)
if (length < 2)
return false;
auto *var = compiler.maybe_get<SPIRVariable>(args[0]);
@ -2865,7 +2865,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
case OpCopyMemory:
{
if (length < 3)
if (length < 2)
return false;
uint32_t lhs = args[0];
@ -3116,3 +3116,126 @@ bool Compiler::get_common_basic_type(const SPIRType &type, SPIRType::BaseType &b
return true;
}
}
bool Compiler::ActiveBuiltinHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t length)
{
const auto add_if_builtin = [&](uint32_t id) {
// Only handles variables here.
// Builtins which are part of a block are handled in AccessChain.
auto *var = compiler.maybe_get<SPIRVariable>(id);
if (var && compiler.meta[id].decoration.builtin)
{
auto &type = compiler.get<SPIRType>(var->basetype);
auto &flags =
type.storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins;
flags |= 1ull << compiler.meta[id].decoration.builtin_type;
}
};
switch (opcode)
{
case OpStore:
if (length < 1)
return false;
add_if_builtin(args[0]);
break;
case OpCopyMemory:
if (length < 2)
return false;
add_if_builtin(args[0]);
add_if_builtin(args[1]);
break;
case OpCopyObject:
case OpLoad:
if (length < 3)
return false;
add_if_builtin(args[2]);
break;
case OpFunctionCall:
{
if (length < 3)
return false;
uint32_t count = length - 3;
args += 3;
for (uint32_t i = 0; i < count; i++)
add_if_builtin(args[i]);
break;
}
case OpAccessChain:
case OpInBoundsAccessChain:
{
if (length < 4)
return false;
// Only consider global variables, cannot consider variables in functions yet, or other
// access chains as they have not been created yet.
auto *var = compiler.maybe_get<SPIRVariable>(args[2]);
if (!var)
break;
auto *type = &compiler.get<SPIRType>(var->basetype);
// Start traversing type hierarchy at the proper non-pointer types.
while (type->pointer)
{
assert(type->parent_type);
type = &compiler.get<SPIRType>(type->parent_type);
}
auto &flags =
type->storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins;
uint32_t count = length - 3;
args += 3;
for (uint32_t i = 0; i < count; i++)
{
// Arrays
if (!type->array.empty())
{
type = &compiler.get<SPIRType>(type->parent_type);
}
// Structs
else if (type->basetype == SPIRType::Struct)
{
uint32_t index = compiler.get<SPIRConstant>(args[i]).scalar();
if (index < uint32_t(compiler.meta[type->self].members.size()))
{
auto &decorations = compiler.meta[type->self].members[index];
if (decorations.builtin)
flags |= 1ull << decorations.builtin_type;
}
type = &compiler.get<SPIRType>(type->member_types[index]);
}
else
{
// No point in traversing further. We won't find any extra builtins.
break;
}
}
break;
}
default:
break;
}
return true;
}
void Compiler::update_active_builtins()
{
active_input_builtins = 0;
active_output_builtins = 0;
ActiveBuiltinHandler handler(*this);
traverse_all_reachable_opcodes(get<SPIRFunction>(entry_point), handler);
}

View File

@ -411,7 +411,7 @@ protected:
std::unordered_set<uint32_t> selection_merge_targets;
std::unordered_set<uint32_t> multiselect_merge_targets;
virtual std::string to_name(uint32_t id, bool allow_alias = true);
virtual std::string to_name(uint32_t id, bool allow_alias = true) const;
bool is_builtin_variable(const SPIRVariable &var) const;
bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const;
bool is_immutable(uint32_t id) const;
@ -577,6 +577,17 @@ protected:
void register_combined_image_sampler(SPIRFunction &caller, uint32_t texture_id, uint32_t sampler_id);
};
struct ActiveBuiltinHandler : OpcodeHandler
{
ActiveBuiltinHandler(Compiler &compiler_)
: compiler(compiler_)
{
}
bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
Compiler &compiler;
};
bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const;
bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const;
// This must be an ordered data structure so we always pick the same type aliases.
@ -591,6 +602,11 @@ protected:
std::unordered_set<uint32_t> forced_temporaries;
std::unordered_set<uint32_t> forwarded_temporaries;
uint64_t active_input_builtins = 0;
uint64_t active_output_builtins = 0;
// Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader.
void update_active_builtins();
};
}

View File

@ -310,6 +310,7 @@ string CompilerGLSL::compile()
// Scan the SPIR-V to find trivial uses of extensions.
find_static_extensions();
fixup_image_load_store_access();
update_active_builtins();
uint32_t pass_count = 0;
do
@ -3331,8 +3332,12 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
const auto *type = &expression_type(base);
// For resolving array accesses, etc, keep a local copy for poking.
SPIRType temp;
// Start traversing type hierarchy at the proper non-pointer types.
while (type->pointer)
{
assert(type->parent_type);
type = &get<SPIRType>(type->parent_type);
}
bool access_chain_is_arrayed = false;
bool row_major_matrix_needs_conversion = is_non_native_row_major_matrix(base);
@ -3351,11 +3356,8 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
expr += to_expression(index);
expr += "]";
// We have to modify the type, so keep a local copy.
if (&temp != type)
temp = *type;
type = &temp;
temp.array.pop_back();
assert(type->parent_type);
type = &get<SPIRType>(type->parent_type);
access_chain_is_arrayed = true;
}
@ -3414,11 +3416,7 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
expr += to_expression(index);
expr += "]";
// We have to modify the type, so keep a local copy.
if (&temp != type)
temp = *type;
type = &temp;
temp.columns = 1;
type = &get<SPIRType>(type->parent_type);
}
// Vector -> Scalar
else if (type->vecsize > 1)
@ -3441,11 +3439,7 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
expr += "]";
}
// We have to modify the type, so keep a local copy.
if (&temp != type)
temp = *type;
type = &temp;
temp.vecsize = 1;
type = &get<SPIRType>(type->parent_type);
}
else
SPIRV_CROSS_THROW("Cannot subdivide a scalar value!");
@ -3686,6 +3680,13 @@ std::pair<std::string, uint32_t> CompilerGLSL::flattened_access_chain_offset(uin
{
const auto *type = &expression_type(base);
// Start traversing type hierarchy at the proper non-pointer types.
while (type->pointer)
{
assert(type->parent_type);
type = &get<SPIRType>(type->parent_type);
}
// This holds the type of the current pointer which we are traversing through.
// We always start out from a struct type which is the block.
// This is primarily used to reflect the array strides and matrix strides later.

View File

@ -357,8 +357,8 @@ protected:
virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var);
const char *flags_to_precision_qualifiers_glsl(const SPIRType &type, uint64_t flags);
const char *format_to_glsl(spv::ImageFormat format);
std::string layout_for_member(const SPIRType &type, uint32_t index);
std::string to_interpolation_qualifiers(uint64_t flags);
virtual std::string layout_for_member(const SPIRType &type, uint32_t index);
virtual std::string to_interpolation_qualifiers(uint64_t flags);
uint64_t combined_decoration_for_member(const SPIRType &type, uint32_t index);
std::string layout_for_variable(const SPIRVariable &variable);
std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id);

View File

@ -22,24 +22,6 @@ using namespace spv;
using namespace spirv_cross;
using namespace std;
namespace
{
struct VariableComparator
{
VariableComparator(const std::vector<Meta> &m)
: meta(m)
{
}
bool operator()(SPIRVariable *var1, SPIRVariable *var2)
{
return meta[var1->self].decoration.alias.compare(meta[var2->self].decoration.alias) < 0;
}
const std::vector<Meta> &meta;
};
}
string CompilerHLSL::type_to_glsl(const SPIRType &type)
{
// Ignore the pointer type since GLSL doesn't have pointers.
@ -148,21 +130,15 @@ void CompilerHLSL::emit_header()
void CompilerHLSL::emit_interface_block_globally(const SPIRVariable &var)
{
auto &execution = get_entry_point();
add_resource_name(var.self);
if (execution.model == ExecutionModelVertex && var.storage == StorageClassInput && is_builtin_variable(var))
{
}
else if (execution.model == ExecutionModelVertex && var.storage == StorageClassOutput && is_builtin_variable(var))
{
statement("static float4 gl_Position;");
}
else
{
statement("static ", variable_decl(var), ";");
}
// The global copies of I/O variables should not contain interpolation qualifiers.
// These are emitted inside the interface structs.
auto &flags = meta[var.self].decoration.decoration_flags;
auto old_flags = flags;
flags = 0;
statement("static ", variable_decl(var), ";");
flags = old_flags;
}
const char *CompilerHLSL::to_storage_qualifiers_glsl(const SPIRVariable &var)
@ -178,71 +154,257 @@ const char *CompilerHLSL::to_storage_qualifiers_glsl(const SPIRVariable &var)
return "";
}
void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, uint32_t &binding_number, bool builtins)
void CompilerHLSL::emit_builtin_outputs_in_struct()
{
bool legacy = options.shader_model <= 30;
for (uint32_t i = 0; i < 64; i++)
{
if (!(active_output_builtins & (1ull << i)))
continue;
const char *type = nullptr;
const char *semantic = nullptr;
auto builtin = static_cast<BuiltIn>(i);
switch (builtin)
{
case BuiltInPosition:
type = "float4";
semantic = legacy ? "POSITION" : "SV_Position";
break;
case BuiltInFragDepth:
type = "float";
semantic = legacy ? "DEPTH" : "SV_Depth";
break;
default:
SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
break;
}
if (type && semantic)
statement(type, " ", builtin_to_glsl(builtin), " : ", semantic, ";");
}
}
void CompilerHLSL::emit_builtin_inputs_in_struct()
{
bool legacy = options.shader_model <= 30;
for (uint32_t i = 0; i < 64; i++)
{
if (!(active_input_builtins & (1ull << i)))
continue;
const char *type = nullptr;
const char *semantic = nullptr;
auto builtin = static_cast<BuiltIn>(i);
switch (builtin)
{
case BuiltInFragCoord:
type = "float4";
semantic = legacy ? "VPOS" : "SV_Position";
break;
case BuiltInVertexIndex:
if (legacy)
SPIRV_CROSS_THROW("Vertex index not supported in SM 3.0 or lower.");
type = "uint";
semantic = "SV_VertexID";
break;
case BuiltInInstanceIndex:
if (legacy)
SPIRV_CROSS_THROW("Instance index not supported in SM 3.0 or lower.");
type = "uint";
semantic = "SV_InstanceID";
break;
default:
SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
break;
}
if (type && semantic)
statement(type, " ", builtin_to_glsl(builtin), " : ", semantic, ";");
}
}
uint32_t CompilerHLSL::type_to_consumed_locations(const SPIRType &type) const
{
// TODO: Need to verify correctness.
uint32_t elements = 0;
if (type.basetype == SPIRType::Struct)
{
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
elements += type_to_consumed_locations(get<SPIRType>(type.member_types[i]));
}
else
{
uint32_t array_multiplier = 1;
for (uint32_t i = 0; i < uint32_t(type.array.size()); i++)
{
if (type.array_size_literal[i])
array_multiplier *= type.array[i];
else
array_multiplier *= get<SPIRConstant>(type.array[i]).scalar();
}
elements += array_multiplier * type.columns;
}
return elements;
}
string CompilerHLSL::to_interpolation_qualifiers(uint64_t flags)
{
string res;
//if (flags & (1ull << DecorationSmooth))
// res += "linear ";
if (flags & (1ull << DecorationFlat))
res += "nointerpolation ";
if (flags & (1ull << DecorationNoPerspective))
res += "noperspective ";
if (flags & (1ull << DecorationCentroid))
res += "centroid ";
if (flags & (1ull << DecorationPatch))
res += "patch "; // Seems to be different in actual HLSL.
if (flags & (1ull << DecorationSample))
res += "sample ";
if (flags & (1ull << DecorationInvariant))
res += "invariant "; // Not supported?
return res;
}
void CompilerHLSL::emit_io_block(const SPIRVariable &var)
{
auto &type = get<SPIRType>(var.basetype);
add_resource_name(type.self);
statement("struct ", to_name(type.self));
begin_scope();
type.member_name_cache.clear();
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
{
string semantic;
if (has_member_decoration(type.self, i, DecorationLocation))
{
uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
semantic = join(" : TEXCOORD", location);
}
add_member_name(type, i);
auto &membertype = get<SPIRType>(type.member_types[i]);
statement(to_interpolation_qualifiers(get_member_decoration_mask(type.self, i)),
variable_decl(membertype, to_member_name(type, i)), semantic, ";");
}
end_scope_decl();
statement("");
statement("static ", variable_decl(var), ";");
statement("");
}
void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set<uint32_t> &active_locations)
{
auto &execution = get_entry_point();
auto &type = get<SPIRType>(var.basetype);
const char *binding = "TEXCOORD";
string binding;
bool use_binding_number = true;
bool legacy = options.shader_model <= 30;
if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput)
{
binding = "COLOR";
binding = join(legacy ? "COLOR" : "SV_Target", get_decoration(var.self, DecorationLocation));
use_binding_number = false;
}
else if (execution.model == ExecutionModelVertex && var.storage == StorageClassOutput && is_builtin_variable(var))
const auto get_vacant_location = [&]() -> uint32_t {
for (uint32_t i = 0; i < 64; i++)
if (!active_locations.count(i))
return i;
SPIRV_CROSS_THROW("All locations from 0 to 63 are exhausted.");
};
auto &m = meta[var.self].decoration;
auto name = to_name(var.self);
if (use_binding_number)
{
if (options.shader_model <= 30)
{
binding = "POSITION";
}
uint32_t binding_number;
// If an explicit location exists, use it with TEXCOORD[N] semantic.
// Otherwise, pick a vacant location.
if (m.decoration_flags & (1ull << DecorationLocation))
binding_number = m.location;
else
{
binding = "SV_POSITION";
}
use_binding_number = false;
}
binding_number = get_vacant_location();
bool is_no_builtin = !is_builtin_variable(var) && !var.remapped_variable;
if ((is_no_builtin && !builtins) || (!is_no_builtin && builtins))
{
auto &m = meta[var.self].decoration;
if (use_binding_number)
if (type.columns > 1)
{
if (type.vecsize == 4 && type.columns == 4)
if (!type.array.empty())
SPIRV_CROSS_THROW("Arrays of matrices used as input/output. This is not supported.");
// Unroll matrices.
for (uint32_t i = 0; i < type.columns; i++)
{
for (int i = 0; i < 4; ++i)
{
std::stringstream name;
name << m.alias << "_" << i;
SPIRType newtype = type;
newtype.columns = 1;
statement(variable_decl(newtype, name.str()), " : ", binding, binding_number++, ";");
}
--binding_number;
}
else
{
statement(variable_decl(type, m.alias), " : ", binding, binding_number, ";");
SPIRType newtype = type;
newtype.columns = 1;
statement(to_interpolation_qualifiers(get_decoration_mask(var.self)),
variable_decl(newtype, join(name, "_", i)), " : TEXCOORD", binding_number, ";");
active_locations.insert(binding_number++);
}
}
else
{
if (execution.model == ExecutionModelVertex)
{
statement("float4 gl_Position", " : ", binding, ";");
}
else
{
statement(variable_decl(type, m.alias), " : ", binding, ";");
}
statement(to_interpolation_qualifiers(get_decoration_mask(var.self)), variable_decl(type, name),
" : TEXCOORD", binding_number, ";");
// Structs and arrays should consume more locations.
uint32_t consumed_locations = type_to_consumed_locations(type);
for (uint32_t i = 0; i < consumed_locations; i++)
active_locations.insert(binding_number + i);
}
}
else
statement(variable_decl(type, name), " : ", binding, ";");
}
if (is_no_builtin)
void CompilerHLSL::emit_builtin_variables()
{
// Emit global variables for the interface variables which are statically used by the shader.
for (uint32_t i = 0; i < 64; i++)
{
++binding_number;
if (!((active_input_builtins | active_output_builtins) & (1ull << i)))
continue;
const char *type = nullptr;
auto builtin = static_cast<BuiltIn>(i);
switch (builtin)
{
case BuiltInFragCoord:
case BuiltInPosition:
type = "float4";
break;
case BuiltInFragDepth:
type = "float";
break;
case BuiltInVertexIndex:
case BuiltInInstanceIndex:
type = "int";
break;
default:
SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin)));
break;
}
if (type)
statement("static ", type, " ", builtin_to_glsl(builtin), ";");
}
}
@ -250,10 +412,6 @@ void CompilerHLSL::emit_resources()
{
auto &execution = get_entry_point();
// Emit PLS blocks if we have such variables.
if (!pls_inputs.empty() || !pls_outputs.empty())
emit_pls();
// Output all basic struct types which are not Block or BufferBlock as these are declared inplace
// when such variables are instantiated.
for (auto &id : ids)
@ -334,17 +492,25 @@ void CompilerHLSL::emit_resources()
statement("");
emitted = false;
// Emit builtin input and output variables here.
emit_builtin_variables();
for (auto &id : ids)
{
if (id.get_type() == TypeVariable)
{
auto &var = id.get<SPIRVariable>();
auto &type = get<SPIRType>(var.basetype);
bool block = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) != 0;
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
(var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
// 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.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
(var.storage == StorageClassInput || var.storage == StorageClassOutput) && !is_builtin_variable(var) &&
interface_variable_exists_in_entry_point(var.self))
{
// Only emit non-builtins which are not blocks here. Builtin variables are handled separately.
emit_interface_block_globally(var);
emitted = true;
}
@ -355,81 +521,114 @@ void CompilerHLSL::emit_resources()
statement("");
emitted = false;
if (execution.model == ExecutionModelVertex)
{
statement("struct InputVert");
}
else
{
statement("struct InputFrag");
}
begin_scope();
uint32_t binding_number = 0;
std::vector<SPIRVariable *> variables;
require_input = false;
require_output = false;
unordered_set<uint32_t> active_inputs;
unordered_set<uint32_t> active_outputs;
vector<SPIRVariable *> input_variables;
vector<SPIRVariable *> output_variables;
for (auto &id : ids)
{
if (id.get_type() == TypeVariable)
{
auto &var = id.get<SPIRVariable>();
auto &type = get<SPIRType>(var.basetype);
bool block = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) != 0;
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self))
if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
continue;
// 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) &&
interface_variable_exists_in_entry_point(var.self))
{
if (execution.model == ExecutionModelVertex && is_builtin_variable(var))
continue;
variables.push_back(&var);
if (var.storage == StorageClassInput)
input_variables.push_back(&var);
else
output_variables.push_back(&var);
}
// Reserve input and output locations for block variables as necessary.
if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
{
auto &active = var.storage == StorageClassInput ? active_inputs : active_outputs;
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
{
if (has_member_decoration(type.self, i, DecorationLocation))
{
uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
active.insert(location);
}
}
// Emit the block struct and a global variable here.
emit_io_block(var);
}
}
}
sort(variables.begin(), variables.end(), VariableComparator(meta));
for (auto var : variables)
{
emit_interface_block_in_struct(*var, binding_number, false);
}
for (auto var : variables)
{
emit_interface_block_in_struct(*var, binding_number, true);
}
end_scope_decl();
statement("");
if (execution.model == ExecutionModelVertex)
{
statement("struct OutputVert");
}
else
{
statement("struct OutputFrag");
}
begin_scope();
binding_number = 0;
variables.clear();
for (auto &id : ids)
{
if (id.get_type() == TypeVariable)
const auto variable_compare = [&](const SPIRVariable *a, const SPIRVariable *b) -> bool {
// Sort input and output variables based on, from more robust to less robust:
// - Location
// - Variable has a location
// - Name comparison
// - Variable has a name
// - Fallback: ID
bool has_location_a = has_decoration(a->self, DecorationLocation);
bool has_location_b = has_decoration(b->self, DecorationLocation);
if (has_location_a && has_location_b)
{
auto &var = id.get<SPIRVariable>();
auto &type = get<SPIRType>(var.basetype);
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self))
{
variables.push_back(&var);
}
return get_decoration(a->self, DecorationLocation) < get_decoration(b->self, DecorationLocation);
}
}
sort(variables.begin(), variables.end(), VariableComparator(meta));
for (auto var : variables)
else if (has_location_a && !has_location_b)
return true;
else if (!has_location_a && has_location_b)
return false;
const auto &name1 = to_name(a->self);
const auto &name2 = to_name(b->self);
if (name1.empty() && name2.empty())
return a->self < b->self;
else if (name1.empty())
return true;
else if (name2.empty())
return false;
return name1.compare(name2) < 0;
};
if (!input_variables.empty() || active_input_builtins)
{
emit_interface_block_in_struct(*var, binding_number, false);
require_input = true;
statement("struct SPIRV_Cross_Input");
begin_scope();
sort(input_variables.begin(), input_variables.end(), variable_compare);
emit_builtin_inputs_in_struct();
for (auto var : input_variables)
emit_interface_block_in_struct(*var, active_inputs);
end_scope_decl();
statement("");
}
for (auto var : variables)
if (!output_variables.empty() || active_output_builtins)
{
emit_interface_block_in_struct(*var, binding_number, true);
require_output = true;
statement("struct SPIRV_Cross_Output");
begin_scope();
// FIXME: Use locations properly if they exist.
sort(output_variables.begin(), output_variables.end(), variable_compare);
emit_builtin_outputs_in_struct();
for (auto var : output_variables)
emit_interface_block_in_struct(*var, active_outputs);
end_scope_decl();
statement("");
}
end_scope_decl();
statement("");
// Global variables.
for (auto global : global_variables)
@ -456,6 +655,11 @@ void CompilerHLSL::emit_resources()
}
}
string CompilerHLSL::layout_for_member(const SPIRType &, uint32_t)
{
return "";
}
void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
{
auto &type = get<SPIRType>(var.basetype);
@ -541,97 +745,198 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
void CompilerHLSL::emit_hlsl_entry_point()
{
auto &execution = get_entry_point();
const char *post = "Frag";
if (execution.model == ExecutionModelVertex)
{
post = "Vert";
}
vector<string> arguments;
statement("Output", post, " main(Input", post, " input)");
begin_scope();
if (require_input)
arguments.push_back("SPIRV_Cross_Input stage_input");
// Add I/O blocks as separate arguments with appropriate storage qualifier.
for (auto &id : ids)
{
if (id.get_type() == TypeVariable)
{
auto &var = id.get<SPIRVariable>();
auto &type = get<SPIRType>(var.basetype);
bool block = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) != 0;
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self))
if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
continue;
if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
{
if (execution.model == ExecutionModelVertex && is_builtin_variable(var))
continue;
auto &m = meta[var.self].decoration;
auto &mtype = get<SPIRType>(var.basetype);
if (mtype.vecsize == 4 && mtype.columns == 4)
if (var.storage == StorageClassInput)
{
statement(m.alias, "[0] = input.", m.alias, "_0;");
statement(m.alias, "[1] = input.", m.alias, "_1;");
statement(m.alias, "[2] = input.", m.alias, "_2;");
statement(m.alias, "[3] = input.", m.alias, "_3;");
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();
statement(require_output ? "SPIRV_Cross_Output " : "void ", "main(", merge(arguments), ")");
begin_scope();
bool legacy = options.shader_model <= 30;
// Copy builtins from entry point arguments to globals.
for (uint32_t i = 0; i < 64; i++)
{
if (!(active_input_builtins & (1ull << i)))
continue;
auto builtin = builtin_to_glsl(static_cast<BuiltIn>(i));
switch (static_cast<BuiltIn>(i))
{
case BuiltInFragCoord:
// VPOS in D3D9 is sampled at integer locations, apply half-pixel offset to be consistent.
// TODO: Do we need an option here? Any reason why a D3D9 shader would be used
// on a D3D10+ system with a different rasterization config?
if (legacy)
statement(builtin, " = stage_input.", builtin, " + float4(0.5f, 0.5f, 0.0f, 0.0f);");
else
statement(builtin, " = stage_input.", builtin, ";");
break;
case BuiltInVertexIndex:
case BuiltInInstanceIndex:
// D3D semantics are uint, but shader wants int.
statement(builtin, " = int(stage_input.", builtin, ");");
break;
default:
statement(builtin, " = stage_input.", builtin, ";");
break;
}
}
// Copy from stage input struct to globals.
for (auto &id : ids)
{
if (id.get_type() == TypeVariable)
{
auto &var = id.get<SPIRVariable>();
auto &type = get<SPIRType>(var.basetype);
bool block = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) != 0;
if (var.storage != StorageClassInput)
continue;
if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
interface_variable_exists_in_entry_point(var.self))
{
auto name = to_name(var.self);
auto &mtype = get<SPIRType>(var.basetype);
if (mtype.columns > 1)
{
// Unroll matrices.
for (uint32_t col = 0; col < mtype.columns; col++)
statement(name, "[", col, "] = stage_input.", name, "_0;");
}
else
{
statement(m.alias, " = input.", m.alias, ";");
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.
if (execution.model == ExecutionModelVertex)
{
statement("vert_main();");
}
else
{
else if (execution.model == ExecutionModelFragment)
statement("frag_main();");
}
statement("Output", post, " output;");
else
SPIRV_CROSS_THROW("Unsupported shader stage.");
// Copy block outputs.
for (auto &id : ids)
{
if (id.get_type() == TypeVariable)
{
auto &var = id.get<SPIRVariable>();
auto &type = get<SPIRType>(var.basetype);
bool block = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) != 0;
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self))
if (var.storage != StorageClassOutput)
continue;
// 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 &m = meta[var.self].decoration;
bool is_no_builtin = !is_builtin_variable(var) && !var.remapped_variable;
if (is_no_builtin)
statement("output.", m.alias, " = ", m.alias, ";");
else if (execution.model == ExecutionModelVertex)
{
statement("output.gl_Position = gl_Position;");
}
auto name = to_name(var.self);
statement("stage_output", name, " = ", name, ";");
}
}
}
if (execution.model == ExecutionModelVertex)
// Copy stage outputs.
if (require_output)
{
if (options.shader_model <= 30)
{
statement("output.gl_Position.x = output.gl_Position.x - gl_HalfPixel.x * output.gl_Position.w;");
statement("output.gl_Position.y = output.gl_Position.y + gl_HalfPixel.y * output.gl_Position.w;");
}
if (options.flip_vert_y)
{
statement("output.gl_Position.y = -output.gl_Position.y;");
}
if (options.fixup_clipspace)
{
statement("output.gl_Position.z = (output.gl_Position.z + output.gl_Position.w) * 0.5;");
}
}
statement("SPIRV_Cross_Output stage_output;");
statement("return output;");
// Copy builtins from globals to return struct.
for (uint32_t i = 0; i < 64; i++)
{
if (!(active_output_builtins & (1ull << i)))
continue;
auto builtin = builtin_to_glsl(static_cast<BuiltIn>(i));
statement("stage_output.", builtin, " = ", builtin, ";");
}
for (auto &id : ids)
{
if (id.get_type() == TypeVariable)
{
auto &var = id.get<SPIRVariable>();
auto &type = get<SPIRType>(var.basetype);
bool block = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) != 0;
if (var.storage != StorageClassOutput)
continue;
if (!block && 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);
statement("stage_output.", name, " = ", name, ";");
}
}
}
if (execution.model == ExecutionModelVertex)
{
// Do various mangling on the gl_Position.
if (options.shader_model <= 30)
{
statement("stage_output.gl_Position.x = stage_output.gl_Position.x - gl_HalfPixel.x * "
"stage_output.gl_Position.w;");
statement("stage_output.gl_Position.y = stage_output.gl_Position.y + gl_HalfPixel.y * "
"stage_output.gl_Position.w;");
}
if (options.flip_vert_y)
{
statement("stage_output.gl_Position.y = -stage_output.gl_Position.y;");
}
if (options.fixup_clipspace)
{
statement(
"stage_output.gl_Position.z = (stage_output.gl_Position.z + stage_output.gl_Position.w) * 0.5;");
}
}
statement("return stage_output;");
}
end_scope();
}
@ -993,6 +1298,7 @@ string CompilerHLSL::compile()
// Do not deal with ES-isms like precision, older extensions and such.
CompilerGLSL::options.es = false;
CompilerGLSL::options.version = 450;
CompilerGLSL::options.vulkan_semantics = true;
backend.float_literal_suffix = true;
backend.double_literal_suffix = false;
backend.long_long_literal_suffix = true;
@ -1002,10 +1308,12 @@ string CompilerHLSL::compile()
backend.swizzle_is_function = false;
backend.shared_is_implied = true;
backend.flexible_member_array_supported = false;
backend.explicit_struct_type = true;
backend.explicit_struct_type = false;
backend.use_initializer_list = true;
backend.use_constructor_splatting = false;
update_active_builtins();
uint32_t pass_count = 0;
do
{

View File

@ -57,7 +57,9 @@ private:
void emit_header() override;
void emit_resources();
void emit_interface_block_globally(const SPIRVariable &type);
void emit_interface_block_in_struct(const SPIRVariable &type, uint32_t &binding_number, bool builtins);
void emit_interface_block_in_struct(const SPIRVariable &type, std::unordered_set<uint32_t> &active_locations);
void emit_builtin_inputs_in_struct();
void emit_builtin_outputs_in_struct();
void emit_texture_op(const Instruction &i) override;
void emit_instruction(const Instruction &instruction) override;
void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
@ -65,11 +67,21 @@ private:
void emit_buffer_block(const SPIRVariable &type) override;
void emit_push_constant_block(const SPIRVariable &var) override;
void emit_uniform(const SPIRVariable &var) override;
std::string layout_for_member(const SPIRType &type, uint32_t index) override;
std::string to_interpolation_qualifiers(uint64_t flags) override;
const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override;
Options options;
bool requires_op_fmod = false;
void emit_builtin_variables();
bool require_output = false;
bool require_input = false;
uint32_t type_to_consumed_locations(const SPIRType &type) const;
void emit_io_block(const SPIRVariable &var);
};
}

View File

@ -68,6 +68,8 @@ string CompilerMSL::compile()
non_stage_in_input_var_ids.clear();
struct_member_padding.clear();
update_active_builtins();
// Preprocess OpCodes to extract the need to output additional header content
set_enabled_interface_variables(get_active_interface_variables());
preprocess_op_codes();
@ -1792,7 +1794,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
// If we're currently in the entry point function, and the object
// has a qualified name, use it, otherwise use the standard name.
string CompilerMSL::to_name(uint32_t id, bool allow_alias)
string CompilerMSL::to_name(uint32_t id, bool allow_alias) const
{
if (current_function && (current_function->self == entry_point))
{

View File

@ -136,7 +136,7 @@ protected:
std::string constant_expression(const SPIRConstant &c) override;
size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const override;
std::string to_func_call_arg(uint32_t id) override;
std::string to_name(uint32_t id, bool allow_alias = true) override;
std::string to_name(uint32_t id, bool allow_alias = true) const override;
std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
bool has_array_offsets, bool has_offset, bool has_grad, bool has_lod,
bool has_dref) override;

View File

@ -107,7 +107,7 @@ 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'])
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl', '--shader-model', '50'])
subprocess.check_call(['spirv-val', spirv_path])
validate_shader_hlsl(hlsl_path)