Implement struct == and != on SPIR-V.
OpenGL ES2 allows structs to be compared: http://screen/6KnX4ZfkdLtqDWv This already worked in GLSL, Metal and SkVM. Change-Id: Iaf7029c0c1ea9d447348c8280a2788f0d36befad Bug: skia:11846 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/393598 Auto-Submit: John Stiles <johnstiles@google.com> Commit-Queue: Ethan Nicholas <ethannicholas@google.com> Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
This commit is contained in:
parent
ca1137b1d1
commit
bc5c2a0ab9
@ -4,6 +4,8 @@ uniform half4 colorRed, colorGreen;
|
||||
|
||||
struct S { float x; int y; };
|
||||
|
||||
struct Nested { S a, b; };
|
||||
|
||||
S returns_a_struct() {
|
||||
S s;
|
||||
s.x = 1;
|
||||
@ -25,6 +27,16 @@ half4 main() {
|
||||
float x = accepts_a_struct(s);
|
||||
modifies_a_struct(s);
|
||||
|
||||
bool valid = (x == 3) && (s.x == 2) && (s.y == 3);
|
||||
S expected;
|
||||
expected.x = 2;
|
||||
expected.y = 3;
|
||||
|
||||
Nested n1, n2, n3;
|
||||
n1.a = n1.b = returns_a_struct();
|
||||
n3 = n2 = n1;
|
||||
modifies_a_struct(n3.b);
|
||||
|
||||
bool valid = (x == 3) && (s.x == 2) && (s.y == 3) &&
|
||||
(s == expected) && (s != returns_a_struct()) && (n1 == n2) && (n1 != n3);
|
||||
return valid ? colorGreen : colorRed;
|
||||
}
|
||||
|
@ -2394,6 +2394,9 @@ SpvId SPIRVCodeGenerator::writeBinaryExpression(const Type& leftType, SpvId lhs,
|
||||
return this->writeMatrixComparison(*operandType, lhs, rhs, SpvOpFOrdEqual,
|
||||
SpvOpIEqual, SpvOpAll, SpvOpLogicalAnd, out);
|
||||
}
|
||||
if (operandType->isStruct()) {
|
||||
return this->writeStructComparison(*operandType, lhs, op, rhs, out);
|
||||
}
|
||||
SkASSERT(resultType.isBoolean());
|
||||
const Type* tmpType;
|
||||
if (operandType->isVector()) {
|
||||
@ -2413,6 +2416,9 @@ SpvId SPIRVCodeGenerator::writeBinaryExpression(const Type& leftType, SpvId lhs,
|
||||
return this->writeMatrixComparison(*operandType, lhs, rhs, SpvOpFOrdNotEqual,
|
||||
SpvOpINotEqual, SpvOpAny, SpvOpLogicalOr, out);
|
||||
}
|
||||
if (operandType->isStruct()) {
|
||||
return this->writeStructComparison(*operandType, lhs, op, rhs, out);
|
||||
}
|
||||
[[fallthrough]];
|
||||
case Token::Kind::TK_LOGICALXOR:
|
||||
SkASSERT(resultType.isBoolean());
|
||||
@ -2503,21 +2509,85 @@ SpvId SPIRVCodeGenerator::writeBinaryExpression(const Type& leftType, SpvId lhs,
|
||||
}
|
||||
}
|
||||
|
||||
SpvId SPIRVCodeGenerator::writeStructComparison(const Type& structType, SpvId lhs, Operator op,
|
||||
SpvId rhs, OutputStream& out) {
|
||||
// The inputs must be structs containing fields, and the op must be == or !=.
|
||||
SkASSERT(structType.isStruct());
|
||||
SkASSERT(op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ);
|
||||
const std::vector<Type::Field>& fields = structType.fields();
|
||||
SkASSERT(!fields.empty());
|
||||
|
||||
// Synthesize equality checks for each field in the struct.
|
||||
const Type& boolType = *fContext.fTypes.fBool;
|
||||
SpvId boolTypeId = this->getType(boolType);
|
||||
SpvId allComparisons = (SpvId)-1;
|
||||
for (int index = 0; index < (int)fields.size(); ++index) {
|
||||
// Get the left and right versions of this field.
|
||||
const Type& fieldType = *fields[index].fType;
|
||||
SpvId fieldTypeId = this->getType(fieldType);
|
||||
|
||||
SpvId fieldL = this->nextId(&fieldType);
|
||||
this->writeInstruction(SpvOpCompositeExtract, fieldTypeId, fieldL, lhs, index, out);
|
||||
SpvId fieldR = this->nextId(&fieldType);
|
||||
this->writeInstruction(SpvOpCompositeExtract, fieldTypeId, fieldR, rhs, index, out);
|
||||
// Use `writeBinaryExpression` with the requested == or != operator on these fields.
|
||||
SpvId comparison = this->writeBinaryExpression(fieldType, fieldL, op, fieldType, fieldR,
|
||||
boolType, out);
|
||||
// If this is the first field, we don't need to merge comparison results with anything.
|
||||
if (allComparisons == (SpvId)-1) {
|
||||
allComparisons = comparison;
|
||||
continue;
|
||||
}
|
||||
// Use LogicalAnd or LogicalOr to combine the comparison with all the other comparisons.
|
||||
SpvId logicalOp = this->nextId(&boolType);
|
||||
switch (op.kind()) {
|
||||
case Token::Kind::TK_EQEQ:
|
||||
this->writeInstruction(SpvOpLogicalAnd, boolTypeId, logicalOp,
|
||||
comparison, allComparisons, out);
|
||||
break;
|
||||
case Token::Kind::TK_NEQ:
|
||||
this->writeInstruction(SpvOpLogicalOr, boolTypeId, logicalOp,
|
||||
comparison, allComparisons, out);
|
||||
break;
|
||||
default:
|
||||
return (SpvId)-1;
|
||||
}
|
||||
allComparisons = logicalOp;
|
||||
}
|
||||
return allComparisons;
|
||||
}
|
||||
|
||||
static float division_by_literal_value(Operator op, const Expression& right) {
|
||||
// If this is a division by a literal value, returns that literal value. Otherwise, returns 0.
|
||||
if (op.kind() == Token::Kind::TK_SLASH && right.is<FloatLiteral>()) {
|
||||
float rhsValue = right.as<FloatLiteral>().value();
|
||||
if (std::isfinite(rhsValue)) {
|
||||
return rhsValue;
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, OutputStream& out) {
|
||||
const Expression* left = b.left().get();
|
||||
const Expression* right = b.right().get();
|
||||
Operator op = b.getOperator();
|
||||
// handle cases where we don't necessarily evaluate both LHS and RHS
|
||||
|
||||
switch (op.kind()) {
|
||||
case Token::Kind::TK_EQ: {
|
||||
// Handles assignment.
|
||||
SpvId rhs = this->writeExpression(*right, out);
|
||||
this->getLValue(*left, out)->store(rhs, out);
|
||||
return rhs;
|
||||
}
|
||||
case Token::Kind::TK_LOGICALAND:
|
||||
return this->writeLogicalAnd(b, out);
|
||||
// Handles short-circuiting; we don't necessarily evaluate both LHS and RHS.
|
||||
return this->writeLogicalAnd(*b.left(), *b.right(), out);
|
||||
|
||||
case Token::Kind::TK_LOGICALOR:
|
||||
return this->writeLogicalOr(b, out);
|
||||
// Handles short-circuiting; we don't necessarily evaluate both LHS and RHS.
|
||||
return this->writeLogicalOr(*b.left(), *b.right(), out);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2532,20 +2602,17 @@ SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, Outpu
|
||||
lhs = this->writeExpression(*left, out);
|
||||
}
|
||||
|
||||
SpvId rhs = (SpvId)-1;
|
||||
if (op.kind() == Token::Kind::TK_SLASH && right->is<FloatLiteral>()) {
|
||||
float rhsValue = right->as<FloatLiteral>().value();
|
||||
if (std::isfinite(rhsValue) && rhsValue != 0.0f) {
|
||||
// Rewrite floating-point division by a literal into multiplication by the reciprocal.
|
||||
// This converts `expr / 2` into `expr * 0.5`
|
||||
// This improves codegen, especially for certain types of divides (e.g. vector/scalar).
|
||||
op = Operator(Token::Kind::TK_STAR);
|
||||
FloatLiteral reciprocal{right->fOffset, 1.0f / rhsValue, &right->type()};
|
||||
rhs = this->writeExpression(reciprocal, out);
|
||||
}
|
||||
}
|
||||
|
||||
if (rhs == (SpvId)-1) {
|
||||
SpvId rhs;
|
||||
float rhsValue = division_by_literal_value(op, *right);
|
||||
if (rhsValue != 0.0f) {
|
||||
// Rewrite floating-point division by a literal into multiplication by the reciprocal.
|
||||
// This converts `expr / 2` into `expr * 0.5`
|
||||
// This improves codegen, especially for certain types of divides (e.g. vector/scalar).
|
||||
op = Operator(Token::Kind::TK_STAR);
|
||||
FloatLiteral reciprocal{right->fOffset, 1.0f / rhsValue, &right->type()};
|
||||
rhs = this->writeExpression(reciprocal, out);
|
||||
} else {
|
||||
// Write the right-hand side expression normally.
|
||||
rhs = this->writeExpression(*right, out);
|
||||
}
|
||||
|
||||
@ -2557,18 +2624,18 @@ SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, Outpu
|
||||
return result;
|
||||
}
|
||||
|
||||
SpvId SPIRVCodeGenerator::writeLogicalAnd(const BinaryExpression& a, OutputStream& out) {
|
||||
SkASSERT(a.getOperator().kind() == Token::Kind::TK_LOGICALAND);
|
||||
SpvId SPIRVCodeGenerator::writeLogicalAnd(const Expression& left, const Expression& right,
|
||||
OutputStream& out) {
|
||||
BoolLiteral falseLiteral(/*offset=*/-1, /*value=*/false, fContext.fTypes.fBool.get());
|
||||
SpvId falseConstant = this->writeBoolLiteral(falseLiteral);
|
||||
SpvId lhs = this->writeExpression(*a.left(), out);
|
||||
SpvId lhs = this->writeExpression(left, out);
|
||||
SpvId rhsLabel = this->nextId(nullptr);
|
||||
SpvId end = this->nextId(nullptr);
|
||||
SpvId lhsBlock = fCurrentBlock;
|
||||
this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
|
||||
this->writeInstruction(SpvOpBranchConditional, lhs, rhsLabel, end, out);
|
||||
this->writeLabel(rhsLabel, out);
|
||||
SpvId rhs = this->writeExpression(*a.right(), out);
|
||||
SpvId rhs = this->writeExpression(right, out);
|
||||
SpvId rhsBlock = fCurrentBlock;
|
||||
this->writeInstruction(SpvOpBranch, end, out);
|
||||
this->writeLabel(end, out);
|
||||
@ -2578,18 +2645,18 @@ SpvId SPIRVCodeGenerator::writeLogicalAnd(const BinaryExpression& a, OutputStrea
|
||||
return result;
|
||||
}
|
||||
|
||||
SpvId SPIRVCodeGenerator::writeLogicalOr(const BinaryExpression& o, OutputStream& out) {
|
||||
SkASSERT(o.getOperator().kind() == Token::Kind::TK_LOGICALOR);
|
||||
SpvId SPIRVCodeGenerator::writeLogicalOr(const Expression& left, const Expression& right,
|
||||
OutputStream& out) {
|
||||
BoolLiteral trueLiteral(/*offset=*/-1, /*value=*/true, fContext.fTypes.fBool.get());
|
||||
SpvId trueConstant = this->writeBoolLiteral(trueLiteral);
|
||||
SpvId lhs = this->writeExpression(*o.left(), out);
|
||||
SpvId lhs = this->writeExpression(left, out);
|
||||
SpvId rhsLabel = this->nextId(nullptr);
|
||||
SpvId end = this->nextId(nullptr);
|
||||
SpvId lhsBlock = fCurrentBlock;
|
||||
this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
|
||||
this->writeInstruction(SpvOpBranchConditional, lhs, end, rhsLabel, out);
|
||||
this->writeLabel(rhsLabel, out);
|
||||
SpvId rhs = this->writeExpression(*o.right(), out);
|
||||
SpvId rhs = this->writeExpression(right, out);
|
||||
SpvId rhsBlock = fCurrentBlock;
|
||||
this->writeInstruction(SpvOpBranch, end, out);
|
||||
this->writeLabel(end, out);
|
||||
|
@ -324,6 +324,9 @@ private:
|
||||
SpvOp_ intOperator, SpvOp_ vectorMergeOperator,
|
||||
SpvOp_ mergeOperator, OutputStream& out);
|
||||
|
||||
SpvId writeStructComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs,
|
||||
OutputStream& out);
|
||||
|
||||
SpvId writeComponentwiseMatrixBinary(const Type& operandType, SpvId lhs, SpvId rhs,
|
||||
SpvOp_ floatOperator, SpvOp_ intOperator,
|
||||
OutputStream& out);
|
||||
@ -347,9 +350,9 @@ private:
|
||||
|
||||
SpvId writeIndexExpression(const IndexExpression& expr, OutputStream& out);
|
||||
|
||||
SpvId writeLogicalAnd(const BinaryExpression& b, OutputStream& out);
|
||||
SpvId writeLogicalAnd(const Expression& left, const Expression& right, OutputStream& out);
|
||||
|
||||
SpvId writeLogicalOr(const BinaryExpression& o, OutputStream& out);
|
||||
SpvId writeLogicalOr(const Expression& left, const Expression& right, OutputStream& out);
|
||||
|
||||
SpvId writePrefixExpression(const PrefixExpression& p, OutputStream& out);
|
||||
|
||||
|
@ -19,6 +19,13 @@ OpName %modifies_a_struct_vS "modifies_a_struct_vS"
|
||||
OpName %main "main"
|
||||
OpName %s_0 "s"
|
||||
OpName %x "x"
|
||||
OpName %expected "expected"
|
||||
OpName %Nested "Nested"
|
||||
OpMemberName %Nested 0 "a"
|
||||
OpMemberName %Nested 1 "b"
|
||||
OpName %n1 "n1"
|
||||
OpName %n2 "n2"
|
||||
OpName %n3 "n3"
|
||||
OpName %valid "valid"
|
||||
OpDecorate %sk_FragColor RelaxedPrecision
|
||||
OpDecorate %sk_FragColor Location 0
|
||||
@ -35,10 +42,23 @@ OpMemberDecorate %S 0 Offset 0
|
||||
OpMemberDecorate %S 1 Offset 4
|
||||
OpDecorate %35 RelaxedPrecision
|
||||
OpDecorate %59 RelaxedPrecision
|
||||
OpDecorate %83 RelaxedPrecision
|
||||
OpDecorate %91 RelaxedPrecision
|
||||
OpDecorate %93 RelaxedPrecision
|
||||
OpDecorate %94 RelaxedPrecision
|
||||
OpMemberDecorate %Nested 0 Offset 0
|
||||
OpMemberDecorate %Nested 0 RelaxedPrecision
|
||||
OpMemberDecorate %Nested 1 Offset 16
|
||||
OpMemberDecorate %Nested 1 RelaxedPrecision
|
||||
OpDecorate %76 RelaxedPrecision
|
||||
OpDecorate %78 RelaxedPrecision
|
||||
OpDecorate %102 RelaxedPrecision
|
||||
OpDecorate %103 RelaxedPrecision
|
||||
OpDecorate %114 RelaxedPrecision
|
||||
OpDecorate %126 RelaxedPrecision
|
||||
OpDecorate %127 RelaxedPrecision
|
||||
OpDecorate %150 RelaxedPrecision
|
||||
OpDecorate %151 RelaxedPrecision
|
||||
OpDecorate %172 RelaxedPrecision
|
||||
OpDecorate %180 RelaxedPrecision
|
||||
OpDecorate %182 RelaxedPrecision
|
||||
OpDecorate %183 RelaxedPrecision
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
||||
@ -64,11 +84,13 @@ OpDecorate %94 RelaxedPrecision
|
||||
%36 = OpTypeFunction %float %_ptr_Function_S
|
||||
%45 = OpTypeFunction %void %_ptr_Function_S
|
||||
%54 = OpTypeFunction %v4float
|
||||
%float_2 = OpConstant %float 2
|
||||
%int_3 = OpConstant %int 3
|
||||
%Nested = OpTypeStruct %S %S
|
||||
%_ptr_Function_Nested = OpTypePointer Function %Nested
|
||||
%_ptr_Function_bool = OpTypePointer Function %bool
|
||||
%false = OpConstantFalse %bool
|
||||
%float_3 = OpConstant %float 3
|
||||
%float_2 = OpConstant %float 2
|
||||
%int_3 = OpConstant %int 3
|
||||
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
||||
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
|
||||
%_entrypoint_v = OpFunction %void None %18
|
||||
@ -116,8 +138,13 @@ OpFunctionEnd
|
||||
%s_0 = OpVariable %_ptr_Function_S Function
|
||||
%x = OpVariable %_ptr_Function_float Function
|
||||
%60 = OpVariable %_ptr_Function_S Function
|
||||
%expected = OpVariable %_ptr_Function_S Function
|
||||
%n1 = OpVariable %_ptr_Function_Nested Function
|
||||
%n2 = OpVariable %_ptr_Function_Nested Function
|
||||
%n3 = OpVariable %_ptr_Function_Nested Function
|
||||
%79 = OpVariable %_ptr_Function_S Function
|
||||
%valid = OpVariable %_ptr_Function_bool Function
|
||||
%84 = OpVariable %_ptr_Function_v4float Function
|
||||
%173 = OpVariable %_ptr_Function_v4float Function
|
||||
%57 = OpFunctionCall %S %returns_a_struct_S
|
||||
OpStore %s_0 %57
|
||||
%59 = OpLoad %S %s_0
|
||||
@ -125,41 +152,143 @@ OpStore %60 %59
|
||||
%61 = OpFunctionCall %float %accepts_a_struct_fS %60
|
||||
OpStore %x %61
|
||||
%62 = OpFunctionCall %void %modifies_a_struct_vS %s_0
|
||||
%66 = OpLoad %float %x
|
||||
%68 = OpFOrdEqual %bool %66 %float_3
|
||||
OpSelectionMerge %70 None
|
||||
OpBranchConditional %68 %69 %70
|
||||
%69 = OpLabel
|
||||
%71 = OpAccessChain %_ptr_Function_float %s_0 %int_0
|
||||
%72 = OpLoad %float %71
|
||||
%74 = OpFOrdEqual %bool %72 %float_2
|
||||
OpBranch %70
|
||||
%70 = OpLabel
|
||||
%75 = OpPhi %bool %false %55 %74 %69
|
||||
OpSelectionMerge %77 None
|
||||
OpBranchConditional %75 %76 %77
|
||||
%76 = OpLabel
|
||||
%78 = OpAccessChain %_ptr_Function_int %s_0 %int_1
|
||||
%79 = OpLoad %int %78
|
||||
%81 = OpIEqual %bool %79 %int_3
|
||||
OpBranch %77
|
||||
%77 = OpLabel
|
||||
%82 = OpPhi %bool %false %70 %81 %76
|
||||
OpStore %valid %82
|
||||
%83 = OpLoad %bool %valid
|
||||
OpSelectionMerge %88 None
|
||||
OpBranchConditional %83 %86 %87
|
||||
%86 = OpLabel
|
||||
%89 = OpAccessChain %_ptr_Uniform_v4float %13 %int_1
|
||||
%91 = OpLoad %v4float %89
|
||||
OpStore %84 %91
|
||||
OpBranch %88
|
||||
%87 = OpLabel
|
||||
%92 = OpAccessChain %_ptr_Uniform_v4float %13 %int_0
|
||||
%93 = OpLoad %v4float %92
|
||||
OpStore %84 %93
|
||||
OpBranch %88
|
||||
%65 = OpAccessChain %_ptr_Function_float %expected %int_0
|
||||
OpStore %65 %float_2
|
||||
%67 = OpAccessChain %_ptr_Function_int %expected %int_1
|
||||
OpStore %67 %int_3
|
||||
%73 = OpFunctionCall %S %returns_a_struct_S
|
||||
%74 = OpAccessChain %_ptr_Function_S %n1 %int_1
|
||||
OpStore %74 %73
|
||||
%75 = OpAccessChain %_ptr_Function_S %n1 %int_0
|
||||
OpStore %75 %73
|
||||
%76 = OpLoad %Nested %n1
|
||||
OpStore %n2 %76
|
||||
OpStore %n3 %76
|
||||
%77 = OpAccessChain %_ptr_Function_S %n3 %int_1
|
||||
%78 = OpLoad %S %77
|
||||
OpStore %79 %78
|
||||
%80 = OpFunctionCall %void %modifies_a_struct_vS %79
|
||||
%81 = OpLoad %S %79
|
||||
OpStore %77 %81
|
||||
%85 = OpLoad %float %x
|
||||
%87 = OpFOrdEqual %bool %85 %float_3
|
||||
OpSelectionMerge %89 None
|
||||
OpBranchConditional %87 %88 %89
|
||||
%88 = OpLabel
|
||||
%94 = OpLoad %v4float %84
|
||||
OpReturnValue %94
|
||||
%90 = OpAccessChain %_ptr_Function_float %s_0 %int_0
|
||||
%91 = OpLoad %float %90
|
||||
%92 = OpFOrdEqual %bool %91 %float_2
|
||||
OpBranch %89
|
||||
%89 = OpLabel
|
||||
%93 = OpPhi %bool %false %55 %92 %88
|
||||
OpSelectionMerge %95 None
|
||||
OpBranchConditional %93 %94 %95
|
||||
%94 = OpLabel
|
||||
%96 = OpAccessChain %_ptr_Function_int %s_0 %int_1
|
||||
%97 = OpLoad %int %96
|
||||
%98 = OpIEqual %bool %97 %int_3
|
||||
OpBranch %95
|
||||
%95 = OpLabel
|
||||
%99 = OpPhi %bool %false %89 %98 %94
|
||||
OpSelectionMerge %101 None
|
||||
OpBranchConditional %99 %100 %101
|
||||
%100 = OpLabel
|
||||
%102 = OpLoad %S %s_0
|
||||
%103 = OpLoad %S %expected
|
||||
%104 = OpCompositeExtract %float %102 0
|
||||
%105 = OpCompositeExtract %float %103 0
|
||||
%106 = OpFOrdEqual %bool %104 %105
|
||||
%107 = OpCompositeExtract %int %102 1
|
||||
%108 = OpCompositeExtract %int %103 1
|
||||
%109 = OpIEqual %bool %107 %108
|
||||
%110 = OpLogicalAnd %bool %109 %106
|
||||
OpBranch %101
|
||||
%101 = OpLabel
|
||||
%111 = OpPhi %bool %false %95 %110 %100
|
||||
OpSelectionMerge %113 None
|
||||
OpBranchConditional %111 %112 %113
|
||||
%112 = OpLabel
|
||||
%114 = OpLoad %S %s_0
|
||||
%115 = OpFunctionCall %S %returns_a_struct_S
|
||||
%116 = OpCompositeExtract %float %114 0
|
||||
%117 = OpCompositeExtract %float %115 0
|
||||
%118 = OpFOrdNotEqual %bool %116 %117
|
||||
%119 = OpCompositeExtract %int %114 1
|
||||
%120 = OpCompositeExtract %int %115 1
|
||||
%121 = OpINotEqual %bool %119 %120
|
||||
%122 = OpLogicalOr %bool %121 %118
|
||||
OpBranch %113
|
||||
%113 = OpLabel
|
||||
%123 = OpPhi %bool %false %101 %122 %112
|
||||
OpSelectionMerge %125 None
|
||||
OpBranchConditional %123 %124 %125
|
||||
%124 = OpLabel
|
||||
%126 = OpLoad %Nested %n1
|
||||
%127 = OpLoad %Nested %n2
|
||||
%128 = OpCompositeExtract %S %126 0
|
||||
%129 = OpCompositeExtract %S %127 0
|
||||
%130 = OpCompositeExtract %float %128 0
|
||||
%131 = OpCompositeExtract %float %129 0
|
||||
%132 = OpFOrdEqual %bool %130 %131
|
||||
%133 = OpCompositeExtract %int %128 1
|
||||
%134 = OpCompositeExtract %int %129 1
|
||||
%135 = OpIEqual %bool %133 %134
|
||||
%136 = OpLogicalAnd %bool %135 %132
|
||||
%137 = OpCompositeExtract %S %126 1
|
||||
%138 = OpCompositeExtract %S %127 1
|
||||
%139 = OpCompositeExtract %float %137 0
|
||||
%140 = OpCompositeExtract %float %138 0
|
||||
%141 = OpFOrdEqual %bool %139 %140
|
||||
%142 = OpCompositeExtract %int %137 1
|
||||
%143 = OpCompositeExtract %int %138 1
|
||||
%144 = OpIEqual %bool %142 %143
|
||||
%145 = OpLogicalAnd %bool %144 %141
|
||||
%146 = OpLogicalAnd %bool %145 %136
|
||||
OpBranch %125
|
||||
%125 = OpLabel
|
||||
%147 = OpPhi %bool %false %113 %146 %124
|
||||
OpSelectionMerge %149 None
|
||||
OpBranchConditional %147 %148 %149
|
||||
%148 = OpLabel
|
||||
%150 = OpLoad %Nested %n1
|
||||
%151 = OpLoad %Nested %n3
|
||||
%152 = OpCompositeExtract %S %150 0
|
||||
%153 = OpCompositeExtract %S %151 0
|
||||
%154 = OpCompositeExtract %float %152 0
|
||||
%155 = OpCompositeExtract %float %153 0
|
||||
%156 = OpFOrdNotEqual %bool %154 %155
|
||||
%157 = OpCompositeExtract %int %152 1
|
||||
%158 = OpCompositeExtract %int %153 1
|
||||
%159 = OpINotEqual %bool %157 %158
|
||||
%160 = OpLogicalOr %bool %159 %156
|
||||
%161 = OpCompositeExtract %S %150 1
|
||||
%162 = OpCompositeExtract %S %151 1
|
||||
%163 = OpCompositeExtract %float %161 0
|
||||
%164 = OpCompositeExtract %float %162 0
|
||||
%165 = OpFOrdNotEqual %bool %163 %164
|
||||
%166 = OpCompositeExtract %int %161 1
|
||||
%167 = OpCompositeExtract %int %162 1
|
||||
%168 = OpINotEqual %bool %166 %167
|
||||
%169 = OpLogicalOr %bool %168 %165
|
||||
%170 = OpLogicalOr %bool %169 %160
|
||||
OpBranch %149
|
||||
%149 = OpLabel
|
||||
%171 = OpPhi %bool %false %125 %170 %148
|
||||
OpStore %valid %171
|
||||
%172 = OpLoad %bool %valid
|
||||
OpSelectionMerge %177 None
|
||||
OpBranchConditional %172 %175 %176
|
||||
%175 = OpLabel
|
||||
%178 = OpAccessChain %_ptr_Uniform_v4float %13 %int_1
|
||||
%180 = OpLoad %v4float %178
|
||||
OpStore %173 %180
|
||||
OpBranch %177
|
||||
%176 = OpLabel
|
||||
%181 = OpAccessChain %_ptr_Uniform_v4float %13 %int_0
|
||||
%182 = OpLoad %v4float %181
|
||||
OpStore %173 %182
|
||||
OpBranch %177
|
||||
%177 = OpLabel
|
||||
%183 = OpLoad %v4float %173
|
||||
OpReturnValue %183
|
||||
OpFunctionEnd
|
||||
|
@ -6,6 +6,10 @@ struct S {
|
||||
float x;
|
||||
int y;
|
||||
};
|
||||
struct Nested {
|
||||
S a;
|
||||
S b;
|
||||
};
|
||||
S returns_a_struct_S() {
|
||||
S s;
|
||||
s.x = 1.0;
|
||||
@ -23,6 +27,15 @@ vec4 main() {
|
||||
S s = returns_a_struct_S();
|
||||
float x = accepts_a_struct_fS(s);
|
||||
modifies_a_struct_vS(s);
|
||||
bool valid = (x == 3.0 && s.x == 2.0) && s.y == 3;
|
||||
S expected;
|
||||
expected.x = 2.0;
|
||||
expected.y = 3;
|
||||
Nested n1;
|
||||
Nested n2;
|
||||
Nested n3;
|
||||
n1.a = (n1.b = returns_a_struct_S());
|
||||
n3 = (n2 = n1);
|
||||
modifies_a_struct_vS(n3.b);
|
||||
bool valid = (((((x == 3.0 && s.x == 2.0) && s.y == 3) && s == expected) && s != returns_a_struct_S()) && n1 == n2) && n1 != n3;
|
||||
return valid ? colorGreen : colorRed;
|
||||
}
|
||||
|
@ -5,6 +5,10 @@ struct S {
|
||||
float x;
|
||||
int y;
|
||||
};
|
||||
struct Nested {
|
||||
S a;
|
||||
S b;
|
||||
};
|
||||
struct Uniforms {
|
||||
float4 colorRed;
|
||||
float4 colorGreen;
|
||||
@ -20,6 +24,12 @@ void _skOutParamHelper0_modifies_a_struct_vS(thread S& s) {
|
||||
modifies_a_struct_vS(_var0);
|
||||
s = _var0;
|
||||
}
|
||||
void modifies_a_struct_vS(thread S& s);
|
||||
void _skOutParamHelper1_modifies_a_struct_vS(thread Nested& n3) {
|
||||
S _var0 = n3.b;
|
||||
modifies_a_struct_vS(_var0);
|
||||
n3.b = _var0;
|
||||
}
|
||||
|
||||
S returns_a_struct_S() {
|
||||
S s;
|
||||
@ -40,7 +50,16 @@ fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _unifo
|
||||
S s = returns_a_struct_S();
|
||||
float x = accepts_a_struct_fS(s);
|
||||
_skOutParamHelper0_modifies_a_struct_vS(s);
|
||||
bool valid = (x == 3.0 && s.x == 2.0) && s.y == 3;
|
||||
S expected;
|
||||
expected.x = 2.0;
|
||||
expected.y = 3;
|
||||
Nested n1;
|
||||
Nested n2;
|
||||
Nested n3;
|
||||
n1.a = (n1.b = returns_a_struct_S());
|
||||
n3 = (n2 = n1);
|
||||
_skOutParamHelper1_modifies_a_struct_vS(n3);
|
||||
bool valid = (((((x == 3.0 && s.x == 2.0) && s.y == 3) && s == expected) && s != returns_a_struct_S()) && n1 == n2) && n1 != n3;
|
||||
_out.sk_FragColor = valid ? _uniforms.colorGreen : _uniforms.colorRed;
|
||||
return _out;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user