Move constant swizzle handling into the IR generator

SkSL::Swizzle objects never represent constant swizzles - they are
directly converted to IR that represents a small sub-tree, up to the
most complex case of:

    Swizzle(Constructor(Swizzle(BaseVector), Literals, ...))

GLSL was the only backend that handled arbitrary complex swizzles
correctly in all cases - this change basically moves that logic into the
IR generator, subsumes the (similar) handling of scalar swizzles that
was there, and simplifies the algorithm so that it's all procedural (not
hard-coded based on bit-patterns).

Bug: skia:10721
Change-Id: Iac96a26da517a7b1bdc9905cc38ad727fb68af12
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/317238
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2020-09-15 15:16:56 -04:00 committed by Skia Commit-Bot
parent db42bf2140
commit 2564767d24
14 changed files with 120 additions and 406 deletions

View File

@ -108,7 +108,7 @@ color = clamp(color, 0.0, 1.0);
@if (%s) {
color.xyz = sqrt(color.xyz);
}
%s = half4(color.xyz, 1) * inColor.w;
%s = half4(color.xyz, 1.0) * inColor.w;
)SkSL",
_sample896.c_str(), (_outer.linearize ? "true" : "false"),
(_outer.grayscale ? "true" : "false"), (_outer.invertBrightness ? "true" : "false"),

View File

