Improve Metal support for out parameters.

We now insert helper functions which defer the assignment of out-
parameters back into their original variables to the end of the
function call. This allows us to match the semantics listed the GLSL
spec in section 6.1.1:

"All arguments are evaluated at call time, exactly once, in order, from
left to right. [...] Evaluation of an out parameter results in an
l-value that is used to copy out a value when the function returns.
Evaluation of an inout parameter results in both a value and an l-value;
the value is copied to the formal parameter at call time and the lvalue
is used to copy out a value when the function returns."

This technique also allows us to support swizzled out-parameters in
Metal, by reading the swizzle into a temp variable, calling the original
function, and then re-assigning the result back into the original
swizzle expression.

At present, we don't deduplicate these helper functions, so in theory
there could be a fair amount of redundant code generated if a function
with out parameters is called many times in a row. The cost of properly
deduplicating them is probably larger than the benefit in the 99% case.

Change-Id: Iefc922ac9e2b24ef2ff1e9dacb17a735a75ec8ea
Bug: skia:10855, skia:11052
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/341162
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2020-12-09 12:35:48 -05:00 committed by Skia Commit-Bot
parent 5ef2267a88
commit 06b84efcb3
9 changed files with 563 additions and 121 deletions

View File

@ -188,6 +188,7 @@ sksl_metal_tests = [
"$_tests/sksl/metal/OutParams.sksl",
"$_tests/sksl/metal/OutVarsRequireLocation.sksl",
"$_tests/sksl/metal/SamplerGlobals.sksl",
"$_tests/sksl/metal/SwizzleHelper.sksl",
]
sksl_spirv_tests = [

View File

@ -165,8 +165,6 @@ void MetalCodeGenerator::writeArrayDimensions(const Type& type) {
this->write(to_string(type.columns()));
}
this->write("]");
this->writeArrayDimensions(type.componentType());
}
}
@ -264,10 +262,123 @@ void MetalCodeGenerator::writeIntrinsicCall(const FunctionCall& c) {
}
}
String MetalCodeGenerator::getOutParamHelper(const FunctionDeclaration& function,
const ExpressionArray& arguments) {
// TODO: actually synthesize helper method.
return String::printf("/*needs swizzle fix*/ %s", String(function.name()).c_str());
String MetalCodeGenerator::getOutParamHelper(const FunctionCall& call,
const ExpressionArray& arguments,
const SkTArray<VariableReference*>& outVars) {
AutoOutputStream outputToExtraFunctions(this, &fExtraFunctions, &fIndentation);
const FunctionDeclaration& function = call.function();
String name = "_skOutParamHelper" + to_string(fSwizzleHelperCount++) + "_" + function.name();
const char* separator = "";
// Emit a prototype for the function we'll be calling through to in our helper.
if (!function.isBuiltin()) {
this->writeFunctionDeclaration(function);
this->writeLine(";");
}
// Synthesize a helper function that takes the same inputs as `function`, except in places where
// `outVars` is non-null; in those places, we take the type of the VariableReference.
//
// float _skOutParamHelper0_originalFuncName(float _var0, float _var1, float& outParam) {
this->writeBaseType(call.type());
this->write(" ");
this->write(name);
this->write("(");
this->writeFunctionRequirementParams(function, separator);
SkASSERT(outVars.size() == arguments.size());
SkASSERT(outVars.size() == function.parameters().size());
for (int index = 0; index < arguments.count(); ++index) {
this->write(separator);
separator = ", ";
const Variable* param = function.parameters()[index];
this->writeModifiers(param->modifiers(), /*globalContext=*/false);
const Type* type = outVars[index] ? &outVars[index]->type() : &arguments[index]->type();
this->writeBaseType(*type);
if (param->modifiers().fFlags & Modifiers::kOut_Flag) {
this->write("&");
}
if (outVars[index]) {
this->write(" ");
fIgnoreVariableReferenceModifiers = true;
this->writeVariableReference(*outVars[index]);
fIgnoreVariableReferenceModifiers = false;
} else {
this->write(" _var");
this->write(to_string(index));
}
this->writeArrayDimensions(*type);
}
this->writeLine(") {");
++fIndentation;
for (int index = 0; index < outVars.count(); ++index) {
if (!outVars[index]) {
continue;
}
// float3 _var2[ = outParam.zyx];
this->writeBaseType(arguments[index]->type());
this->write(" _var");
this->write(to_string(index));
const Variable* param = function.parameters()[index];
if (param->modifiers().fFlags & Modifiers::kIn_Flag) {
this->write(" = ");
fIgnoreVariableReferenceModifiers = true;
this->writeExpression(*arguments[index], kAssignment_Precedence);
fIgnoreVariableReferenceModifiers = false;
}
this->writeLine(";");
}
// [int _skResult = ] myFunction(inputs, outputs, globals, _var0, _var1, _var2, _var3);
bool hasResult = (call.type().name() != "void");
if (hasResult) {
this->writeBaseType(call.type());
this->write(" _skResult = ");
}
this->writeName(function.name());
this->write("(");
separator = "";
this->writeFunctionRequirementArgs(function, separator);
for (int index = 0; index < arguments.count(); ++index) {
this->write(separator);
separator = ", ";
this->write("_var");
this->write(to_string(index));
}
this->writeLine(");");
for (int index = 0; index < outVars.count(); ++index) {
if (!outVars[index]) {
continue;
}
// outParam.zyx = _var2;
fIgnoreVariableReferenceModifiers = true;
this->writeExpression(*arguments[index], kAssignment_Precedence);
fIgnoreVariableReferenceModifiers = false;
this->write(" = _var");
this->write(to_string(index));
this->writeLine(";");
}
if (hasResult) {
this->writeLine("return _skResult;");
}
--fIndentation;
this->writeLine("}");
return name;
}
void MetalCodeGenerator::writeFunctionCall(const FunctionCall& c) {
@ -298,58 +409,50 @@ void MetalCodeGenerator::writeFunctionCall(const FunctionCall& c) {
name = "dfdy";
}
// GLSL supports passing swizzled variables to out params; Metal doesn't. To emulate that
// support, we synthesize a helper function which performs the swizzle into a temporary
// variable, calls the original function, then writes the temp var back into the out param.
// GLSL supports passing swizzled variables to out params; Metal doesn't. Walk the list of
// parameters and see if any are out parameters; if so, check if the passed-in expression is a
// swizzle. Take a note of all the swizzled variables that we find.
const std::vector<const Variable*>& parameters = function.parameters();
SkASSERT(arguments.size() == parameters.size());
for (size_t index = 0; index < arguments.size(); ++index) {
bool foundOutParam = false;
SkSTArray<16, VariableReference*> outVars;
outVars.push_back_n(arguments.size(), (VariableReference*)nullptr);
for (int index = 0; index < arguments.count(); ++index) {
// If this is an out parameter...
if (parameters[index]->modifiers().fFlags & Modifiers::kOut_Flag) {
// Inspect the expression to see if it contains a swizzle.
// Find the expression's inner variable being written to.
Analysis::AssignmentInfo info;
bool outParamIsAssignable = Analysis::IsAssignable(*arguments[index], &info, nullptr);
SkASSERT(outParamIsAssignable); // assignability was verified at IRGeneration time
if (outParamIsAssignable && info.fIsSwizzled) {
// Found a swizzle; we need to use a helper function here.
name = this->getOutParamHelper(function, arguments);
break;
}
// Assignability was verified at IRGeneration time, so this should always succeed.
SkAssertResult(Analysis::IsAssignable(*arguments[index], &info));
outVars[index] = info.fAssignedVar;
foundOutParam = true;
}
}
if (foundOutParam) {
// Out parameters need to be written back to at the end of the function. To do this, we
// synthesize a helper function which evaluates the out-param expression into a temporary
// variable, calls the original function, then writes the temp var back into the out param
// using the original out-param expression. (This lets us support things like swizzles and
// array indices.)
name = getOutParamHelper(c, arguments, outVars);
}
this->write(name);
this->write("(");
const char* separator = "";
if (this->requirements(function) & kInputs_Requirement) {
this->write("_in");
separator = ", ";
}
if (this->requirements(function) & kOutputs_Requirement) {
this->write(separator);
this->write("_out");
separator = ", ";
}
if (this->requirements(function) & kUniforms_Requirement) {
this->write(separator);
this->write("_uniforms");
separator = ", ";
}
if (this->requirements(function) & kGlobals_Requirement) {
this->write(separator);
this->write("_globals");
separator = ", ";
}
if (this->requirements(function) & kFragCoord_Requirement) {
this->write(separator);
this->write("_fragCoord");
separator = ", ";
}
for (size_t i = 0; i < arguments.size(); ++i) {
const Expression& arg = *arguments[i];
this->writeFunctionRequirementArgs(function, separator);
for (int i = 0; i < arguments.count(); ++i) {
this->write(separator);
separator = ", ";
this->writeExpression(arg, kSequence_Precedence);
if (outVars[i]) {
this->writeExpression(*outVars[i], kSequence_Precedence);
} else {
this->writeExpression(*arguments[i], kSequence_Precedence);
}
}
this->write(")");
}
@ -807,6 +910,14 @@ void MetalCodeGenerator::writeFragCoord() {
}
void MetalCodeGenerator::writeVariableReference(const VariableReference& ref) {
// When assembling out-param helper functions, we copy variables into local clones with matching
// names. We never want to prepend "_in." or "_globals->" when writing these variables since
// we're actually targeting the clones.
if (fIgnoreVariableReferenceModifiers) {
this->writeName(ref.variable()->name());
return;
}
switch (ref.variable()->modifiers().fLayout.fBuiltin) {
case SK_FRAGCOLOR_BUILTIN:
this->write("_out->sk_FragColor");
@ -1047,6 +1158,66 @@ void MetalCodeGenerator::writeSetting(const Setting& s) {
ABORT("internal error; setting was not folded to a constant during compilation\n");
}
void MetalCodeGenerator::writeFunctionRequirementArgs(const FunctionDeclaration& f,
const char*& separator) {
Requirements requirements = this->requirements(f);
if (requirements & kInputs_Requirement) {
this->write(separator);
this->write("_in");
separator = ", ";
}
if (requirements & kOutputs_Requirement) {
this->write(separator);
this->write("_out");
separator = ", ";
}
if (requirements & kUniforms_Requirement) {
this->write(separator);
this->write("_uniforms");
separator = ", ";
}
if (requirements & kGlobals_Requirement) {
this->write(separator);
this->write("_globals");
separator = ", ";
}
if (requirements & kFragCoord_Requirement) {
this->write(separator);
this->write("_fragCoord");
separator = ", ";
}
}
void MetalCodeGenerator::writeFunctionRequirementParams(const FunctionDeclaration& f,
const char*& separator) {
Requirements requirements = this->requirements(f);
if (requirements & kInputs_Requirement) {
this->write(separator);
this->write("Inputs _in");
separator = ", ";
}
if (requirements & kOutputs_Requirement) {
this->write(separator);
this->write("thread Outputs* _out");
separator = ", ";
}
if (requirements & kUniforms_Requirement) {
this->write(separator);
this->write("Uniforms _uniforms");
separator = ", ";
}
if (requirements & kGlobals_Requirement) {
this->write(separator);
this->write("thread Globals* _globals");
separator = ", ";
}
if (requirements & kFragCoord_Requirement) {
this->write(separator);
this->write("float4 _fragCoord");
separator = ", ";
}
}
bool MetalCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& f) {
fRTHeightName = fProgram.fInputs.fRTHeight ? "_globals->_anonInterface0->u_skRTHeight" : "";
const char* separator = "";
@ -1130,36 +1301,12 @@ bool MetalCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& f)
this->write(" ");
this->writeName(f.name());
this->write("(");
Requirements requirements = this->requirements(f);
if (requirements & kInputs_Requirement) {
this->write("Inputs _in");
separator = ", ";
}
if (requirements & kOutputs_Requirement) {
this->write(separator);
this->write("thread Outputs* _out");
separator = ", ";
}
if (requirements & kUniforms_Requirement) {
this->write(separator);
this->write("Uniforms _uniforms");
separator = ", ";
}
if (requirements & kGlobals_Requirement) {
this->write(separator);
this->write("thread Globals* _globals");
separator = ", ";
}
if (requirements & kFragCoord_Requirement) {
this->write(separator);
this->write("float4 _fragCoord");
separator = ", ";
}
this->writeFunctionRequirementParams(f, separator);
}
for (const auto& param : f.parameters()) {
this->write(separator);
separator = ", ";
this->writeModifiers(param->modifiers(), false);
this->writeModifiers(param->modifiers(), /*globalContext=*/false);
const Type* type = &param->type();
this->writeBaseType(*type);
if (param->modifiers().fFlags & Modifiers::kOut_Flag) {
@ -1243,7 +1390,7 @@ void MetalCodeGenerator::writeFunction(const FunctionDefinition& f) {
}
void MetalCodeGenerator::writeModifiers(const Modifiers& modifiers,
bool globalContext) {
bool globalContext) {
if (modifiers.fFlags & Modifiers::kOut_Flag) {
this->write("thread ");
}
@ -1256,7 +1403,7 @@ void MetalCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
if ("sk_PerVertex" == intf.typeName()) {
return;
}
this->writeModifiers(intf.variable().modifiers(), true);
this->writeModifiers(intf.variable().modifiers(), /*globalContext=*/true);
this->write("struct ");
this->writeLine(intf.typeName() + " {");
const Type* structType = &intf.variable().type();
@ -1327,7 +1474,7 @@ void MetalCodeGenerator::writeFields(const std::vector<Type::Field>& fields, int
return;
}
currentOffset += fieldSize;
this->writeModifiers(field.fModifiers, false);
this->writeModifiers(field.fModifiers, /*globalContext=*/false);
this->writeBaseType(*fieldType);
this->write(" ");
this->writeName(field.fName);
@ -1849,7 +1996,8 @@ void MetalCodeGenerator::writeProgramElement(const ProgramElement& e) {
this->writeFunctionPrototype(e.as<FunctionPrototype>());
break;
case ProgramElement::Kind::kModifiers:
this->writeModifiers(e.as<ModifiersDeclaration>().modifiers(), true);
this->writeModifiers(e.as<ModifiersDeclaration>().modifiers(),
/*globalContext=*/true);
this->writeLine(";");
break;
case ProgramElement::Kind::kEnum:

View File

@ -178,6 +178,11 @@ protected:
void writeFunctionStart(const FunctionDeclaration& f);
void writeFunctionRequirementParams(const FunctionDeclaration& f,
const char*& separator);
void writeFunctionRequirementArgs(const FunctionDeclaration& f, const char*& separator);
bool writeFunctionDeclaration(const FunctionDeclaration& f);
void writeFunction(const FunctionDefinition& f);
@ -204,7 +209,9 @@ protected:
void writeMinAbsHack(Expression& absExpr, Expression& otherExpr);
String getOutParamHelper(const FunctionDeclaration& function, const ExpressionArray& arguments);
String getOutParamHelper(const FunctionCall& c,
const ExpressionArray& arguments,
const SkTArray<VariableReference*>& outVars);
String getInverseHack(const Expression& mat);
@ -284,7 +291,6 @@ protected:
int fPaddingCount = 0;
const char* fLineEnding;
const Context& fContext;
StringStream fHeader;
String fFunctionHeader;
StringStream fExtraFunctions;
Program::Kind fProgramKind;
@ -305,6 +311,8 @@ protected:
int fUniformBuffer = -1;
String fRTHeightName;
const FunctionDeclaration* fCurrentFunction = nullptr;
int fSwizzleHelperCount = 0;
bool fIgnoreVariableReferenceModifiers = false;
using INHERITED = CodeGenerator;
};

View File

@ -210,15 +210,11 @@ String to_string(uint32_t value) {
}
String to_string(int64_t value) {
std::stringstream buffer;
buffer << value;
return String(buffer.str().c_str());
return SkSL::String::printf("%lld", value);
}
String to_string(uint64_t value) {
std::stringstream buffer;
buffer << value;
return String(buffer.str().c_str());
return SkSL::String::printf("%llu", value);
}
String to_string(double value) {

View File

@ -0,0 +1,20 @@
/*#pragma settings NoInline*/
half2 glob = half2(1);
half4 fn(half a, out half2 b, inout half2 c, inout half3 d) {
a = sk_FragColor.r + a;
b = sk_FragColor.gb - glob.y;
c *= a;
d = sk_FragColor.aaa / d;
return half4(a, b.x, c.y, d.x);
}
void main() {
half2 a = half2(1);
half3 b = half3(2);
half4x4 c = half4x4(3);
half3x3 d = half3x3(4);
sk_FragColor = fn(a.x, b.yz, glob.yx, d[1].zyx);
}

View File

@ -6,6 +6,216 @@ struct Inputs {
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
void out_half(thread float& v);
void _skOutParamHelper0_out_half(thread float& h) {
float _var0;
out_half(_var0);
h = _var0;
}
void out_half2(thread float2& v);
void _skOutParamHelper1_out_half2(thread float2& h2) {
float2 _var0;
out_half2(_var0);
h2 = _var0;
}
void out_half3(thread float3& v);
void _skOutParamHelper2_out_half3(thread float3& h3) {
float3 _var0;
out_half3(_var0);
h3 = _var0;
}
void out_half4(thread float4& v);
void _skOutParamHelper3_out_half4(thread float4& h4) {
float4 _var0;
out_half4(_var0);
h4 = _var0;
}
void out_half(thread float& v);
void _skOutParamHelper4_out_half(thread float3& h3) {
float _var0;
out_half(_var0);
h3.y = _var0;
}
void out_half2(thread float2& v);
void _skOutParamHelper5_out_half2(thread float3& h3) {
float2 _var0;
out_half2(_var0);
h3.xz = _var0;
}
void out_half4(thread float4& v);
void _skOutParamHelper6_out_half4(thread float4& h4) {
float4 _var0;
out_half4(_var0);
h4.zwxy = _var0;
}
void out_half2x2(thread float2x2& v);
void _skOutParamHelper7_out_half2x2(thread float2x2& h2x2) {
float2x2 _var0;
out_half2x2(_var0);
h2x2 = _var0;
}
void out_half3x3(thread float3x3& v);
void _skOutParamHelper8_out_half3x3(thread float3x3& h3x3) {
float3x3 _var0;
out_half3x3(_var0);
h3x3 = _var0;
}
void out_half4x4(thread float4x4& v);
void _skOutParamHelper9_out_half4x4(thread float4x4& h4x4) {
float4x4 _var0;
out_half4x4(_var0);
h4x4 = _var0;
}
void out_half3(thread float3& v);
void _skOutParamHelper10_out_half3(thread float3x3& h3x3) {
float3 _var0;
out_half3(_var0);
h3x3[1] = _var0;
}
void out_half4(thread float4& v);
void _skOutParamHelper11_out_half4(thread float4x4& h4x4) {
float4 _var0;
out_half4(_var0);
h4x4[3].zwxy = _var0;
}
void out_half2(thread float2& v);
void _skOutParamHelper12_out_half2(thread float2x2& h2x2) {
float2 _var0;
out_half2(_var0);
h2x2[0] = _var0;
}
void out_int(thread int& v);
void _skOutParamHelper13_out_int(thread int& i) {
int _var0;
out_int(_var0);
i = _var0;
}
void out_int2(thread int2& v);
void _skOutParamHelper14_out_int2(thread int2& i2) {
int2 _var0;
out_int2(_var0);
i2 = _var0;
}
void out_int3(thread int3& v);
void _skOutParamHelper15_out_int3(thread int3& i3) {
int3 _var0;
out_int3(_var0);
i3 = _var0;
}
void out_int4(thread int4& v);
void _skOutParamHelper16_out_int4(thread int4& i4) {
int4 _var0;
out_int4(_var0);
i4 = _var0;
}
void out_int3(thread int3& v);
void _skOutParamHelper17_out_int3(thread int4& i4) {
int3 _var0;
out_int3(_var0);
i4.xyz = _var0;
}
void out_float(thread float& v);
void _skOutParamHelper18_out_float(thread float& f) {
float _var0;
out_float(_var0);
f = _var0;
}
void out_float2(thread float2& v);
void _skOutParamHelper19_out_float2(thread float2& f2) {
float2 _var0;
out_float2(_var0);
f2 = _var0;
}
void out_float3(thread float3& v);
void _skOutParamHelper20_out_float3(thread float3& f3) {
float3 _var0;
out_float3(_var0);
f3 = _var0;
}
void out_float4(thread float4& v);
void _skOutParamHelper21_out_float4(thread float4& f4) {
float4 _var0;
out_float4(_var0);
f4 = _var0;
}
void out_float2(thread float2& v);
void _skOutParamHelper22_out_float2(thread float3& f3) {
float2 _var0;
out_float2(_var0);
f3.xy = _var0;
}
void out_float(thread float& v);
void _skOutParamHelper23_out_float(thread float2& f2) {
float _var0;
out_float(_var0);
f2.x = _var0;
}
void out_float2x2(thread float2x2& v);
void _skOutParamHelper24_out_float2x2(thread float2x2& f2x2) {
float2x2 _var0;
out_float2x2(_var0);
f2x2 = _var0;
}
void out_float3x3(thread float3x3& v);
void _skOutParamHelper25_out_float3x3(thread float3x3& f3x3) {
float3x3 _var0;
out_float3x3(_var0);
f3x3 = _var0;
}
void out_float4x4(thread float4x4& v);
void _skOutParamHelper26_out_float4x4(thread float4x4& f4x4) {
float4x4 _var0;
out_float4x4(_var0);
f4x4 = _var0;
}
void out_float(thread float& v);
void _skOutParamHelper27_out_float(thread float2x2& f2x2) {
float _var0;
out_float(_var0);
f2x2[0].x = _var0;
}
void out_float4(thread float4& v);
void _skOutParamHelper28_out_float4(thread float4x4& f4x4) {
float4 _var0;
out_float4(_var0);
f4x4[1] = _var0;
}
void out_bool(thread bool& v);
void _skOutParamHelper29_out_bool(thread bool& b) {
bool _var0;
out_bool(_var0);
b = _var0;
}
void out_bool2(thread bool2& v);
void _skOutParamHelper30_out_bool2(thread bool2& b2) {
bool2 _var0;
out_bool2(_var0);
b2 = _var0;
}
void out_bool3(thread bool3& v);
void _skOutParamHelper31_out_bool3(thread bool3& b3) {
bool3 _var0;
out_bool3(_var0);
b3 = _var0;
}
void out_bool4(thread bool4& v);
void _skOutParamHelper32_out_bool4(thread bool4& b4) {
bool4 _var0;
out_bool4(_var0);
b4 = _var0;
}
void out_bool2(thread bool2& v);
void _skOutParamHelper33_out_bool2(thread bool4& b4) {
bool2 _var0;
out_bool2(_var0);
b4.xw = _var0;
}
void out_bool(thread bool& v);
void _skOutParamHelper34_out_bool(thread bool3& b3) {
bool _var0;
out_bool(_var0);
b3.z = _var0;
}
void out_half(thread float& v) {
v = 1.0;
}
@ -76,67 +286,67 @@ fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front
Outputs _outputStruct;
thread Outputs* _out = &_outputStruct;
float h;
out_half(h);
_skOutParamHelper0_out_half(h);
float2 h2;
out_half2(h2);
_skOutParamHelper1_out_half2(h2);
float3 h3;
out_half3(h3);
_skOutParamHelper2_out_half3(h3);
float4 h4;
out_half4(h4);
/*needs swizzle fix*/ out_half(h3.y);
/*needs swizzle fix*/ out_half2(h3.xz);
/*needs swizzle fix*/ out_half4(h4.zwxy);
_skOutParamHelper3_out_half4(h4);
_skOutParamHelper4_out_half(h3);
_skOutParamHelper5_out_half2(h3);
_skOutParamHelper6_out_half4(h4);
_out->sk_FragColor = float4(h, h2.x, h3.x, h4.x);
float2x2 h2x2;
out_half2x2(h2x2);
_skOutParamHelper7_out_half2x2(h2x2);
float3x3 h3x3;
out_half3x3(h3x3);
_skOutParamHelper8_out_half3x3(h3x3);
float4x4 h4x4;
out_half4x4(h4x4);
out_half3(h3x3[1]);
/*needs swizzle fix*/ out_half4(h4x4[3].zwxy);
out_half2(h2x2[0]);
_skOutParamHelper9_out_half4x4(h4x4);
_skOutParamHelper10_out_half3(h3x3);
_skOutParamHelper11_out_half4(h4x4);
_skOutParamHelper12_out_half2(h2x2);
_out->sk_FragColor = float4(h2x2[0].x, h3x3[0].x, h4x4[0].x, 1.0);
int i;
out_int(i);
_skOutParamHelper13_out_int(i);
int2 i2;
out_int2(i2);
_skOutParamHelper14_out_int2(i2);
int3 i3;
out_int3(i3);
_skOutParamHelper15_out_int3(i3);
int4 i4;
out_int4(i4);
/*needs swizzle fix*/ out_int3(i4.xyz);
_skOutParamHelper16_out_int4(i4);
_skOutParamHelper17_out_int3(i4);
_out->sk_FragColor = float4(float(i), float(i2.x), float(i3.x), float(i4.x));
float f;
out_float(f);
_skOutParamHelper18_out_float(f);
float2 f2;
out_float2(f2);
_skOutParamHelper19_out_float2(f2);
float3 f3;
out_float3(f3);
_skOutParamHelper20_out_float3(f3);
float4 f4;
out_float4(f4);
/*needs swizzle fix*/ out_float2(f3.xy);
/*needs swizzle fix*/ out_float(f2.x);
_skOutParamHelper21_out_float4(f4);
_skOutParamHelper22_out_float2(f3);
_skOutParamHelper23_out_float(f2);
_out->sk_FragColor = float4(f, f2.x, f3.x, f4.x);
float2x2 f2x2;
out_float2x2(f2x2);
_skOutParamHelper24_out_float2x2(f2x2);
float3x3 f3x3;
out_float3x3(f3x3);
_skOutParamHelper25_out_float3x3(f3x3);
float4x4 f4x4;
out_float4x4(f4x4);
/*needs swizzle fix*/ out_float(f2x2[0].x);
out_float4(f4x4[1]);
_skOutParamHelper26_out_float4x4(f4x4);
_skOutParamHelper27_out_float(f2x2);
_skOutParamHelper28_out_float4(f4x4);
_out->sk_FragColor = float4(f2x2[0].x, f3x3[0].x, f4x4[0].x, 1.0);
bool b;
out_bool(b);
_skOutParamHelper29_out_bool(b);
bool2 b2;
out_bool2(b2);
_skOutParamHelper30_out_bool2(b2);
bool3 b3;
out_bool3(b3);
_skOutParamHelper31_out_bool3(b3);
bool4 b4;
out_bool4(b4);
/*needs swizzle fix*/ out_bool2(b4.xw);
/*needs swizzle fix*/ out_bool(b3.z);
_skOutParamHelper32_out_bool4(b4);
_skOutParamHelper33_out_bool2(b4);
_skOutParamHelper34_out_bool(b3);
_out->sk_FragColor = float4(b ? 1.0 : 0.0, b2.x ? 1.0 : 0.0, b3.x ? 1.0 : 0.0, b4.x ? 1.0 : 0.0);
return *_out;
}

View File

@ -0,0 +1,41 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Inputs {
};
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
struct Globals {
float2 glob;
};
float4 fn(thread Outputs* _out, thread Globals* _globals, float a, thread float2& b, thread float2& c, thread float3& d);
float4 _skOutParamHelper0_fn(thread Outputs* _out, thread Globals* _globals, float _var0, thread float3& b, thread float2& glob, thread float3x3& d) {
float2 _var1;
float2 _var2 = glob.yx;
float3 _var3 = d[1].zyx;
float4 _skResult = fn(_out, _globals, _var0, _var1, _var2, _var3);
b.yz = _var1;
glob.yx = _var2;
d[1].zyx = _var3;
return _skResult;
}
float4 fn(thread Outputs* _out, thread Globals* _globals, float a, thread float2& b, thread float2& c, thread float3& d) {
a = _out->sk_FragColor.x + a;
b = _out->sk_FragColor.yz - _globals->glob.y;
c *= a;
d = _out->sk_FragColor.www / d;
return float4(a, b.x, c.y, d.x);
}
fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Globals globalStruct{float2(1.0)};
thread Globals* _globals = &globalStruct;
(void)_globals;
Outputs _outputStruct;
thread Outputs* _out = &_outputStruct;
float3 b = float3(2.0);
float3x3 d = float3x3(4.0);
_out->sk_FragColor = _skOutParamHelper0_fn(_out, _globals, 1.0, b, _globals->glob, d);
return *_out;
}

View File

@ -6,13 +6,25 @@ struct Inputs {
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
float _skOutParamHelper0_frexp(float _var0, thread int& exp1) {
int _var1;
float _skResult = frexp(_var0, _var1);
exp1 = _var1;
return _skResult;
}
float3 _skOutParamHelper1_frexp(float3 _var0, thread int3& exp3) {
int3 _var1;
float3 _skResult = frexp(_var0, _var1);
exp3 = _var1;
return _skResult;
}
fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _outputStruct;
thread Outputs* _out = &_outputStruct;
int exp1;
float a = frexp(0.5, exp1);
float a = _skOutParamHelper0_frexp(0.5, exp1);
_out->sk_FragColor = float4(float(exp1));
int3 exp3;
_out->sk_FragColor.xyz = frexp(float3(3.5), exp3);
_out->sk_FragColor.xyz = _skOutParamHelper1_frexp(float3(3.5), exp3);
return *_out;
}

View File

@ -6,6 +6,12 @@ struct Inputs {
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
void bar(thread float& x);
void _skOutParamHelper0_bar(thread float& x) {
float _var0 = x;
bar(_var0);
x = _var0;
}
float foo(float v[2]) {
return v[0] * v[1];
}
@ -20,7 +26,7 @@ fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front
Outputs _outputStruct;
thread Outputs* _out = &_outputStruct;
float x = 10.0;
bar(x);
_skOutParamHelper0_bar(x);
_out->sk_FragColor = float4(x);
return *_out;
}