Merge pull request #433 from KhronosGroup/fix-428

Support returning arrays from functions in GLSL/MSL, refactor array copies in MSL.
This commit is contained in:
Hans-Kristian Arntzen 2018-02-10 10:52:14 +01:00 коммит произвёл GitHub
Родитель ed6b7755cd c9db3e5521
Коммит f4bce688d5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
19 изменённых файлов: 552 добавлений и 25 удалений

Просмотреть файл

@ -0,0 +1,31 @@
static const float4 _20[2] = { 10.0f.xxxx, 20.0f.xxxx };
static float4 gl_Position;
static float4 vInput0;
static float4 vInput1;
struct SPIRV_Cross_Input
{
float4 vInput0 : TEXCOORD0;
float4 vInput1 : TEXCOORD1;
};
struct SPIRV_Cross_Output
{
float4 gl_Position : SV_Position;
};
void vert_main()
{
gl_Position = 10.0f.xxxx + vInput1;
}
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
vInput0 = stage_input.vInput0;
vInput1 = stage_input.vInput1;
vert_main();
SPIRV_Cross_Output stage_output;
stage_output.gl_Position = gl_Position;
return stage_output;
}

Просмотреть файл

@ -1,3 +1,5 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib> #include <metal_stdlib>
#include <simd/simd.h> #include <simd/simd.h>
@ -9,6 +11,12 @@ struct Foobar
float b; float b;
}; };
constant float4 _37[3] = {float4(1.0), float4(2.0), float4(3.0)};
constant float4 _49[2] = {float4(1.0), float4(2.0)};
constant float4 _54[2] = {float4(8.0), float4(10.0)};
constant float4 _55[2][2] = {{float4(1.0), float4(2.0)}, {float4(8.0), float4(10.0)}};
constant Foobar _75[2] = {{10.0, 40.0}, {90.0, 70.0}};
struct main0_in struct main0_in
{ {
int index [[user(locn0)]]; int index [[user(locn0)]];
@ -19,6 +27,20 @@ struct main0_out
float4 FragColor [[color(0)]]; float4 FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
// An overload for constant arrays.
template<typename T, uint N>
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
fragment main0_out main0(main0_in in [[stage_in]]) fragment main0_out main0(main0_in in [[stage_in]])
{ {
main0_out out = {}; main0_out out = {};

Просмотреть файл

@ -1,3 +1,5 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib> #include <metal_stdlib>
#include <simd/simd.h> #include <simd/simd.h>
@ -9,6 +11,9 @@ struct Foo
float b; float b;
}; };
constant float _16[4] = {1.0, 4.0, 3.0, 2.0};
constant Foo _28[2] = {{10.0, 20.0}, {30.0, 40.0}};
struct main0_in struct main0_in
{ {
int line [[user(locn0)]]; int line [[user(locn0)]];
@ -19,6 +24,20 @@ struct main0_out
float4 FragColor [[color(0)]]; float4 FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
// An overload for constant arrays.
template<typename T, uint N>
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
fragment main0_out main0(main0_in in [[stage_in]]) fragment main0_out main0(main0_in in [[stage_in]])
{ {
main0_out out = {}; main0_out out = {};

Просмотреть файл

@ -0,0 +1,24 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
constant float4 _20[2] = {float4(10.0), float4(20.0)};
struct main0_in
{
float4 vInput1 [[attribute(1)]];
};
struct main0_out
{
float4 gl_Position [[position]];
};
vertex main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
out.gl_Position = float4(10.0) + in.vInput1;
return out;
}

Просмотреть файл

@ -0,0 +1,9 @@
#version 310 es
layout(location = 1) in vec4 vInput1;
void main()
{
gl_Position = vec4(10.0) + vInput1;
}

Просмотреть файл

@ -0,0 +1,48 @@
static const float4 _20[2] = { 10.0f.xxxx, 20.0f.xxxx };
static float4 gl_Position;
static float4 vInput0;
static float4 vInput1;
struct SPIRV_Cross_Input
{
float4 vInput0 : TEXCOORD0;
float4 vInput1 : TEXCOORD1;
};
struct SPIRV_Cross_Output
{
float4 gl_Position : SV_Position;
};
void test(out float4 SPIRV_Cross_return_value[2])
{
SPIRV_Cross_return_value = _20;
}
void test2(out float4 SPIRV_Cross_return_value[2])
{
float4 foobar[2];
foobar[0] = vInput0;
foobar[1] = vInput1;
SPIRV_Cross_return_value = foobar;
}
void vert_main()
{
float4 _42[2];
test(_42);
float4 _44[2];
test2(_44);
gl_Position = _42[0] + _44[1];
}
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
vInput0 = stage_input.vInput0;
vInput1 = stage_input.vInput1;
vert_main();
SPIRV_Cross_Output stage_output;
stage_output.gl_Position = gl_Position;
return stage_output;
}

Просмотреть файл

@ -1,3 +1,5 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib> #include <metal_stdlib>
#include <simd/simd.h> #include <simd/simd.h>
@ -9,11 +11,27 @@ struct D
float b; float b;
}; };
constant float4 _14[4] = {float4(0.0), float4(0.0), float4(0.0), float4(0.0)};
struct main0_out struct main0_out
{ {
float FragColor [[color(0)]]; float FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
// An overload for constant arrays.
template<typename T, uint N>
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
fragment main0_out main0() fragment main0_out main0()
{ {
main0_out out = {}; main0_out out = {};

Просмотреть файл

@ -11,6 +11,12 @@ struct Foobar
float b; float b;
}; };
constant float4 _37[3] = {float4(1.0), float4(2.0), float4(3.0)};
constant float4 _49[2] = {float4(1.0), float4(2.0)};
constant float4 _54[2] = {float4(8.0), float4(10.0)};
constant float4 _55[2][2] = {{float4(1.0), float4(2.0)}, {float4(8.0), float4(10.0)}};
constant Foobar _75[2] = {{10.0, 40.0}, {90.0, 70.0}};
struct main0_in struct main0_in
{ {
int index [[user(locn0)]]; int index [[user(locn0)]];
@ -21,6 +27,20 @@ struct main0_out
float4 FragColor [[color(0)]]; float4 FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
// An overload for constant arrays.
template<typename T, uint N>
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
float4 resolve(thread const Foobar& f) float4 resolve(thread const Foobar& f)
{ {
return float4(f.a + f.b); return float4(f.a + f.b);

Просмотреть файл

@ -1,3 +1,5 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib> #include <metal_stdlib>
#include <simd/simd.h> #include <simd/simd.h>
@ -9,6 +11,9 @@ struct Foo
float b; float b;
}; };
constant float _16[4] = {1.0, 4.0, 3.0, 2.0};
constant Foo _28[2] = {{10.0, 20.0}, {30.0, 40.0}};
struct main0_in struct main0_in
{ {
int line [[user(locn0)]]; int line [[user(locn0)]];
@ -19,6 +24,20 @@ struct main0_out
float4 FragColor [[color(0)]]; float4 FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
// An overload for constant arrays.
template<typename T, uint N>
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
fragment main0_out main0(main0_in in [[stage_in]]) fragment main0_out main0(main0_in in [[stage_in]])
{ {
main0_out out = {}; main0_out out = {};

Просмотреть файл

@ -0,0 +1,58 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
constant float4 _20[2] = {float4(10.0), float4(20.0)};
struct main0_in
{
float4 vInput1 [[attribute(1)]];
float4 vInput0 [[attribute(0)]];
};
struct main0_out
{
float4 gl_Position [[position]];
};
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
// An overload for constant arrays.
template<typename T, uint N>
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
void test(thread float4 (&SPIRV_Cross_return_value)[2])
{
spvArrayCopyConstant(SPIRV_Cross_return_value, _20);
}
void test2(thread float4 (&SPIRV_Cross_return_value)[2], thread float4& vInput0, thread float4& vInput1)
{
float4 foobar[2];
foobar[0] = vInput0;
foobar[1] = vInput1;
spvArrayCopy(SPIRV_Cross_return_value, foobar);
}
vertex main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
float4 _42[2];
test(_42);
float4 _44[2];
test2(_44, in.vInput0, in.vInput1);
out.gl_Position = _42[0] + _44[1];
return out;
}

Просмотреть файл

@ -0,0 +1,23 @@
#version 310 es
layout(location = 0) in vec4 vInput0;
layout(location = 1) in vec4 vInput1;
vec4[2] test()
{
return vec4[](vec4(10.0), vec4(20.0));
}
vec4[2] test2()
{
vec4 foobar[2];
foobar[0] = vInput0;
foobar[1] = vInput1;
return foobar;
}
void main()
{
gl_Position = test()[0] + test2()[1];
}

Просмотреть файл

@ -0,0 +1,22 @@
#version 310 es
layout(location = 0) in vec4 vInput0;
layout(location = 1) in vec4 vInput1;
vec4[2] test()
{
return vec4[](vec4(10.0), vec4(20.0));
}
vec4[2] test2()
{
vec4 foobar[2];
foobar[0] = vInput0;
foobar[1] = vInput1;
return foobar;
}
void main()
{
gl_Position = test()[0] + test2()[1];
}

Просмотреть файл

@ -0,0 +1,22 @@
#version 310 es
layout(location = 0) in vec4 vInput0;
layout(location = 1) in vec4 vInput1;
vec4[2] test()
{
return vec4[](vec4(10.0), vec4(20.0));
}
vec4[2] test2()
{
vec4 foobar[2];
foobar[0] = vInput0;
foobar[1] = vInput1;
return foobar;
}
void main()
{
gl_Position = test()[0] + test2()[1];
}

Просмотреть файл

@ -0,0 +1,22 @@
#version 310 es
layout(location = 0) in vec4 vInput0;
layout(location = 1) in vec4 vInput1;
vec4[2] test()
{
return vec4[](vec4(10.0), vec4(20.0));
}
vec4[2] test2()
{
vec4 foobar[2];
foobar[0] = vInput0;
foobar[1] = vInput1;
return foobar;
}
void main()
{
gl_Position = test()[0] + test2()[1];
}

Просмотреть файл

@ -5367,9 +5367,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
length -= 3; length -= 3;
auto &callee = get<SPIRFunction>(func); auto &callee = get<SPIRFunction>(func);
auto &return_type = get<SPIRType>(callee.return_type);
bool pure = function_is_pure(callee); bool pure = function_is_pure(callee);
bool callee_has_out_variables = false; bool callee_has_out_variables = false;
bool emit_return_value_as_argument = false;
// Invalidate out variables passed to functions since they can be OpStore'd to. // Invalidate out variables passed to functions since they can be OpStore'd to.
for (uint32_t i = 0; i < length; i++) for (uint32_t i = 0; i < length; i++)
@ -5383,12 +5385,25 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
flush_variable_declaration(arg[i]); flush_variable_declaration(arg[i]);
} }
if (!return_type.array.empty() && !backend.can_return_array)
{
callee_has_out_variables = true;
emit_return_value_as_argument = true;
}
if (!pure) if (!pure)
register_impure_function_call(); register_impure_function_call();
string funexpr; string funexpr;
vector<string> arglist; vector<string> arglist;
funexpr += to_name(func) + "("; funexpr += to_name(func) + "(";
if (emit_return_value_as_argument)
{
statement(type_to_glsl(return_type), " ", to_name(id), type_to_array_glsl(return_type), ";");
arglist.push_back(to_name(id));
}
for (uint32_t i = 0; i < length; i++) for (uint32_t i = 0; i < length; i++)
{ {
// Do not pass in separate images or samplers if we're remapping // Do not pass in separate images or samplers if we're remapping
@ -5423,7 +5438,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// Check for function call constraints. // Check for function call constraints.
check_function_call_constraints(arg, length); check_function_call_constraints(arg, length);
if (get<SPIRType>(result_type).basetype != SPIRType::Void) if (return_type.basetype != SPIRType::Void)
{ {
// If the function actually writes to an out variable, // If the function actually writes to an out variable,
// take the conservative route and do not forward. // take the conservative route and do not forward.
@ -5435,7 +5450,13 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
bool forward = args_will_forward(id, arg, length, pure) && !callee_has_out_variables && pure && bool forward = args_will_forward(id, arg, length, pure) && !callee_has_out_variables && pure &&
(forced_temporaries.find(id) == end(forced_temporaries)); (forced_temporaries.find(id) == end(forced_temporaries));
emit_op(result_type, id, funexpr, forward); if (emit_return_value_as_argument)
{
statement(funexpr, ";");
set<SPIRExpression>(id, to_name(id), result_type, true);
}
else
emit_op(result_type, id, funexpr, forward);
// Function calls are implicit loads from all variables in question. // Function calls are implicit loads from all variables in question.
// Set dependencies for them. // Set dependencies for them.
@ -7645,6 +7666,7 @@ void CompilerGLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
auto &type = get<SPIRType>(func.return_type); auto &type = get<SPIRType>(func.return_type);
decl += flags_to_precision_qualifiers_glsl(type, return_flags); decl += flags_to_precision_qualifiers_glsl(type, return_flags);
decl += type_to_glsl(type); decl += type_to_glsl(type);
decl += type_to_array_glsl(type);
decl += " "; decl += " ";
if (func.self == entry_point) if (func.self == entry_point)
@ -8453,9 +8475,26 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
if (block.return_value) if (block.return_value)
{ {
// OpReturnValue can return Undef, so don't emit anything for this case. auto &type = expression_type(block.return_value);
if (ids.at(block.return_value).get_type() != TypeUndef) if (!type.array.empty() && !backend.can_return_array)
statement("return ", to_expression(block.return_value), ";"); {
// If we cannot return arrays, we will have a special out argument we can write to instead.
// The backend is responsible for setting this up, and redirection the return values as appropriate.
if (ids.at(block.return_value).get_type() != TypeUndef)
emit_array_copy("SPIRV_Cross_return_value", block.return_value);
if (!block_is_outside_flow_control_from_block(get<SPIRBlock>(current_function->entry_block), block) ||
block.loop_dominator != SPIRBlock::NoDominator)
{
statement("return;");
}
}
else
{
// OpReturnValue can return Undef, so don't emit anything for this case.
if (ids.at(block.return_value).get_type() != TypeUndef)
statement("return ", to_expression(block.return_value), ";");
}
} }
// If this block is the very final block and not called from control flow, // If this block is the very final block and not called from control flow,
// we do not need an explicit return which looks out of place. Just end the function here. // we do not need an explicit return which looks out of place. Just end the function here.
@ -8463,7 +8502,9 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
// but we actually need a return here ... // but we actually need a return here ...
else if (!block_is_outside_flow_control_from_block(get<SPIRBlock>(current_function->entry_block), block) || else if (!block_is_outside_flow_control_from_block(get<SPIRBlock>(current_function->entry_block), block) ||
block.loop_dominator != SPIRBlock::NoDominator) block.loop_dominator != SPIRBlock::NoDominator)
{
statement("return;"); statement("return;");
}
break; break;
case SPIRBlock::Kill: case SPIRBlock::Kill:
@ -8581,3 +8622,8 @@ uint32_t CompilerGLSL::mask_relevant_memory_semantics(uint32_t semantics)
MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask | MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask |
MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask); MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask);
} }
void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
{
statement(lhs, " = ", to_expression(rhs_id), ";");
}

Просмотреть файл

@ -331,6 +331,7 @@ protected:
bool allow_precision_qualifiers = false; bool allow_precision_qualifiers = false;
bool can_swizzle_scalar = false; bool can_swizzle_scalar = false;
bool force_gl_in_out_block = false; bool force_gl_in_out_block = false;
bool can_return_array = true;
} backend; } backend;
void emit_struct(SPIRType &type); void emit_struct(SPIRType &type);
@ -424,6 +425,7 @@ protected:
std::string layout_for_variable(const SPIRVariable &variable); std::string layout_for_variable(const SPIRVariable &variable);
std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id); std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id);
virtual bool skip_argument(uint32_t id) const; virtual bool skip_argument(uint32_t id) const;
virtual void emit_array_copy(const std::string &lhs, uint32_t rhs_id);
bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t start_offset = 0, bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t start_offset = 0,
uint32_t end_offset = std::numeric_limits<uint32_t>::max()); uint32_t end_offset = std::numeric_limits<uint32_t>::max());

Просмотреть файл

@ -1663,9 +1663,17 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
string decl; string decl;
auto &type = get<SPIRType>(func.return_type); auto &type = get<SPIRType>(func.return_type);
decl += flags_to_precision_qualifiers_glsl(type, return_flags); if (type.array.empty())
decl += type_to_glsl(type); {
decl += " "; decl += flags_to_precision_qualifiers_glsl(type, return_flags);
decl += type_to_glsl(type);
decl += " ";
}
else
{
// We cannot return arrays in HLSL, so "return" through an out variable.
decl = "void ";
}
if (func.self == entry_point) if (func.self == entry_point)
{ {
@ -1683,6 +1691,19 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
decl += to_name(func.self); decl += to_name(func.self);
decl += "("; decl += "(";
if (!type.array.empty())
{
// Fake array returns by writing to an out array instead.
decl += "out ";
decl += type_to_glsl(type);
decl += " ";
decl += "SPIRV_Cross_return_value";
decl += type_to_array_glsl(type);
if (!func.arguments.empty())
decl += ", ";
}
for (auto &arg : func.arguments) for (auto &arg : func.arguments)
{ {
// Might change the variable name if it already exists in this function. // Might change the variable name if it already exists in this function.
@ -3802,6 +3823,7 @@ string CompilerHLSL::compile()
backend.can_swizzle_scalar = true; backend.can_swizzle_scalar = true;
backend.can_declare_struct_inline = false; backend.can_declare_struct_inline = false;
backend.can_declare_arrays_inline = false; backend.can_declare_arrays_inline = false;
backend.can_return_array = false;
update_active_builtins(); update_active_builtins();
analyze_sampler_comparison_states(); analyze_sampler_comparison_states();

Просмотреть файл

@ -72,6 +72,8 @@ string CompilerMSL::compile()
backend.use_typed_initializer_list = true; backend.use_typed_initializer_list = true;
backend.native_row_major_matrix = false; backend.native_row_major_matrix = false;
backend.flexible_member_array_supported = false; backend.flexible_member_array_supported = false;
backend.can_declare_arrays_inline = false;
backend.can_return_array = false;
replace_illegal_names(); replace_illegal_names();
@ -912,11 +914,19 @@ void CompilerMSL::emit_custom_functions()
case SPVFuncImplArrayCopy: case SPVFuncImplArrayCopy:
statement("// Implementation of an array copy function to cover GLSL's ability to copy an array via " statement("// Implementation of an array copy function to cover GLSL's ability to copy an array via "
"assignment. "); "assignment.");
statement("template<typename T>"); statement("template<typename T, uint N>");
statement("void spvArrayCopy(thread T* dst, thread const T* src, uint count)"); statement("void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])");
begin_scope(); begin_scope();
statement("for (uint i = 0; i < count; *dst++ = *src++, i++);"); statement("for (uint i = 0; i < N; dst[i] = src[i], i++);");
end_scope();
statement("");
statement("// An overload for constant arrays.");
statement("template<typename T, uint N>");
statement("void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])");
begin_scope();
statement("for (uint i = 0; i < N; dst[i] = src[i], i++);");
end_scope(); end_scope();
statement(""); statement("");
break; break;
@ -1134,6 +1144,34 @@ void CompilerMSL::declare_undefined_values()
statement(""); statement("");
} }
void CompilerMSL::declare_constant_arrays()
{
// MSL cannot declare arrays inline (except when declaring a variable), so we must move them out to
// global constants directly, so we are able to use constants as variable expressions.
bool emitted = false;
for (auto &id : ids)
{
if (id.get_type() == TypeConstant)
{
auto &c = id.get<SPIRConstant>();
if (c.specialization)
continue;
auto &type = get<SPIRType>(c.constant_type);
if (!type.array.empty())
{
auto name = to_name(c.self);
statement("constant ", variable_decl(type, name), " = ", constant_expression(c), ";");
emitted = true;
}
}
}
if (emitted)
statement("");
}
void CompilerMSL::emit_resources() void CompilerMSL::emit_resources()
{ {
// Output non-interface structs. These include local function structs // Output non-interface structs. These include local function structs
@ -1170,6 +1208,7 @@ void CompilerMSL::emit_resources()
} }
} }
declare_constant_arrays();
declare_undefined_values(); declare_undefined_values();
// Output interface structs. // Output interface structs.
@ -1743,27 +1782,44 @@ bool CompilerMSL::maybe_emit_input_struct_assignment(uint32_t id_lhs, uint32_t i
return true; return true;
} }
void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
{
// Assignment from an array initializer is fine.
if (ids[rhs_id].get_type() == TypeConstant)
statement("spvArrayCopyConstant(", lhs, ", ", to_expression(rhs_id), ");");
else
statement("spvArrayCopy(", lhs, ", ", to_expression(rhs_id), ");");
}
// Since MSL does not allow arrays to be copied via simple variable assignment, // Since MSL does not allow arrays to be copied via simple variable assignment,
// if the LHS and RHS represent an assignment of an entire array, it must be // if the LHS and RHS represent an assignment of an entire array, it must be
// implemented by calling an array copy function. // implemented by calling an array copy function.
// Returns whether the struct assignment was emitted. // Returns whether the struct assignment was emitted.
bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs) bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
{ {
// Assignment from an array initializer is fine.
if (ids[id_rhs].get_type() == TypeConstant)
return false;
// We only care about assignments of an entire array // We only care about assignments of an entire array
auto &type = expression_type(id_rhs); auto &type = expression_type(id_rhs);
if (type.array.size() == 0) if (type.array.size() == 0)
return false; return false;
auto *var = maybe_get<SPIRVariable>(id_lhs);
if (ids[id_rhs].get_type() == TypeConstant && var && var->deferred_declaration)
{
// Special case, if we end up declaring a variable when assigning the constant array,
// we can avoid the copy by directly assigning the constant expression.
// This is likely necessary to be able to use a variable as a true look-up table, as it is unlikely
// the compiler will be able to optimize the spvArrayCopy() into a constant LUT.
// After a variable has been declared, we can no longer assign constant arrays in MSL unfortunately.
statement(to_expression(id_lhs), " = ", constant_expression(get<SPIRConstant>(id_rhs)), ";");
return true;
}
// Ensure the LHS variable has been declared // Ensure the LHS variable has been declared
auto *p_v_lhs = maybe_get_backing_variable(id_lhs); auto *p_v_lhs = maybe_get_backing_variable(id_lhs);
if (p_v_lhs) if (p_v_lhs)
flush_variable_declaration(p_v_lhs->self); flush_variable_declaration(p_v_lhs->self);
statement("spvArrayCopy(", to_expression(id_lhs), ", ", to_expression(id_rhs), ", ", to_array_size(type, 0), ");"); emit_array_copy(to_expression(id_lhs), id_rhs);
register_write(id_lhs); register_write(id_lhs);
return true; return true;
@ -1949,12 +2005,32 @@ void CompilerMSL::emit_function_prototype(SPIRFunction &func, uint64_t)
processing_entry_point = (func.self == entry_point); processing_entry_point = (func.self == entry_point);
auto &type = get<SPIRType>(func.return_type); auto &type = get<SPIRType>(func.return_type);
decl += func_type_decl(type);
if (type.array.empty())
{
decl += func_type_decl(type);
}
else
{
// We cannot return arrays in MSL, so "return" through an out variable.
decl = "void";
}
decl += " "; decl += " ";
decl += to_name(func.self); decl += to_name(func.self);
decl += "("; decl += "(";
if (!type.array.empty())
{
// Fake arrays returns by writing to an out array instead.
decl += "thread ";
decl += type_to_glsl(type);
decl += " (&SPIRV_Cross_return_value)";
decl += type_to_array_glsl(type);
if (!func.arguments.empty())
decl += ", ";
}
if (processing_entry_point) if (processing_entry_point)
{ {
decl += entry_point_args(!func.arguments.empty()); decl += entry_point_args(!func.arguments.empty());
@ -2595,7 +2671,7 @@ string CompilerMSL::func_type_decl(SPIRType &type)
{ {
auto &execution = get_entry_point(); auto &execution = get_entry_point();
// The regular function return type. If not processing the entry point function, that's all we need // The regular function return type. If not processing the entry point function, that's all we need
string return_type = type_to_glsl(type); string return_type = type_to_glsl(type) + type_to_array_glsl(type);
if (!processing_entry_point) if (!processing_entry_point)
return return_type; return return_type;
@ -2604,7 +2680,7 @@ string CompilerMSL::func_type_decl(SPIRType &type)
{ {
auto &so_var = get<SPIRVariable>(stage_out_var_id); auto &so_var = get<SPIRVariable>(stage_out_var_id);
auto &so_type = get<SPIRType>(so_var.basetype); auto &so_type = get<SPIRType>(so_var.basetype);
return_type = type_to_glsl(so_type); return_type = type_to_glsl(so_type) + type_to_array_glsl(type);
} }
// Prepend a entry type, based on the execution model // Prepend a entry type, based on the execution model
@ -3522,16 +3598,38 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o
case OpFMod: case OpFMod:
return SPVFuncImplMod; return SPVFuncImplMod;
case OpFunctionCall:
{
auto &return_type = compiler.get<SPIRType>(args[0]);
if (!return_type.array.empty())
return SPVFuncImplArrayCopy;
else
return SPVFuncImplNone;
}
case OpStore: case OpStore:
{ {
// Get the result type of the RHS. Since this is run as a pre-processing stage, // Get the result type of the RHS. Since this is run as a pre-processing stage,
// we must extract the result type directly from the Instruction, rather than the ID. // we must extract the result type directly from the Instruction, rather than the ID.
uint32_t id_rhs = args[1]; uint32_t id_rhs = args[1];
uint32_t type_id_rhs = result_types[id_rhs];
if ((compiler.ids[id_rhs].get_type() != TypeConstant) && type_id_rhs &&
compiler.is_array(compiler.get<SPIRType>(type_id_rhs)))
return SPVFuncImplArrayCopy;
const SPIRType *type = nullptr;
if (compiler.ids[id_rhs].get_type() != TypeNone)
{
// Could be a constant, or similar.
type = &compiler.expression_type(id_rhs);
}
else
{
// Or ... an expression.
if (result_types[id_rhs] != 0)
type = &compiler.get<SPIRType>(result_types[id_rhs]);
}
if (type && compiler.is_array(*type))
return SPVFuncImplArrayCopy;
else
return SPVFuncImplNone;
break; break;
} }

Просмотреть файл

@ -208,6 +208,7 @@ protected:
std::string to_qualifiers_glsl(uint32_t id) override; std::string to_qualifiers_glsl(uint32_t id) override;
void replace_illegal_names() override; void replace_illegal_names() override;
void declare_undefined_values() override; void declare_undefined_values() override;
void declare_constant_arrays();
bool is_non_native_row_major_matrix(uint32_t id) override; bool is_non_native_row_major_matrix(uint32_t id) override;
bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override; bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override;
std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type) override; std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type) override;
@ -265,6 +266,7 @@ protected:
const char *get_memory_order(uint32_t spv_mem_sem); const char *get_memory_order(uint32_t spv_mem_sem);
void add_pragma_line(const std::string &line); void add_pragma_line(const std::string &line);
void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem); void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
void emit_array_copy(const std::string &lhs, uint32_t rhs_id) override;
Options options; Options options;
std::set<SPVFuncImpl> spv_function_implementations; std::set<SPVFuncImpl> spv_function_implementations;