Moved swizzle error checking into Swizzle::Convert

Change-Id: Id2676ffaba1dafc4a0485d99e1c5b87b990e0861
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/426976
Reviewed-by: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
This commit is contained in:
Ethan Nicholas 2021-07-12 12:28:57 -04:00 committed by Skia Commit-Bot
parent df3edc570f
commit 59ff4e2b78
4 changed files with 161 additions and 148 deletions

View File

@ -14,7 +14,6 @@
#include "include/private/SkSLLayout.h"
#include "include/private/SkTArray.h"
#include "include/private/SkTOptional.h"
#include "include/sksl/DSLCore.h"
#include "src/core/SkScopeExit.h"
#include "src/sksl/SkSLAnalysis.h"
@ -1410,147 +1409,9 @@ std::unique_ptr<Expression> IRGenerator::convertPrefixExpression(const ASTNode&
return PrefixExpression::Convert(fContext, expression.getOperator(), std::move(base));
}
static bool validate_swizzle_domain(skstd::string_view fields) {
enum SwizzleDomain {
kCoordinate,
kColor,
kUV,
kRectangle,
};
skstd::optional<SwizzleDomain> domain;
for (char field : fields) {
SwizzleDomain fieldDomain;
switch (field) {
case 'x':
case 'y':
case 'z':
case 'w':
fieldDomain = kCoordinate;
break;
case 'r':
case 'g':
case 'b':
case 'a':
fieldDomain = kColor;
break;
case 's':
case 't':
case 'p':
case 'q':
fieldDomain = kUV;
break;
case 'L':
case 'T':
case 'R':
case 'B':
fieldDomain = kRectangle;
break;
case '0':
case '1':
continue;
default:
return false;
}
if (!domain.has_value()) {
domain = fieldDomain;
} else if (domain != fieldDomain) {
return false;
}
}
return true;
}
// Swizzles are complicated due to constant components. The most difficult case is a mask like
// '.x1w0'. A naive approach might turn that into 'float4(base.x, 1, base.w, 0)', but that evaluates
// 'base' twice. We instead group the swizzle mask ('xw') and constants ('1, 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, 1, 0).xzyw'.
std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expression> base,
skstd::string_view fields) {
const int offset = base->fOffset;
const Type& baseType = base->type();
if (!baseType.isVector() && !baseType.isNumber()) {
this->errorReporter().error(
offset, "cannot swizzle value of type '" + baseType.displayName() + "'");
return nullptr;
}
if (fields.length() > 4) {
this->errorReporter().error(offset, "too many components in swizzle mask '" + fields + "'");
return nullptr;
}
if (!validate_swizzle_domain(fields)) {
this->errorReporter().error(offset, "invalid swizzle mask '" + fields + "'");
return nullptr;
}
ComponentArray components;
bool foundXYZW = false;
for (char field : fields) {
switch (field) {
case '0':
components.push_back(SwizzleComponent::ZERO);
break;
case '1':
components.push_back(SwizzleComponent::ONE);
break;
case 'x':
case 'r':
case 's':
case 'L':
components.push_back(SwizzleComponent::X);
foundXYZW = true;
break;
case 'y':
case 'g':
case 't':
case 'T':
if (baseType.columns() >= 2) {
components.push_back(SwizzleComponent::Y);
foundXYZW = true;
break;
}
[[fallthrough]];
case 'z':
case 'b':
case 'p':
case 'R':
if (baseType.columns() >= 3) {
components.push_back(SwizzleComponent::Z);
foundXYZW = true;
break;
}
[[fallthrough]];
case 'w':
case 'a':
case 'q':
case 'B':
if (baseType.columns() >= 4) {
components.push_back(SwizzleComponent::W);
foundXYZW = true;
break;
}
// The swizzle component references a field that doesn't exist in the base type.
this->errorReporter().error(
offset, String::printf("invalid swizzle component '%c'", field));
return nullptr;
default:
SkDEBUGFAIL("unexpected swizzle component");
return nullptr;
}
}
if (!foundXYZW) {
this->errorReporter().error(offset, "swizzle must refer to base expression");
return nullptr;
}
return Swizzle::Convert(fContext, std::move(base), components);
return Swizzle::Convert(fContext, std::move(base), fields);
}
std::unique_ptr<Expression> IRGenerator::convertIndexExpression(const ASTNode& index) {

View File

@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "include/private/SkTOptional.h"
#include "src/sksl/SkSLConstantFolder.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
@ -13,51 +14,193 @@
namespace SkSL {
static bool validate_swizzle_domain(skstd::string_view fields) {
enum SwizzleDomain {
kCoordinate,
kColor,
kUV,
kRectangle,
};
skstd::optional<SwizzleDomain> domain;
for (char field : fields) {
SwizzleDomain fieldDomain;
switch (field) {
case 'x':
case 'y':
case 'z':
case 'w':
fieldDomain = kCoordinate;
break;
case 'r':
case 'g':
case 'b':
case 'a':
fieldDomain = kColor;
break;
case 's':
case 't':
case 'p':
case 'q':
fieldDomain = kUV;
break;
case 'L':
case 'T':
case 'R':
case 'B':
fieldDomain = kRectangle;
break;
case '0':
case '1':
continue;
default:
return false;
}
if (!domain.has_value()) {
domain = fieldDomain;
} else if (domain != fieldDomain) {
return false;
}
}
return true;
}
std::unique_ptr<Expression> Swizzle::Convert(const Context& context,
std::unique_ptr<Expression> base,
skstd::string_view maskString) {
if (!validate_swizzle_domain(maskString)) {
context.fErrors.error(base->fOffset, "invalid swizzle mask '" + maskString + "'");
return nullptr;
}
ComponentArray components;
for (char field : maskString) {
switch (field) {
case '0':
components.push_back(SwizzleComponent::ZERO);
break;
case '1':
components.push_back(SwizzleComponent::ONE);
break;
case 'x':
case 'r':
case 's':
case 'L':
components.push_back(SwizzleComponent::X);
break;
case 'y':
case 'g':
case 't':
case 'T':
components.push_back(SwizzleComponent::Y);
break;
case 'z':
case 'b':
case 'p':
case 'R':
components.push_back(SwizzleComponent::Z);
break;
case 'w':
case 'a':
case 'q':
case 'B':
components.push_back(SwizzleComponent::W);
break;
default:
SkDEBUGFAIL("unexpected swizzle component");
return nullptr;
}
}
return Convert(context, std::move(base), std::move(components), maskString);
}
std::unique_ptr<Expression> Swizzle::Convert(const Context& context,
std::unique_ptr<Expression> base,
ComponentArray inComponents) {
return Convert(context, std::move(base), std::move(inComponents), "");
}
// Swizzles are complicated due to constant components. The most difficult case is a mask like
// '.x1w0'. A naive approach might turn that into 'float4(base.x, 1, base.w, 0)', but that evaluates
// 'base' twice. We instead group the swizzle mask ('xw') and constants ('1, 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, 1, 0).xzyw'.
std::unique_ptr<Expression> Swizzle::Convert(const Context& context,
std::unique_ptr<Expression> base,
ComponentArray inComponents,
skstd::string_view maskString) {
SkASSERT(maskString.empty() || (int) maskString.length() == inComponents.count());
const int offset = base->fOffset;
const Type& baseType = base->type();
// The IRGenerator is responsible for enforcing these invariants.
SkASSERTF(baseType.isVector() || baseType.isScalar(),
"cannot swizzle type '%s'", baseType.description().c_str());
SkASSERT(inComponents.count() >= 1 && inComponents.count() <= 4);
if (!baseType.isVector() && !baseType.isNumber()) {
context.fErrors.error(
offset, "cannot swizzle value of type '" + baseType.displayName() + "'");
return nullptr;
}
if (inComponents.count() > 4) {
String error = "too many components in swizzle mask";
if (!maskString.empty()) {
error += " '" + maskString + "'";
}
context.fErrors.error(offset, error.c_str());
return nullptr;
}
ComponentArray maskComponents;
for (int8_t component : inComponents) {
switch (component) {
bool foundXYZW = false;
for (int i = 0; i < inComponents.count(); ++i) {
switch (inComponents[i]) {
case SwizzleComponent::ZERO:
case SwizzleComponent::ONE:
// Skip over constant fields for now.
break;
case SwizzleComponent::X:
foundXYZW = true;
maskComponents.push_back(SwizzleComponent::X);
break;
case SwizzleComponent::Y:
foundXYZW = true;
if (baseType.columns() >= 2) {
maskComponents.push_back(SwizzleComponent::Y);
break;
}
[[fallthrough]];
case SwizzleComponent::Z:
foundXYZW = true;
if (baseType.columns() >= 3) {
maskComponents.push_back(SwizzleComponent::Z);
break;
}
[[fallthrough]];
case SwizzleComponent::W:
foundXYZW = true;
if (baseType.columns() >= 4) {
maskComponents.push_back(SwizzleComponent::W);
break;
}
[[fallthrough]];
default:
SkDEBUGFAILF("invalid swizzle component %d", component);
// The swizzle component references a field that doesn't exist in the base type.
context.fErrors.error(offset,
maskString.empty() ? "invalid swizzle component"
: String::printf("invalid swizzle component '%c'",
maskString[i]));
return nullptr;
}
}
if (!foundXYZW) {
context.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)

View File

@ -39,6 +39,10 @@ struct Swizzle final : public Expression {
std::unique_ptr<Expression> base,
ComponentArray inComponents);
static std::unique_ptr<Expression> Convert(const Context& context,
std::unique_ptr<Expression> base,
skstd::string_view maskString);
// Swizzle::Make does not permit ZERO or ONE in the component array, just X/Y/Z/W; errors are
// reported via ASSERT.
static std::unique_ptr<Expression> Make(const Context& context,
@ -75,6 +79,11 @@ struct Swizzle final : public Expression {
}
private:
static std::unique_ptr<Expression> Convert(const Context& context,
std::unique_ptr<Expression> base,
ComponentArray inComponents,
skstd::string_view maskString);
Swizzle(const Type* type, std::unique_ptr<Expression> base, const ComponentArray& components)
: INHERITED(base->fOffset, kExpressionKind, type)
, fBase(std::move(base))

View File

@ -3,6 +3,6 @@
error: 3: type 'S' does not have a field named 'missing'
error: 4: not a function
error: 5: type mismatch: '=' cannot operate on 'float', 'bool3'
error: 6: too many components in swizzle mask 'missing'
error: 6: invalid swizzle mask 'missing'
error: 7: expected array, but found 'float'
5 errors