@ -51,7 +51,7 @@ if (!%s && t.y < 0.0) {
args.fOutputColor, args.fOutputColor,
args.fUniformHandler->getUniformCStr(leftBorderColorVar), args.fOutputColor,
args.fUniformHandler->getUniformCStr(rightBorderColorVar));
SkString _coords1871("float2(half2(t.x, 0))");
SkString _coords1871("float2(half2(t.x, 0.0))");
SkString _sample1871 = this->invokeChild(0, args, _coords1871.c_str());
fragBuilder->codeAppendf(
R"SkSL(

View File

@ -49,7 +49,7 @@ if (!%s && t.y < 0.0) {
_sample453.c_str(),
(_outer.childProcessor(1)->preservesOpaqueInput() ? "true" : "false"),
args.fOutputColor, (_outer.mirror ? "true" : "false"));
SkString _coords1451("float2(half2(t.x, 0))");
SkString _coords1451("float2(half2(t.x, 0.0))");
SkString _sample1451 = this->invokeChild(0, args, _coords1451.c_str());
fragBuilder->codeAppendf(
R"SkSL(

View File

@ -1090,11 +1090,7 @@ void Compiler::simplifyExpression(DefinitionMap& definitions,
Swizzle& base = s.fBase->as<Swizzle>();
std::vector<int> final;
for (int c : s.fComponents) {
if (c == SKSL_SWIZZLE_0 || c == SKSL_SWIZZLE_1) {
final.push_back(c);
} else {
final.push_back(base.fComponents[c]);
}
final.push_back(base.fComponents[c]);
}
*outUpdated = true;
std::unique_ptr<Expression> replacement(new Swizzle(*fContext, base.fBase->clone(),

View File

@ -862,189 +862,12 @@ void GLSLCodeGenerator::writeFieldAccess(const FieldAccess& f) {
}
}
void GLSLCodeGenerator::writeConstantSwizzle(const Swizzle& swizzle, const String& constants) {
this->writeType(swizzle.type());
this->write("(");
this->write(constants);
this->write(")");
}
void GLSLCodeGenerator::writeSwizzleMask(const Swizzle& swizzle, const String& mask) {
void GLSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
this->write(".");
this->write(mask);
}
void GLSLCodeGenerator::writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
const String& mask,
GLSLCodeGenerator::SwizzleOrder order) {
this->writeType(swizzle.type());
this->write("(");
if (order == SwizzleOrder::CONSTANTS_FIRST) {
this->write(constants);
this->write(", ");
this->writeSwizzleMask(swizzle, mask);
} else {
this->writeSwizzleMask(swizzle, mask);
this->write(", ");
this->write(constants);
}
this->write(")");
}
void GLSLCodeGenerator::writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
const String& mask, const String& reswizzle) {
this->writeSwizzleConstructor(swizzle, constants, mask, SwizzleOrder::MASK_FIRST);
this->write(".");
this->write(reswizzle);
}
// Writing a swizzle is complicated due to the handling of constant swizzle components. The most
// problematic case is a mask like '.r00a'. A naive approach might turn that into
// 'vec4(base.r, 0, 0, base.a)', but that would cause 'base' to be evaluated twice. We instead
// group the swizzle mask ('ra') and constants ('0, 0') together and use a secondary swizzle to put
// them back into the right order, so in this case we end up with something like
// 'vec4(base4.ra, 0, 0).rbag'.
void GLSLCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
// has a 1 bit in every position for which the swizzle mask is a constant, so 'r0b1' would
// yield binary 0101.
int constantBits = 0;
String mask;
String constants;
// compute mask ("ra") and constant ("0, 0") strings, and fill in constantBits
for (int c : swizzle.fComponents) {
constantBits <<= 1;
switch (c) {
case SKSL_SWIZZLE_0:
constantBits |= 1;
if (constants.length() > 0) {
constants += ", ";
}
constants += "0";
break;
case SKSL_SWIZZLE_1:
constantBits |= 1;
if (constants.length() > 0) {
constants += ", ";
}
constants += "1";
break;
case 0:
mask += "x";
break;
case 1:
mask += "y";
break;
case 2:
mask += "z";
break;
case 3:
mask += "w";
break;
default:
SkASSERT(false);
}
}
switch (swizzle.fComponents.size()) {
case 1:
if (constantBits == 1) {
this->write(constants);
}
else {
this->writeSwizzleMask(swizzle, mask);
}
break;
case 2:
switch (constantBits) {
case 0: // 00
this->writeSwizzleMask(swizzle, mask);
break;
case 1: // 01
this->writeSwizzleConstructor(swizzle, constants, mask,
SwizzleOrder::MASK_FIRST);
break;
case 2: // 10
this->writeSwizzleConstructor(swizzle, constants, mask,
SwizzleOrder::CONSTANTS_FIRST);
break;
case 3: // 11
this->writeConstantSwizzle(swizzle, constants);
break;
default:
SkASSERT(false);
}
break;
case 3:
switch (constantBits) {
case 0: // 000
this->writeSwizzleMask(swizzle, mask);
break;
case 1: // 001
case 3: // 011
this->writeSwizzleConstructor(swizzle, constants, mask,
SwizzleOrder::MASK_FIRST);
break;
case 4: // 100
case 6: // 110
this->writeSwizzleConstructor(swizzle, constants, mask,
SwizzleOrder::CONSTANTS_FIRST);
break;
case 2: // 010
this->writeSwizzleConstructor(swizzle, constants, mask, "xzy");
break;
case 5: // 101
this->writeSwizzleConstructor(swizzle, constants, mask, "yxz");
break;
case 7: // 111
this->writeConstantSwizzle(swizzle, constants);
break;
}
break;
case 4:
switch (constantBits) {
case 0: // 0000
this->writeSwizzleMask(swizzle, mask);
break;
case 1: // 0001
case 3: // 0011
case 7: // 0111
this->writeSwizzleConstructor(swizzle, constants, mask,
SwizzleOrder::MASK_FIRST);
break;
case 8: // 1000
case 12: // 1100
case 14: // 1110
this->writeSwizzleConstructor(swizzle, constants, mask,
SwizzleOrder::CONSTANTS_FIRST);
break;
case 2: // 0010
this->writeSwizzleConstructor(swizzle, constants, mask, "xywz");
break;
case 4: // 0100
this->writeSwizzleConstructor(swizzle, constants, mask, "xwyz");
break;
case 5: // 0101
this->writeSwizzleConstructor(swizzle, constants, mask, "xzyw");
break;
case 6: // 0110
this->writeSwizzleConstructor(swizzle, constants, mask, "xzwy");
break;
case 9: // 1001
this->writeSwizzleConstructor(swizzle, constants, mask, "zxyw");
break;
case 10: // 1010
this->writeSwizzleConstructor(swizzle, constants, mask, "zxwy");
break;
case 11: // 1011
this->writeSwizzleConstructor(swizzle, constants, mask, "yxzw");
break;
case 13: // 1101
this->writeSwizzleConstructor(swizzle, constants, mask, "yzxw");
break;
case 15: // 1111
this->writeConstantSwizzle(swizzle, constants);
break;
}
SkASSERT(c >= 0 && c <= 3);
this->write(&("x\0y\0z\0w\0"[c * 2]));
}
}

View File

@ -83,11 +83,6 @@ public:
bool generateCode() override;
protected:
enum class SwizzleOrder {
MASK_FIRST,
CONSTANTS_FIRST
};
void write(const char* s);
void writeLine();
@ -158,15 +153,6 @@ protected:
virtual void writeFieldAccess(const FieldAccess& f);
void writeConstantSwizzle(const Swizzle& swizzle, const String& constants);
void writeSwizzleMask(const Swizzle& swizzle, const String& mask);
void writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
const String& mask, SwizzleOrder order);
void writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
const String& mask, const String& reswizzle);
virtual void writeSwizzle(const Swizzle& swizzle);
static Precedence GetBinaryPrecedence(Token::Kind op);

