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:
parent
5ef2267a88
commit
06b84efcb3
@ -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 = [
|
||||
|
@ -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 = ¶m->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:
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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) {
|
||||
|
20
tests/sksl/metal/SwizzleHelper.sksl
Normal file
20
tests/sksl/metal/SwizzleHelper.sksl
Normal 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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
41
tests/sksl/metal/golden/SwizzleHelper.metal
Normal file
41
tests/sksl/metal/golden/SwizzleHelper.metal
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user