Return arrays in HLSL/MSL by writing to an output variable instead.
This commit is contained in:
parent
9fa91f7e1c
commit
00ccd590ee
31
reference/opt/shaders-hlsl/vert/return-array.vert
Normal file
31
reference/opt/shaders-hlsl/vert/return-array.vert
Normal file
@ -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;
|
||||
}
|
@ -3,15 +3,20 @@
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float4 vInput1 [[attribute(1)]];
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
vertex main0_out main0()
|
||||
vertex main0_out main0(main0_in in [[stage_in]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.gl_Position = float4(10.0);
|
||||
out.gl_Position = float4(10.0) + in.vInput1;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
#version 310 es
|
||||
|
||||
layout(location = 1) in vec4 vInput1;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(10.0);
|
||||
gl_Position = vec4(10.0) + vInput1;
|
||||
}
|
||||
|
||||
|
48
reference/shaders-hlsl/vert/return-array.vert
Normal file
48
reference/shaders-hlsl/vert/return-array.vert
Normal file
@ -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;
|
||||
}
|
@ -5,20 +5,45 @@
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float4 vInput1 [[attribute(1)]];
|
||||
float4 vInput0 [[attribute(0)]];
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
float4[2] test()
|
||||
// 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])
|
||||
{
|
||||
return {float4(10.0), float4(20.0)};
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
vertex main0_out main0()
|
||||
void (float4 (&SPIRV_Cross_return_value)[2])
|
||||
{
|
||||
SPIRV_Cross_return_value = {float4(10.0), float4(20.0)};
|
||||
}
|
||||
|
||||
void (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 = {};
|
||||
out.gl_Position = test()[0];
|
||||
float4 _42[2];
|
||||
test(_42);
|
||||
float4 _44[2];
|
||||
test2(_44, in.vInput0, in.vInput1);
|
||||
out.gl_Position = _42[0] + _44[1];
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -1,12 +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));
|
||||
}
|
||||
|
||||
void main()
|
||||
vec4[2] test2()
|
||||
{
|
||||
gl_Position = test()[0];
|
||||
vec4 foobar[2];
|
||||
foobar[0] = vInput0;
|
||||
foobar[1] = vInput1;
|
||||
return foobar;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = test()[0] + test2()[1];
|
||||
}
|
||||
|
||||
|
22
shaders-hlsl/vert/return-array.vert
Normal file
22
shaders-hlsl/vert/return-array.vert
Normal file
@ -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];
|
||||
}
|
@ -1,11 +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];
|
||||
gl_Position = test()[0] + test2()[1];
|
||||
}
|
||||
|
@ -1,11 +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];
|
||||
gl_Position = test()[0] + test2()[1];
|
||||
}
|
||||
|
@ -5367,9 +5367,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
||||
length -= 3;
|
||||
|
||||
auto &callee = get<SPIRFunction>(func);
|
||||
auto &return_type = get<SPIRType>(callee.return_type);
|
||||
bool pure = function_is_pure(callee);
|
||||
|
||||
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.
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
@ -5383,12 +5385,25 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
||||
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)
|
||||
register_impure_function_call();
|
||||
|
||||
string funexpr;
|
||||
vector<string> arglist;
|
||||
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++)
|
||||
{
|
||||
// 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_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,
|
||||
// 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 &&
|
||||
(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.
|
||||
// Set dependencies for them.
|
||||
@ -8454,9 +8475,26 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
||||
|
||||
if (block.return_value)
|
||||
{
|
||||
// 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), ";");
|
||||
auto &type = expression_type(block.return_value);
|
||||
if (!type.array.empty() && !backend.can_return_array)
|
||||
{
|
||||
// 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,
|
||||
// we do not need an explicit return which looks out of place. Just end the function here.
|
||||
@ -8464,7 +8502,9 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
||||
// but we actually need a return here ...
|
||||
else if (!block_is_outside_flow_control_from_block(get<SPIRBlock>(current_function->entry_block), block) ||
|
||||
block.loop_dominator != SPIRBlock::NoDominator)
|
||||
{
|
||||
statement("return;");
|
||||
}
|
||||
break;
|
||||
|
||||
case SPIRBlock::Kill:
|
||||
@ -8582,3 +8622,8 @@ uint32_t CompilerGLSL::mask_relevant_memory_semantics(uint32_t semantics)
|
||||
MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask |
|
||||
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 can_swizzle_scalar = false;
|
||||
bool force_gl_in_out_block = false;
|
||||
bool can_return_array = true;
|
||||
} backend;
|
||||
|
||||
void emit_struct(SPIRType &type);
|
||||
@ -424,6 +425,7 @@ protected:
|
||||
std::string layout_for_variable(const SPIRVariable &variable);
|
||||
std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id);
|
||||
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,
|
||||
uint32_t end_offset = std::numeric_limits<uint32_t>::max());
|
||||
|
@ -1663,11 +1663,17 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
|
||||
string decl;
|
||||
|
||||
auto &type = get<SPIRType>(func.return_type);
|
||||
decl += flags_to_precision_qualifiers_glsl(type, return_flags);
|
||||
decl += type_to_glsl(type);
|
||||
if (!type.array.empty())
|
||||
SPIRV_CROSS_THROW("Returning arrays from functions not possible in HLSL.");
|
||||
decl += " ";
|
||||
if (type.array.empty())
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -1685,6 +1691,19 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
|
||||
decl += to_name(func.self);
|
||||
|
||||
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)
|
||||
{
|
||||
// Might change the variable name if it already exists in this function.
|
||||
@ -3804,6 +3823,7 @@ string CompilerHLSL::compile()
|
||||
backend.can_swizzle_scalar = true;
|
||||
backend.can_declare_struct_inline = false;
|
||||
backend.can_declare_arrays_inline = false;
|
||||
backend.can_return_array = false;
|
||||
|
||||
update_active_builtins();
|
||||
analyze_sampler_comparison_states();
|
||||
|
@ -72,6 +72,7 @@ string CompilerMSL::compile()
|
||||
backend.use_typed_initializer_list = true;
|
||||
backend.native_row_major_matrix = false;
|
||||
backend.flexible_member_array_supported = false;
|
||||
backend.can_return_array = false;
|
||||
|
||||
replace_illegal_names();
|
||||
|
||||
@ -913,10 +914,10 @@ void CompilerMSL::emit_custom_functions()
|
||||
case SPVFuncImplArrayCopy:
|
||||
statement("// Implementation of an array copy function to cover GLSL's ability to copy an array via "
|
||||
"assignment. ");
|
||||
statement("template<typename T>");
|
||||
statement("void spvArrayCopy(thread T* dst, thread const T* src, uint count)");
|
||||
statement("template<typename T, uint N>");
|
||||
statement("void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])");
|
||||
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("");
|
||||
break;
|
||||
@ -1743,6 +1744,15 @@ bool CompilerMSL::maybe_emit_input_struct_assignment(uint32_t id_lhs, uint32_t i
|
||||
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("spvArrayCopy(", lhs, ", ", to_expression(rhs_id), ");");
|
||||
else
|
||||
statement(lhs, " = ", to_expression(rhs_id), ";");
|
||||
}
|
||||
|
||||
// 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
|
||||
// implemented by calling an array copy function.
|
||||
@ -1763,7 +1773,7 @@ bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
|
||||
if (p_v_lhs)
|
||||
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);
|
||||
|
||||
return true;
|
||||
@ -1949,12 +1959,31 @@ void CompilerMSL::emit_function_prototype(SPIRFunction &func, uint64_t)
|
||||
processing_entry_point = (func.self == entry_point);
|
||||
|
||||
auto &type = get<SPIRType>(func.return_type);
|
||||
decl += func_type_decl(type);
|
||||
decl += " ";
|
||||
decl += to_name(func.self);
|
||||
|
||||
if (type.array.empty())
|
||||
{
|
||||
decl += func_type_decl(type);
|
||||
decl += " ";
|
||||
decl += to_name(func.self);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We cannot return arrays in MSL, so "return" through an out variable.
|
||||
decl = "void ";
|
||||
}
|
||||
|
||||
decl += "(";
|
||||
|
||||
if (!type.array.empty())
|
||||
{
|
||||
// Fake arrays returns by writing to an out array instead.
|
||||
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)
|
||||
{
|
||||
decl += entry_point_args(!func.arguments.empty());
|
||||
@ -3522,6 +3551,15 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o
|
||||
case OpFMod:
|
||||
return SPVFuncImplMod;
|
||||
|
||||
case OpFunctionCall:
|
||||
{
|
||||
auto &return_type = compiler.get<SPIRType>(args[0]);
|
||||
if (!return_type.array.empty())
|
||||
return SPVFuncImplArrayCopy;
|
||||
else
|
||||
return SPVFuncImplNone;
|
||||
}
|
||||
|
||||
case OpStore:
|
||||
{
|
||||
// Get the result type of the RHS. Since this is run as a pre-processing stage,
|
||||
|
@ -265,6 +265,7 @@ protected:
|
||||
const char *get_memory_order(uint32_t spv_mem_sem);
|
||||
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_array_copy(const std::string &lhs, uint32_t rhs_id) override;
|
||||
|
||||
Options options;
|
||||
std::set<SPVFuncImpl> spv_function_implementations;
|
||||
|
Loading…
Reference in New Issue
Block a user