View File

@ -2483,53 +2483,45 @@ std::unique_ptr<Expression> IRGenerator::convertField(std::unique_ptr<Expression
return nullptr;
}
// counts the number of chunks of contiguous 'x's in a swizzle, e.g. xxx1 has one and x0xx has two
static int count_contiguous_swizzle_chunks(const std::vector<int>& components) {
int chunkCount = 0;
for (size_t i = 0; i < components.size(); ++i) {
SkASSERT(components[i] <= 0);
if (components[i] == 0) {
++chunkCount;
while (i + 1 < components.size() && components[i + 1] == 0) {
++i;
}
}
}
return chunkCount;
}
// Swizzles are complicated due to constant components. The most difficult case is a mask like
// '.x00w'. A naive approach might turn that into 'float4(base.x, 0, 0, base.w)', but that evaluates
// 'base' twice. We instead group the swizzle mask ('xw') and constants ('0, 0') together and use a
// secondary swizzle to put them back into the right order, so in this case we end up with
// 'float4(base.xw, 0, 0).xzwy'.
std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expression> base,
StringFragment fields) {
const int offset = base->fOffset;
const Type& baseType = base->type();
if (baseType.typeKind() != Type::TypeKind::kVector && !baseType.isNumber()) {
fErrors.error(base->fOffset, "cannot swizzle value of type '" + baseType.displayName() +
"'");
fErrors.error(offset, "cannot swizzle value of type '" + baseType.displayName() + "'");
return nullptr;
}
std::vector<int> swizzleComponents;
size_t numLiteralFields = 0;
if (fields.fLength > 4) {
fErrors.error(offset, "too many components in swizzle mask '" + fields + "'");
return nullptr;
}
std::vector<int> maskComponents;
std::vector<int> constants;
for (size_t i = 0; i < fields.fLength; i++) {
switch (fields[i]) {
case '0':
swizzleComponents.push_back(SKSL_SWIZZLE_0);
numLiteralFields++;
break;
case '1':
swizzleComponents.push_back(SKSL_SWIZZLE_1);
numLiteralFields++;
constants.push_back(fields[i] - '0');
break;
case 'x':
case 'r':
case 's':
case 'L':
swizzleComponents.push_back(0);
maskComponents.push_back(0);
break;
case 'y':
case 'g':
case 't':
case 'T':
if (baseType.columns() >= 2) {
swizzleComponents.push_back(1);
maskComponents.push_back(1);
break;
}
[[fallthrough]];
@ -2538,7 +2530,7 @@ std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expressi
case 'p':
case 'R':
if (baseType.columns() >= 3) {
swizzleComponents.push_back(2);
maskComponents.push_back(2);
break;
}
[[fallthrough]];
@ -2547,104 +2539,74 @@ std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expressi
case 'q':
case 'B':
if (baseType.columns() >= 4) {
swizzleComponents.push_back(3);
maskComponents.push_back(3);
break;
}
[[fallthrough]];
default:
fErrors.error(base->fOffset, String::printf("invalid swizzle component '%c'",
fields[i]));
fErrors.error(offset, String::printf("invalid swizzle component '%c'", fields[i]));
return nullptr;
}
}
SkASSERT(swizzleComponents.size() > 0);
if (swizzleComponents.size() > 4) {
fErrors.error(base->fOffset, "too many components in swizzle mask '" + fields + "'");
return nullptr;
}
if (numLiteralFields == swizzleComponents.size()) {
fErrors.error(base->fOffset, "swizzle must refer to base expression");
if (maskComponents.empty()) {
fErrors.error(offset, "swizzle must refer to base expression");
return nullptr;
}
// First, we need a vector expression that is the non-constant portion of the swizzle, packed:
// scalar.xxx -> type3(scalar)
// scalar.x0x0 -> type2(scalar)
// vector.zyx -> vector.zyx
// vector.x0y0 -> vector.xy
std::unique_ptr<Expression> expr;
if (baseType.isNumber()) {
// Swizzling a single scalar. Something like foo.x0x1 is equivalent to float4(foo, 0, foo,
// 1)
int offset = base->fOffset;
std::unique_ptr<Expression> expr;
switch (base->kind()) {
case Expression::Kind::kVariableReference:
case Expression::Kind::kFloatLiteral:
case Expression::Kind::kIntLiteral:
// the value being swizzled is just a constant or variable reference, so we can
// safely re-use copies of it without reevaluation concerns
expr = std::move(base);
break;
default:
// It's a value we can't safely re-use multiple times. If it's all in one contiguous
// chunk it's easy (e.g. foo.xxx0 can be turned into half4(half3(x), 0)), but
// for multiple discontiguous chunks we'll need to copy it into a temporary value.
int chunkCount = count_contiguous_swizzle_chunks(swizzleComponents);
if (chunkCount <= 1) {
// no copying needed, so we can just use the value directly
expr = std::move(base);
} else {
// store the value in a temporary variable so we can re-use it
int varIndex = fTmpSwizzleCounter++;
auto name = std::make_unique<String>();
name->appendf("_tmpSwizzle%d", varIndex);
const String* namePtr = fSymbolTable->takeOwnershipOfString(std::move(name));
const Variable* var = fSymbolTable->takeOwnershipOfSymbol(
std::make_unique<Variable>(offset,
Modifiers(),
namePtr->c_str(),
&baseType,
Variable::kLocal_Storage,
base.get()));
expr = std::make_unique<VariableReference>(offset, *var);
std::vector<std::unique_ptr<VarDeclaration>> variables;
variables.emplace_back(new VarDeclaration(var, {}, std::move(base)));
fExtraStatements.emplace_back(new VarDeclarationsStatement(
std::make_unique<VarDeclarations>(offset, &expr->type(),
std::move(variables))));
}
}
std::vector<std::unique_ptr<Expression>> args;
for (size_t i = 0; i < swizzleComponents.size(); ++i) {
switch (swizzleComponents[i]) {
case 0: {
args.push_back(expr->clone());
int count = 1;
while (i + 1 < swizzleComponents.size() && swizzleComponents[i + 1] == 0) {
++i;
++count;
}
if (count > 1) {
std::vector<std::unique_ptr<Expression>> constructorArgs;
constructorArgs.push_back(std::move(args.back()));
args.pop_back();
args.emplace_back(new Constructor(offset, &expr->type().toCompound(fContext,
count,
1),
std::move(constructorArgs)));
}
break;
}
case SKSL_SWIZZLE_0:
args.emplace_back(new IntLiteral(fContext, offset, 0));
break;
case SKSL_SWIZZLE_1:
args.emplace_back(new IntLiteral(fContext, offset, 1));
break;
}
}
return std::unique_ptr<Expression>(new Constructor(offset,
&expr->type().toCompound(
fContext,
swizzleComponents.size(),
1),
std::move(args)));
std::vector<std::unique_ptr<Expression>> scalarConstructorArgs;
scalarConstructorArgs.push_back(std::move(base));
expr = std::make_unique<Constructor>(
offset, &baseType.toCompound(fContext, maskComponents.size(), 1),
std::move(scalarConstructorArgs));
} else {
expr = std::make_unique<Swizzle>(fContext, std::move(base), maskComponents);
}
return std::unique_ptr<Expression>(new Swizzle(fContext, std::move(base), swizzleComponents));
// If there are no constants, we're done
if (constants.empty()) {
return expr;
}
// Now we create a constructor that has the correct number of elements for the final swizzle,
// with all fields at the start, and all constants at the end:
// scalar.x0x0 -> type4(type2(x), 0, 0)
// vector.z10x -> type4(vector.zx, 1, 0)
//
// NOTE: We could create simpler IR in some cases by reordering here, if all fields are packed
// contiguously. The benefits are minor, so skip the optimization to keep the algorithm simple.
std::vector<std::unique_ptr<Expression>> constructorArgs;
constructorArgs.push_back(std::move(expr));
const Type* numberType = baseType.isNumber() ? &baseType : &baseType.componentType();
for (int constant : constants) {
constructorArgs.push_back(this->coerce(
std::make_unique<IntLiteral>(fContext, offset, constant), *numberType));
}
expr = std::make_unique<Constructor>(offset,
&numberType->toCompound(fContext, fields.fLength, 1),
std::move(constructorArgs));
// Now, we apply another swizzle to shuffle the constants into the correct place. For common
// uses ('.xyz0', '.xyz1'), this produces an identity swizzle, which is optimized away later.
std::vector<int> shuffleComponents;
int curFieldIdx = 0,
curConstIdx = maskComponents.size();
for (size_t i = 0; i < fields.fLength; i++) {
if (fields[i] == '0' || fields[i] == '1') {
shuffleComponents.push_back(curConstIdx++);
} else {
shuffleComponents.push_back(curFieldIdx++);
}
}
return std::make_unique<Swizzle>(fContext, std::move(expr), std::move(shuffleComponents));
}
std::unique_ptr<Expression> IRGenerator::getCap(int offset, String name) {
@ -2813,10 +2775,6 @@ void IRGenerator::checkValid(const Expression& expr) {
bool IRGenerator::checkSwizzleWrite(const Swizzle& swizzle) {
int bits = 0;
for (int idx : swizzle.fComponents) {
if (idx < 0) {
fErrors.error(swizzle.fOffset, "cannot write to a swizzle mask containing a constant");
return false;
}
SkASSERT(idx <= 3);
int bit = 1 << idx;
if (bits & bit) {

View File

@ -746,23 +746,11 @@ void MetalCodeGenerator::writeFieldAccess(const FieldAccess& f) {
}
void MetalCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
int last = swizzle.fComponents.back();
if (last == SKSL_SWIZZLE_0 || last == SKSL_SWIZZLE_1) {
this->writeType(swizzle.type());
this->write("(");
}
this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
this->write(".");
for (int c : swizzle.fComponents) {
if (c >= 0) {
this->write(&("x\0y\0z\0w\0"[c * 2]));
}
}
if (last == SKSL_SWIZZLE_0) {
this->write(", 0)");
}
else if (last == SKSL_SWIZZLE_1) {
this->write(", 1)");
SkASSERT(c >= 0 && c <= 3);
this->write(&("x\0y\0z\0w\0"[c * 2]));
}
}

View File

@ -1980,36 +1980,9 @@ SpvId SPIRVCodeGenerator::writeSwizzle(const Swizzle& swizzle, OutputStream& out
this->writeWord(this->getType(swizzle.type()), out);
this->writeWord(result, out);
this->writeWord(base, out);
SpvId other = base;
for (int c : swizzle.fComponents) {
if (c < 0) {
if (!fConstantZeroOneVector) {
FloatLiteral zero(fContext, -1, 0);
SpvId zeroId = this->writeFloatLiteral(zero);
FloatLiteral one(fContext, -1, 1);
SpvId oneId = this->writeFloatLiteral(one);
SpvId type = this->getType(*fContext.fFloat2_Type);
fConstantZeroOneVector = this->nextId();
this->writeOpCode(SpvOpConstantComposite, 5, fConstantBuffer);
this->writeWord(type, fConstantBuffer);
this->writeWord(fConstantZeroOneVector, fConstantBuffer);
this->writeWord(zeroId, fConstantBuffer);
this->writeWord(oneId, fConstantBuffer);
}
other = fConstantZeroOneVector;
break;
}
}
this->writeWord(other, out);
int baseColumns = swizzle.fBase->type().columns();
this->writeWord(base, out);
for (int component : swizzle.fComponents) {
if (component == SKSL_SWIZZLE_0) {
this->writeWord(baseColumns, out);
} else if (component == SKSL_SWIZZLE_1) {
this->writeWord(baseColumns + 1, out);
} else {
this->writeWord(component, out);
}
this->writeWord(component, out);
}
}
return result;

View File

@ -387,8 +387,6 @@ private:
SpvId fBoolTrue;
SpvId fBoolFalse;
std::unordered_map<std::pair<ConstantValue, ConstantType>, SpvId> fNumberConstants;
// The constant float2(0, 1), used in swizzling
SpvId fConstantZeroOneVector = 0;
bool fSetupFragPosition;
// label of the current block, or 0 if we are not in a block
SpvId fCurrentBlock;

View File

@ -16,12 +16,6 @@
namespace SkSL {
// represents a swizzle component of constant 0, as in x.rgb0
const int SKSL_SWIZZLE_0 = -2;
// represents a swizzle component of constant 1, as in x.rgb1
const int SKSL_SWIZZLE_1 = -1;
/**
* Given a type and a swizzle component count, returns the type that will result from swizzling. For
* instance, swizzling a float3 with two components will result in a float2. It is possible to
@ -140,7 +134,7 @@ struct Swizzle : public Expression {
String description() const override {
String result = fBase->description() + ".";
for (int x : fComponents) {
result += "01xyzw"[x + 2];
result += "xyzw"[x];
}
return result;
}

View File

@ -203,7 +203,7 @@ DEF_TEST(SkSLSwizzleDuplicateOutput, r) {
DEF_TEST(SkSLSwizzleConstantOutput, r) {
test_failure(r,
"void main() { float4 test = float4(1); test.xyz0 = float4(1); }",
"error: 1: cannot write to a swizzle mask containing a constant\n1 error\n");
"error: 1: cannot assign to this expression\n1 error\n");
}
DEF_TEST(SkSLSwizzleOnlyLiterals, r) {

View File

@ -1210,30 +1210,30 @@ DEF_TEST(SkSLSwizzleConstants, r) {
" vec4 v = vec4(sqrt(1.0));\n"
" sk_FragColor = vec4(v.x, 1.0, 1.0, 1.0);\n"
" sk_FragColor = vec4(v.xy, 1.0, 1.0);\n"
" sk_FragColor = vec4(vec2(v.x, 1), 1.0, 1.0);\n"
" sk_FragColor = vec4(vec2(0, v.y), 1.0, 1.0);\n"
" sk_FragColor = vec4(vec2(v.x, 1.0), 1.0, 1.0);\n"
" sk_FragColor = vec4(vec2(v.y, 0.0).yx, 1.0, 1.0);\n"
" sk_FragColor = vec4(v.xyz, 1.0);\n"
" sk_FragColor = vec4(vec3(v.xy, 1), 1.0);\n"
" sk_FragColor = vec4(vec3(v.xz, 0).xzy, 1.0);\n"
" sk_FragColor = vec4(vec3(v.x, 1, 0), 1.0);\n"
" sk_FragColor = vec4(vec3(1, v.yz), 1.0);\n"
" sk_FragColor = vec4(vec3(v.y, 0, 1).yxz, 1.0);\n"
" sk_FragColor = vec4(vec3(1, 1, v.z), 1.0);\n"
" sk_FragColor = vec4(vec3(v.xy, 1.0), 1.0);\n"
" sk_FragColor = vec4(vec3(v.xz, 0.0).xzy, 1.0);\n"
" sk_FragColor = vec4(vec3(v.x, 1.0, 0.0), 1.0);\n"
" sk_FragColor = vec4(vec3(v.yz, 1.0).zxy, 1.0);\n"
" sk_FragColor = vec4(vec3(v.y, 0.0, 1.0).yxz, 1.0);\n"
" sk_FragColor = vec4(vec3(v.z, 1.0, 1.0).yzx, 1.0);\n"
" sk_FragColor = v;\n"
" sk_FragColor = vec4(v.xyz, 1);\n"
" sk_FragColor = vec4(v.xyw, 0).xywz;\n"
" sk_FragColor = vec4(v.xy, 1, 0);\n"
" sk_FragColor = vec4(v.xzw, 1).xwyz;\n"
" sk_FragColor = vec4(v.xz, 0, 1).xzyw;\n"
" sk_FragColor = vec4(v.xw, 1, 1).xzwy;\n"
" sk_FragColor = vec4(v.x, 1, 0, 1);\n"
" sk_FragColor = vec4(1, v.yzw);\n"
" sk_FragColor = vec4(v.yz, 0, 1).zxyw;\n"
" sk_FragColor = vec4(v.yw, 0, 1).zxwy;\n"
" sk_FragColor = vec4(v.y, 1, 1, 1).yxzw;\n"
" sk_FragColor = vec4(0, 0, v.zw);\n"
" sk_FragColor = vec4(v.z, 0, 0, 1).yzxw;\n"
" sk_FragColor = vec4(0, 1, 1, v.w);\n"
" sk_FragColor = vec4(v.xyz, 1.0);\n"
" sk_FragColor = vec4(v.xyw, 0.0).xywz;\n"
" sk_FragColor = vec4(v.xy, 1.0, 0.0);\n"
" sk_FragColor = vec4(v.xzw, 1.0).xwyz;\n"
" sk_FragColor = vec4(v.xz, 0.0, 1.0).xzyw;\n"
" sk_FragColor = vec4(v.xw, 1.0, 1.0).xzwy;\n"
" sk_FragColor = vec4(v.x, 1.0, 0.0, 1.0);\n"
" sk_FragColor = vec4(v.yzw, 1.0).wxyz;\n"
" sk_FragColor = vec4(v.yz, 0.0, 1.0).zxyw;\n"
" sk_FragColor = vec4(v.yw, 0.0, 1.0).zxwy;\n"
" sk_FragColor = vec4(v.y, 1.0, 1.0, 1.0).yxzw;\n"
" sk_FragColor = vec4(v.zw, 0.0, 0.0).zwxy;\n"
" sk_FragColor = vec4(v.z, 0.0, 0.0, 1.0).yzxw;\n"
" sk_FragColor = vec4(v.w, 0.0, 1.0, 1.0).yzwx;\n"
"}\n",
SkSL::Program::kFragment_Kind
);
@ -1258,10 +1258,10 @@ DEF_TEST(SkSLSwizzleOpt, r) {
"void main() {\n"
" float v = sqrt(1.0);\n"
" sk_FragColor = vec4(v);\n"
" sk_FragColor = vec4(0, vec4(v).zyx);\n"
" sk_FragColor = vec4(0, 0, vec4(v).xw);\n"
" sk_FragColor = vec4(1, 1, vec4(v).wx);\n"
" sk_FragColor = vec4(vec4(v).zy, 1, 1);\n"
" sk_FragColor = vec4(vec4(v).xyz, 0.0).wzyx;\n"
" sk_FragColor = vec4(vec4(v).xw, 0.0, 0.0).zwxy;\n"
" sk_FragColor = vec4(vec4(vec4(v).xw, 0.0, 0.0).yx, 1.0, 1.0).zwxy;\n"
" sk_FragColor = vec4(vec4(v).zy, 1.0, 1.0);\n"
" sk_FragColor = vec4(v);\n"
" sk_FragColor = vec4(vec4(v).xx, 1.0, 1.0);\n"
" sk_FragColor = vec4(v).wzwz;\n"
@ -1281,7 +1281,7 @@ DEF_TEST(SkSLSwizzleScalar, r) {
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float x = sqrt(4.0);\n"
" sk_FragColor = vec4(vec2(x), 0, 1);\n"
" sk_FragColor = vec4(vec2(x), 0.0, 1.0);\n"
"}\n");
test(r,
"void main() {"
@ -1291,7 +1291,7 @@ DEF_TEST(SkSLSwizzleScalar, r) {
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" sk_FragColor = vec4(vec2(sqrt(4.0)), 0, 1);\n"
" sk_FragColor = vec4(vec2(sqrt(4.0)), 0.0, 1.0);\n"
"}\n");
test(r,
"void main() {"
@ -1301,7 +1301,7 @@ DEF_TEST(SkSLSwizzleScalar, r) {
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" sk_FragColor = vec4(0, sqrt(4.0), 0, 1);\n"
" sk_FragColor = vec4(sqrt(4.0), 0.0, 0.0, 1.0).yxzw;\n"
"}\n");
test(r,
"void main() {"
@ -1311,9 +1311,7 @@ DEF_TEST(SkSLSwizzleScalar, r) {
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float _tmpSwizzle0 = sqrt(4.0);\n"
" sk_FragColor = vec4(0, _tmpSwizzle0, 0, _tmpSwizzle0);\n"
"\n"
" sk_FragColor = vec4(vec2(sqrt(4.0)), 0.0, 0.0).zxwy;\n"
"}\n");
}

View File

@ -291,7 +291,7 @@ DEF_TEST(SkSLMetalConstantSwizzle, r) {
"fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {\n"
" Outputs _outputStruct;\n"
" thread Outputs* _out = &_outputStruct;\n"
" _out->sk_FragColor = float4(float4(0.5).xyz, 1);\n"
" _out->sk_FragColor = float4(float4(0.5).xyz, 1.0);\n"
" return *_out;\n"
"}\n");
}