Represent scalar-cast constructors with ConstructorScalarCast.

Change-Id: Iff8477f3797c83059c823ca9287493b7f30db71b
Bug: skia:11032
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/392438
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2021-04-04 22:24:40 -04:00 committed by Skia Commit-Bot
parent 1f56479d6e
commit fd7252fa23
21 changed files with 379 additions and 207 deletions

View File

@ -98,6 +98,8 @@ skia_sksl_sources = [
"$_src/sksl/ir/SkSLConstructorArray.h",
"$_src/sksl/ir/SkSLConstructorDiagonalMatrix.cpp",
"$_src/sksl/ir/SkSLConstructorDiagonalMatrix.h",
"$_src/sksl/ir/SkSLConstructorScalarCast.cpp",
"$_src/sksl/ir/SkSLConstructorScalarCast.h",
"$_src/sksl/ir/SkSLConstructorSplat.cpp",
"$_src/sksl/ir/SkSLConstructorSplat.h",
"$_src/sksl/ir/SkSLContinueStatement.h",

View File

@ -750,7 +750,11 @@ bool Analysis::IsSameExpressionTree(const Expression& left, const Expression& ri
case Expression::Kind::kConstructor:
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorDiagonalMatrix:
case Expression::Kind::kConstructorScalarCast:
case Expression::Kind::kConstructorSplat: {
if (left.kind() != right.kind()) {
return false;
}
const AnyConstructor& leftCtor = left.asAnyConstructor();
const AnyConstructor& rightCtor = right.asAnyConstructor();
const auto leftSpan = leftCtor.argumentSpan();
@ -1020,6 +1024,7 @@ public:
case Expression::Kind::kConstructor:
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorDiagonalMatrix:
case Expression::Kind::kConstructorScalarCast:
case Expression::Kind::kConstructorSplat:
case Expression::Kind::kFieldAccess:
case Expression::Kind::kIndex:
@ -1145,6 +1150,7 @@ template <typename T> bool TProgramVisitor<T>::visitExpression(typename T::Expre
case Expression::Kind::kConstructor:
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorDiagonalMatrix:
case Expression::Kind::kConstructorScalarCast:
case Expression::Kind::kConstructorSplat: {
auto& c = e.asAnyConstructor();
for (auto& arg : c.argumentSpan()) {

View File

@ -18,6 +18,7 @@
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLConstructorSplat.h"
#include "src/sksl/ir/SkSLContinueStatement.h"
#include "src/sksl/ir/SkSLDiscardStatement.h"
@ -302,6 +303,12 @@ void Dehydrator::write(const Expression* e) {
this->writeExpressionSpan(e->as<ConstructorDiagonalMatrix>().argumentSpan());
break;
case Expression::Kind::kConstructorScalarCast:
this->writeCommand(Rehydrator::kConstructorScalarCast_Command);
this->write(e->type());
this->writeExpressionSpan(e->as<ConstructorScalarCast>().argumentSpan());
break;
case Expression::Kind::kConstructorSplat:
this->writeCommand(Rehydrator::kConstructorSplat_Command);
this->write(e->type());

View File

@ -199,7 +199,8 @@ void GLSLCodeGenerator::writeExpression(const Expression& expr, Precedence paren
this->writeBoolLiteral(expr.as<BoolLiteral>());
break;
case Expression::Kind::kConstructor:
this->writeConstructor(expr.as<Constructor>(), parentPrecedence);
case Expression::Kind::kConstructorScalarCast:
this->writeConstructorAsNecessary(expr.asAnyConstructor(), parentPrecedence);
break;
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorDiagonalMatrix:
@ -725,17 +726,19 @@ void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) {
this->write(")");
}
void GLSLCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
if (c.arguments().size() == 1 &&
(this->getTypeName(c.type()) == this->getTypeName(c.arguments()[0]->type()) ||
(c.type().isScalar() &&
c.arguments()[0]->type() == *fContext.fTypes.fFloatLiteral))) {
// in cases like half(float), they're different types as far as SkSL is concerned but the
// same type as far as GLSL is concerned. We avoid a redundant float(float) by just writing
// out the inner expression here.
this->writeExpression(*c.arguments()[0], parentPrecedence);
void GLSLCodeGenerator::writeConstructorAsNecessary(const AnyConstructor& c,
Precedence parentPrecedence) {
const auto arguments = c.argumentSpan();
if (arguments.size() == 1 &&
(this->getTypeName(c.type()) == this->getTypeName(arguments.front()->type()) ||
(arguments.front()->type() == *fContext.fTypes.fFloatLiteral))) {
// In cases like half(float), they're different types as far as SkSL is concerned but
// the same type as far as GLSL is concerned. We avoid a redundant float(float) by just
// writing out the inner expression here.
this->writeExpression(*arguments.front(), parentPrecedence);
return;
}
// This cast should be emitted as-is.
return this->writeAnyConstructor(c, parentPrecedence);
}

View File

@ -21,6 +21,7 @@
#include "src/sksl/ir/SkSLBinaryExpression.h"
#include "src/sksl/ir/SkSLBoolLiteral.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLExtension.h"
#include "src/sksl/ir/SkSLFieldAccess.h"
@ -134,7 +135,7 @@ protected:
virtual void writeFunctionCall(const FunctionCall& c);
void writeConstructor(const Constructor& c, Precedence parentPrecedence);
void writeConstructorAsNecessary(const AnyConstructor& c, Precedence parentPrecedence);
void writeAnyConstructor(const AnyConstructor& c, Precedence parentPrecedence);

View File

@ -19,6 +19,7 @@
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLConstructorSplat.h"
#include "src/sksl/ir/SkSLContinueStatement.h"
#include "src/sksl/ir/SkSLDiscardStatement.h"
@ -327,6 +328,12 @@ std::unique_ptr<Expression> Inliner::inlineExpression(int offset,
*ctor.type().clone(symbolTableForExpression),
expr(ctor.argument()));
}
case Expression::Kind::kConstructorScalarCast: {
const ConstructorScalarCast& ctor = expression.as<ConstructorScalarCast>();
return ConstructorScalarCast::Make(*fContext, offset,
*ctor.type().clone(symbolTableForExpression),
expr(ctor.argument()));
}
case Expression::Kind::kConstructorSplat: {
const ConstructorSplat& ctor = expression.as<ConstructorSplat>();
return ConstructorSplat::Make(*fContext, offset,
@ -930,6 +937,7 @@ public:
case Expression::Kind::kConstructor:
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorDiagonalMatrix:
case Expression::Kind::kConstructorScalarCast:
case Expression::Kind::kConstructorSplat: {
AnyConstructor& constructorExpr = (*expr)->asAnyConstructor();
for (std::unique_ptr<Expression>& arg : constructorExpr.argumentSpan()) {

View File

@ -181,6 +181,9 @@ void MetalCodeGenerator::writeExpression(const Expression& expr, Precedence pare
this->writeSingleArgumentConstructor(expr.as<ConstructorDiagonalMatrix>(),
parentPrecedence);
break;
case Expression::Kind::kConstructorScalarCast:
this->writeConstructorScalarCast(expr.as<ConstructorScalarCast>(), parentPrecedence);
break;
case Expression::Kind::kConstructorSplat:
this->writeSingleArgumentConstructor(expr.as<ConstructorSplat>(), parentPrecedence);
break;
@ -1055,26 +1058,13 @@ void MetalCodeGenerator::writeConstructor(const Constructor& c, Precedence paren
// Handle special cases for single-argument constructors.
if (c.arguments().size() == 1) {
// If the type is coercible, emit it directly.
// (This will no longer be needed when VectorCast is added.)
const Expression& arg = *c.arguments().front();
const Type& argType = arg.type();
if (this->canCoerce(constructorType, argType)) {
this->writeExpression(arg, parentPrecedence);
return;
}
// Metal supports creating matrices with a scalar on the diagonal via the single-argument
// matrix constructor.
if (constructorType.isMatrix() && argType.isNumber()) {
const Type& matrix = constructorType;
this->write("float");
this->write(to_string(matrix.columns()));
this->write("x");
this->write(to_string(matrix.rows()));
this->write("(");
this->writeExpression(arg, parentPrecedence);
this->write(")");
return;
}
}
// Emit and invoke a matrix-constructor helper method if one is necessary.
@ -1140,6 +1130,19 @@ void MetalCodeGenerator::writeConstructorArray(const ConstructorArray& c,
this->write("}");
}
void MetalCodeGenerator::writeConstructorScalarCast(const ConstructorScalarCast& c,
Precedence parentPrecedence) {
// If the type is coercible, emit it directly.
const Expression& arg = *c.argument();
const Type& argType = arg.type();
if (this->canCoerce(c.type(), argType)) {
this->writeExpression(arg, parentPrecedence);
return;
}
return this->writeSingleArgumentConstructor(c, parentPrecedence);
}
void MetalCodeGenerator::writeFragCoord() {
if (fRTHeightName.length()) {
this->write("float4(_fragCoord.x, ");
@ -2265,6 +2268,7 @@ MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Expressi
case Expression::Kind::kConstructor:
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorDiagonalMatrix:
case Expression::Kind::kConstructorScalarCast:
case Expression::Kind::kConstructorSplat: {
const AnyConstructor& c = e->asAnyConstructor();
Requirements result = kNo_Requirements;

View File

@ -23,6 +23,7 @@
#include "src/sksl/ir/SkSLBoolLiteral.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLExtension.h"
#include "src/sksl/ir/SkSLFieldAccess.h"
@ -232,6 +233,8 @@ protected:
void writeConstructorArray(const ConstructorArray& c, Precedence parentPrecedence);
void writeConstructorScalarCast(const ConstructorScalarCast& c, Precedence parentPrecedence);
void writeSingleArgumentConstructor(const SingleArgumentConstructor& c,
Precedence parentPrecedence);

View File

@ -412,6 +412,7 @@ void PipelineStageCodeGenerator::writeExpression(const Expression& expr,
case Expression::Kind::kConstructor:
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorDiagonalMatrix:
case Expression::Kind::kConstructorScalarCast:
case Expression::Kind::kConstructorSplat:
this->writeAnyConstructor(expr.asAnyConstructor(), parentPrecedence);
break;

View File

@ -18,6 +18,7 @@
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLConstructorSplat.h"
#include "src/sksl/ir/SkSLContinueStatement.h"
#include "src/sksl/ir/SkSLDiscardStatement.h"
@ -471,6 +472,12 @@ std::unique_ptr<Expression> Rehydrator::expression() {
return ConstructorDiagonalMatrix::Make(fContext, /*offset=*/-1, *type,
std::move(args[0]));
}
case Rehydrator::kConstructorScalarCast_Command: {
const Type* type = this->type();
ExpressionArray args = this->expressionArray();
SkASSERT(args.size() == 1);
return ConstructorScalarCast::Make(fContext, /*offset=*/-1, *type, std::move(args[0]));
}
case Rehydrator::kConstructorSplat_Command: {
const Type* type = this->type();
ExpressionArray args = this->expressionArray();

View File

@ -54,7 +54,7 @@ public:
kConstructorArray_Command,
kConstructorDiagonalMatrix_Command,
kConstructorSplat_Command,
kConstructorVectorCast_Command,
kConstructorScalarCast_Command,
kConstructorReserved1_Command,
kConstructorReserved2_Command,
kConstructorReserved3_Command,

View File

@ -715,6 +715,8 @@ SpvId SPIRVCodeGenerator::writeExpression(const Expression& expr, OutputStream&
return this->writeArrayConstructor(expr.as<ConstructorArray>(), out);
case Expression::Kind::kConstructorDiagonalMatrix:
return this->writeConstructorDiagonalMatrix(expr.as<ConstructorDiagonalMatrix>(), out);
case Expression::Kind::kConstructorScalarCast:
return this->writeConstructorScalarCast(expr.as<ConstructorScalarCast>(), out);
case Expression::Kind::kConstructorSplat:
return this->writeConstructorSplat(expr.as<ConstructorSplat>(), out);
case Expression::Kind::kIntLiteral:
@ -1219,10 +1221,10 @@ SpvId SPIRVCodeGenerator::writeConstantVector(const AnyConstructor& c) {
return iter->second;
}
SpvId SPIRVCodeGenerator::writeFloatConstructor(const Constructor& c, OutputStream& out) {
SkASSERT(c.arguments().size() == 1);
SpvId SPIRVCodeGenerator::writeFloatConstructor(const AnyConstructor& c, OutputStream& out) {
SkASSERT(c.argumentSpan().size() == 1);
SkASSERT(c.type().isFloat());
const Expression& ctorExpr = *c.arguments()[0];
const Expression& ctorExpr = *c.argumentSpan().front();
SpvId expressionId = this->writeExpression(ctorExpr, out);
return this->castScalarToFloat(expressionId, ctorExpr.type(), c.type(), out);
}
@ -1255,10 +1257,10 @@ SpvId SPIRVCodeGenerator::castScalarToFloat(SpvId inputId, const Type& inputType
return result;
}
SpvId SPIRVCodeGenerator::writeIntConstructor(const Constructor& c, OutputStream& out) {
SkASSERT(c.arguments().size() == 1);
SpvId SPIRVCodeGenerator::writeIntConstructor(const AnyConstructor& c, OutputStream& out) {
SkASSERT(c.argumentSpan().size() == 1);
SkASSERT(c.type().isSigned());
const Expression& ctorExpr = *c.arguments()[0];
const Expression& ctorExpr = *c.argumentSpan().front();
SpvId expressionId = this->writeExpression(ctorExpr, out);
return this->castScalarToSignedInt(expressionId, ctorExpr.type(), c.type(), out);
}
@ -1292,10 +1294,10 @@ SpvId SPIRVCodeGenerator::castScalarToSignedInt(SpvId inputId, const Type& input
return result;
}
SpvId SPIRVCodeGenerator::writeUIntConstructor(const Constructor& c, OutputStream& out) {
SkASSERT(c.arguments().size() == 1);
SpvId SPIRVCodeGenerator::writeUIntConstructor(const AnyConstructor& c, OutputStream& out) {
SkASSERT(c.argumentSpan().size() == 1);
SkASSERT(c.type().isUnsigned());
const Expression& ctorExpr = *c.arguments()[0];
const Expression& ctorExpr = *c.argumentSpan().front();
SpvId expressionId = this->writeExpression(ctorExpr, out);
return this->castScalarToUnsignedInt(expressionId, ctorExpr.type(), c.type(), out);
}
@ -1329,10 +1331,10 @@ SpvId SPIRVCodeGenerator::castScalarToUnsignedInt(SpvId inputId, const Type& inp
return result;
}
SpvId SPIRVCodeGenerator::writeBooleanConstructor(const Constructor& c, OutputStream& out) {
SkASSERT(c.arguments().size() == 1);
SpvId SPIRVCodeGenerator::writeBooleanConstructor(const AnyConstructor& c, OutputStream& out) {
SkASSERT(c.argumentSpan().size() == 1);
SkASSERT(c.type().isBoolean());
const Expression& ctorExpr = *c.arguments()[0];
const Expression& ctorExpr = *c.argumentSpan().front();
SpvId expressionId = this->writeExpression(ctorExpr, out);
return this->castScalarToBoolean(expressionId, ctorExpr.type(), c.type(), out);
}
@ -1685,24 +1687,36 @@ SpvId SPIRVCodeGenerator::writeConstructor(const Constructor& c, OutputStream& o
this->getActualType(type) == this->getActualType(c.arguments()[0]->type())) {
return this->writeExpression(*c.arguments()[0], out);
}
if (type.isVector()) {
return this->writeVectorConstructor(c, out);
}
if (type.isMatrix()) {
return this->writeMatrixConstructor(c, out);
}
fErrors.error(c.fOffset, "unsupported constructor: " + c.description());
return -1;
}
SpvId SPIRVCodeGenerator::writeConstructorScalarCast(const ConstructorScalarCast& c,
OutputStream& out) {
const Type& type = c.type();
if (this->getActualType(type) == this->getActualType(c.argument()->type())) {
return this->writeExpression(*c.argument(), out);
}
if (type.isFloat()) {
return this->writeFloatConstructor(c, out);
} else if (type.isSigned()) {
}
if (type.isSigned()) {
return this->writeIntConstructor(c, out);
} else if (type.isUnsigned()) {
}
if (type.isUnsigned()) {
return this->writeUIntConstructor(c, out);
} else if (type.isBoolean()) {
}
if (type.isBoolean()) {
return this->writeBooleanConstructor(c, out);
}
switch (type.typeKind()) {
case Type::TypeKind::kVector:
return this->writeVectorConstructor(c, out);
case Type::TypeKind::kMatrix:
return this->writeMatrixConstructor(c, out);
default:
fErrors.error(c.fOffset, "unsupported constructor: " + c.description());
return -1;
}
fErrors.error(c.fOffset, "unsupported scalar constructor: " + c.description());
return -1;
}
SpvId SPIRVCodeGenerator::writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c,

View File

@ -24,6 +24,7 @@
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLConstructorSplat.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLFieldAccess.h"
@ -244,22 +245,22 @@ private:
SpvId writeConstantVector(const AnyConstructor& c);
SpvId writeFloatConstructor(const Constructor& c, OutputStream& out);
SpvId writeFloatConstructor(const AnyConstructor& c, OutputStream& out);
SpvId castScalarToFloat(SpvId inputId, const Type& inputType, const Type& outputType,
OutputStream& out);
SpvId writeIntConstructor(const Constructor& c, OutputStream& out);
SpvId writeIntConstructor(const AnyConstructor& c, OutputStream& out);
SpvId castScalarToSignedInt(SpvId inputId, const Type& inputType, const Type& outputType,
OutputStream& out);
SpvId writeUIntConstructor(const Constructor& c, OutputStream& out);
SpvId writeUIntConstructor(const AnyConstructor& c, OutputStream& out);
SpvId castScalarToUnsignedInt(SpvId inputId, const Type& inputType, const Type& outputType,
OutputStream& out);
SpvId writeBooleanConstructor(const Constructor& c, OutputStream& out);
SpvId writeBooleanConstructor(const AnyConstructor& c, OutputStream& out);
SpvId castScalarToBoolean(SpvId inputId, const Type& inputType, const Type& outputType,
OutputStream& out);
@ -292,6 +293,8 @@ private:
SpvId writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, OutputStream& out);
SpvId writeConstructorScalarCast(const ConstructorScalarCast& c, OutputStream& out);
SpvId writeConstructorSplat(const ConstructorSplat& c, OutputStream& out);
SpvId writeFieldAccess(const FieldAccess& f, OutputStream& out);

View File

@ -20,6 +20,7 @@
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLConstructorSplat.h"
#include "src/sksl/ir/SkSLContinueStatement.h"
#include "src/sksl/ir/SkSLDoStatement.h"
@ -248,6 +249,7 @@ private:
Value writeConstructor(const Constructor& c);
Value writeMultiArgumentConstructor(const MultiArgumentConstructor& c);
Value writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c);
Value writeConstructorScalarCast(const ConstructorScalarCast& c);
Value writeConstructorSplat(const ConstructorSplat& c);
Value writeFunctionCall(const FunctionCall& c);
Value writeExternalFunctionCall(const ExternalFunctionCall& c);
@ -260,6 +262,8 @@ private:
Value writeTernaryExpression(const TernaryExpression& t);
Value writeVariableExpression(const VariableReference& expr);
Value writeTypeConversion(const Value& src, Type::NumberKind srcKind, Type::NumberKind dstKind);
void writeStatement(const Statement& s);
void writeBlock(const Block& b);
void writeBreakStatement();
@ -685,62 +689,7 @@ Value SkVMGenerator::writeConstructor(const Constructor& c) {
// TODO: Handle signed vs. unsigned. GLSL ES 1.0 only has 'int', so no problem yet.
if (srcKind != dstKind) {
// One argument constructors can do type conversion
Value dst(src.slots());
switch (dstKind) {
case Type::NumberKind::kFloat:
if (srcKind == Type::NumberKind::kSigned) {
// int -> float
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = skvm::to_F32(i32(src[i]));
}
return dst;
} else if (srcKind == Type::NumberKind::kBoolean) {
// bool -> float
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = skvm::select(i32(src[i]), 1.0f, 0.0f);
}
return dst;
}
break;
case Type::NumberKind::kSigned:
if (srcKind == Type::NumberKind::kFloat) {
// float -> int
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = skvm::trunc(f32(src[i]));
}
return dst;
} else if (srcKind == Type::NumberKind::kBoolean) {
// bool -> int
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = skvm::select(i32(src[i]), 1, 0);
}
return dst;
}
break;
case Type::NumberKind::kBoolean:
if (srcKind == Type::NumberKind::kSigned) {
// int -> bool
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = i32(src[i]) != 0;
}
return dst;
} else if (srcKind == Type::NumberKind::kFloat) {
// float -> bool
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = f32(src[i]) != 0.0;
}
return dst;
}
break;
default:
break;
}
SkDEBUGFAILF("Unsupported type conversion: %s -> %s", srcType.displayName().c_str(),
dstType.displayName().c_str());
return {};
return this->writeTypeConversion(src, srcKind, dstKind);
}
// Matrices can be constructed from scalars or other matrices
@ -774,6 +723,84 @@ Value SkVMGenerator::writeConstructor(const Constructor& c) {
return {};
}
Value SkVMGenerator::writeTypeConversion(const Value& src,
Type::NumberKind srcKind,
Type::NumberKind dstKind) {
// Conversion among "similar" types (floatN <-> halfN), (shortN <-> intN), etc. is a no-op.
if (srcKind == dstKind) {
return src;
}
// TODO: Handle signed vs. unsigned. GLSL ES 1.0 only has 'int', so no problem yet.
Value dst(src.slots());
switch (dstKind) {
case Type::NumberKind::kFloat:
if (srcKind == Type::NumberKind::kSigned) {
// int -> float
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = skvm::to_F32(i32(src[i]));
}
return dst;
}
if (srcKind == Type::NumberKind::kBoolean) {
// bool -> float
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = skvm::select(i32(src[i]), 1.0f, 0.0f);
}
return dst;
}
break;
case Type::NumberKind::kSigned:
if (srcKind == Type::NumberKind::kFloat) {
// float -> int
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = skvm::trunc(f32(src[i]));
}
return dst;
}
if (srcKind == Type::NumberKind::kBoolean) {
// bool -> int
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = skvm::select(i32(src[i]), 1, 0);
}
return dst;
}
break;
case Type::NumberKind::kBoolean:
if (srcKind == Type::NumberKind::kSigned) {
// int -> bool
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = i32(src[i]) != 0;
}
return dst;
}
if (srcKind == Type::NumberKind::kFloat) {
// float -> bool
for (size_t i = 0; i < src.slots(); ++i) {
dst[i] = f32(src[i]) != 0.0;
}
return dst;
}
break;
default:
break;
}
SkDEBUGFAILF("Unsupported type conversion: %d -> %d", srcKind, dstKind);
return {};
}
Value SkVMGenerator::writeConstructorScalarCast(const ConstructorScalarCast& c) {
const Type& srcType = c.argument()->type();
const Type& dstType = c.type();
Type::NumberKind srcKind = base_number_kind(srcType);
Type::NumberKind dstKind = base_number_kind(dstType);
Value src = this->writeExpression(*c.argument());
return this->writeTypeConversion(src, srcKind, dstKind);
}
Value SkVMGenerator::writeConstructorSplat(const ConstructorSplat& c) {
SkASSERT(c.type().isVector());
SkASSERT(c.argument()->type().isScalar());
@ -1464,6 +1491,8 @@ Value SkVMGenerator::writeExpression(const Expression& e) {
return this->writeMultiArgumentConstructor(e.as<ConstructorArray>());
case Expression::Kind::kConstructorDiagonalMatrix:
return this->writeConstructorDiagonalMatrix(e.as<ConstructorDiagonalMatrix>());
case Expression::Kind::kConstructorScalarCast:
return this->writeConstructorScalarCast(e.as<ConstructorScalarCast>());
case Expression::Kind::kConstructorSplat:
return this->writeConstructorSplat(e.as<ConstructorSplat>());
case Expression::Kind::kFieldAccess:

View File

@ -10,6 +10,7 @@
#include "src/sksl/ir/SkSLBoolLiteral.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLConstructorSplat.h"
#include "src/sksl/ir/SkSLFloatLiteral.h"
#include "src/sksl/ir/SkSLIntLiteral.h"
@ -29,7 +30,7 @@ std::unique_ptr<Expression> Constructor::Convert(const Context& context,
return std::move(args[0]);
}
if (type.isScalar()) {
return MakeScalarConstructor(context, offset, type.scalarTypeForLiteral(), std::move(args));
return ConstructorScalarCast::Convert(context, offset, type, std::move(args));
}
if (type.isVector() || type.isMatrix()) {
return MakeCompoundConstructor(context, offset, type, std::move(args));
@ -42,33 +43,6 @@ std::unique_ptr<Expression> Constructor::Convert(const Context& context,
return nullptr;
}
std::unique_ptr<Expression> Constructor::MakeScalarConstructor(const Context& context,
int offset,
const Type& type,
ExpressionArray args) {
SkASSERT(type.isScalar());
if (args.size() != 1) {
context.fErrors.error(offset, "invalid arguments to '" + type.displayName() +
"' constructor, (expected exactly 1 argument, but found " +
to_string((uint64_t)args.size()) + ")");
return nullptr;
}
const Type& argType = args[0]->type();
if (!argType.isScalar()) {
context.fErrors.error(offset, "invalid argument to '" + type.displayName() +
"' constructor (expected a number or bool, but found '" +
argType.displayName() + "')");
return nullptr;
}
if (std::unique_ptr<Expression> converted = SimplifyConversion(type, *args[0])) {
return converted;
}
return std::make_unique<Constructor>(offset, type, std::move(args));
}
std::unique_ptr<Expression> Constructor::MakeCompoundConstructor(const Context& context,
int offset,
const Type& type,
@ -83,9 +57,9 @@ std::unique_ptr<Expression> Constructor::MakeCompoundConstructor(const Context&
// A constructor containing a single scalar is a splat (for vectors) or diagonal matrix (for
// matrices). In either event, it's legal regardless of the scalar's type. Synthesize an
// explicit conversion to the proper type (this is a no-op if it's unnecessary).
std::unique_ptr<Expression> typecast = Constructor::Convert(context, offset,
type.componentType(),
std::move(args));
std::unique_ptr<Expression> typecast = ConstructorScalarCast::Make(context, offset,
type.componentType(),
std::move(args[0]));
SkASSERT(typecast);
// Matrix-from-scalar creates a diagonal matrix; vector-from-scalar creates a splat.
@ -146,9 +120,9 @@ std::unique_ptr<Expression> Constructor::MakeCompoundConstructor(const Context&
int argsToOptimize = 0;
int currBit = 1;
for (const std::unique_ptr<Expression>& arg : args) {
if (arg->is<Constructor>()) {
Constructor& inner = arg->as<Constructor>();
if (inner.arguments().size() > 1 &&
if (arg->isAnyConstructor()) {
AnyConstructor& inner = arg->asAnyConstructor();
if (inner.argumentSpan().size() > 1 &&
inner.type().componentType() == type.componentType()) {
argsToOptimize |= currBit;
}
@ -164,8 +138,8 @@ std::unique_ptr<Expression> Constructor::MakeCompoundConstructor(const Context&
currBit = 1;
for (std::unique_ptr<Expression>& arg : args) {
if (argsToOptimize & currBit) {
Constructor& inner = arg->as<Constructor>();
for (std::unique_ptr<Expression>& innerArg : inner.arguments()) {
AnyConstructor& inner = arg->asAnyConstructor();
for (std::unique_ptr<Expression>& innerArg : inner.argumentSpan()) {
flattened.push_back(std::move(innerArg));
}
} else {
@ -180,48 +154,6 @@ std::unique_ptr<Expression> Constructor::MakeCompoundConstructor(const Context&
return std::make_unique<Constructor>(offset, type, std::move(args));
}
std::unique_ptr<Expression> Constructor::SimplifyConversion(const Type& constructorType,
const Expression& expr) {
if (expr.is<IntLiteral>()) {
SKSL_INT value = expr.as<IntLiteral>().value();
if (constructorType.isFloat()) {
// promote float(1) to 1.0
return FloatLiteral::Make(expr.fOffset, (SKSL_FLOAT)value, &constructorType);
} else if (constructorType.isInteger()) {
// promote uint(1) to 1u
return IntLiteral::Make(expr.fOffset, value, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(1) to true/false
return BoolLiteral::Make(expr.fOffset, value != 0, &constructorType);
}
} else if (expr.is<FloatLiteral>()) {
float value = expr.as<FloatLiteral>().value();
if (constructorType.isFloat()) {
// promote float(1.23) to 1.23
return FloatLiteral::Make(expr.fOffset, value, &constructorType);
} else if (constructorType.isInteger()) {
// promote uint(1.23) to 1u
return IntLiteral::Make(expr.fOffset, (SKSL_INT)value, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(1.23) to true/false
return BoolLiteral::Make(expr.fOffset, value != 0.0f, &constructorType);
}
} else if (expr.is<BoolLiteral>()) {
bool value = expr.as<BoolLiteral>().value();
if (constructorType.isFloat()) {
// promote float(true) to 1.0
return FloatLiteral::Make(expr.fOffset, value ? 1.0f : 0.0f, &constructorType);
} else if (constructorType.isInteger()) {
// promote uint(true) to 1u
return IntLiteral::Make(expr.fOffset, value ? 1 : 0, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(true) to true/false
return BoolLiteral::Make(expr.fOffset, value, &constructorType);
}
}
return nullptr;
}
Expression::ComparisonResult Constructor::compareConstant(const Expression& other) const {
if (other.is<ConstructorDiagonalMatrix>()) {
return other.compareConstant(*this);

View File

@ -179,14 +179,6 @@ public:
const Type& type,
ExpressionArray args);
// If the passed-in expression is a literal, performs a constructor-conversion of the literal
// value to the constructor's type and returns that converted value as a new literal. e.g., the
// constructor expression `short(3.14)` would be represented as `FloatLiteral(3.14)` along with
// type `Short`, and this would result in `IntLiteral(3, type=Short)`. Returns nullptr if the
// expression is not a literal or the conversion cannot be made.
static std::unique_ptr<Expression> SimplifyConversion(const Type& constructorType,
const Expression& expr);
std::unique_ptr<Expression> clone() const override {
return std::make_unique<Constructor>(fOffset, this->type(), this->cloneArguments());
}

View File

@ -0,0 +1,98 @@
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
namespace SkSL {
static std::unique_ptr<Expression> cast_scalar_literal(const Type& constructorType,
const Expression& expr) {
if (expr.is<IntLiteral>()) {
SKSL_INT value = expr.as<IntLiteral>().value();
if (constructorType.isFloat()) {
// promote float(1) to 1.0
return FloatLiteral::Make(expr.fOffset, (SKSL_FLOAT)value, &constructorType);
} else if (constructorType.isInteger()) {
// promote uint(1) to 1u
return IntLiteral::Make(expr.fOffset, value, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(1) to true/false
return BoolLiteral::Make(expr.fOffset, value != 0, &constructorType);
}
} else if (expr.is<FloatLiteral>()) {
float value = expr.as<FloatLiteral>().value();
if (constructorType.isFloat()) {
// promote float(1.23) to 1.23
return FloatLiteral::Make(expr.fOffset, value, &constructorType);
} else if (constructorType.isInteger()) {
// promote uint(1.23) to 1u
return IntLiteral::Make(expr.fOffset, (SKSL_INT)value, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(1.23) to true/false
return BoolLiteral::Make(expr.fOffset, value != 0.0f, &constructorType);
}
} else if (expr.is<BoolLiteral>()) {
bool value = expr.as<BoolLiteral>().value();
if (constructorType.isFloat()) {
// promote float(true) to 1.0
return FloatLiteral::Make(expr.fOffset, value ? 1.0f : 0.0f, &constructorType);
} else if (constructorType.isInteger()) {
// promote uint(true) to 1u
return IntLiteral::Make(expr.fOffset, value ? 1 : 0, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(true) to true/false
return BoolLiteral::Make(expr.fOffset, value, &constructorType);
}
}
return nullptr;
}
std::unique_ptr<Expression> ConstructorScalarCast::Convert(const Context& context,
int offset,
const Type& rawType,
ExpressionArray args) {
// As you might expect, scalar-cast constructors should only be created with scalar types.
const Type& type = rawType.scalarTypeForLiteral();
SkASSERT(type.isScalar());
if (args.size() != 1) {
context.fErrors.error(offset, "invalid arguments to '" + type.displayName() +
"' constructor, (expected exactly 1 argument, but found " +
to_string((uint64_t)args.size()) + ")");
return nullptr;
}
const Type& argType = args[0]->type();
if (!argType.isScalar()) {
context.fErrors.error(offset, "invalid argument to '" + type.displayName() +
"' constructor (expected a number or bool, but found '" +
argType.displayName() + "')");
return nullptr;
}
return ConstructorScalarCast::Make(context, offset, type, std::move(args[0]));
}
std::unique_ptr<Expression> ConstructorScalarCast::Make(const Context& context,
int offset,
const Type& type,
std::unique_ptr<Expression> arg) {
SkASSERT(type.isScalar());
SkASSERT(arg->type().isScalar());
// No cast required when the types match.
if (arg->type() == type) {
return arg;
}
// We can cast scalar literals at compile-time.
if (std::unique_ptr<Expression> converted = cast_scalar_literal(type, *arg)) {
return converted;
}
return std::make_unique<ConstructorScalarCast>(offset, type, std::move(arg));
}
} // namespace SkSL

View File

@ -0,0 +1,61 @@
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_CONSTRUCTOR_SCALAR_CAST
#define SKSL_CONSTRUCTOR_SCALAR_CAST
#include "include/private/SkSLDefines.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLExpression.h"
#include <memory>
namespace SkSL {
/**
* Represents the construction of a scalar cast, such as `float(intVariable)`.
*
* These always contain exactly 1 scalar of a differing type, and are never constant.
*/
class ConstructorScalarCast final : public SingleArgumentConstructor {
public:
static constexpr Kind kExpressionKind = Kind::kConstructorScalarCast;
ConstructorScalarCast(int offset, const Type& type, std::unique_ptr<Expression> arg)
: INHERITED(offset, kExpressionKind, &type, std::move(arg)) {}
// ConstructorScalarCast::Convert will typecheck and create scalar-constructor expressions.
// Reports errors via the ErrorReporter; returns null on error.
static std::unique_ptr<Expression> Convert(const Context& context,
int offset,
const Type& rawType,
ExpressionArray args);
// ConstructorScalarCast::Make casts a scalar expression. Casts that can be evaluated at
// compile-time will do so (e.g. `int(4.1)` --> `IntLiteral(4)`). Errors reported via SkASSERT.
static std::unique_ptr<Expression> Make(const Context& context,
int offset,
const Type& type,
std::unique_ptr<Expression> arg);
std::unique_ptr<Expression> clone() const override {
return std::make_unique<ConstructorScalarCast>(fOffset, this->type(), argument()->clone());
}
bool isCompileTimeConstant() const override {
// If this were a compile-time constant, we would have created a literal instead.
return false;
}
private:
using INHERITED = SingleArgumentConstructor;
};
} // namespace SkSL
#endif

View File

@ -33,6 +33,7 @@ public:
kConstructor,
kConstructorArray,
kConstructorDiagonalMatrix,
kConstructorScalarCast,
kConstructorSplat,
kDefined,
kExternalFunctionCall,

View File

@ -6,6 +6,7 @@
*/
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLConstructorSplat.h"
#include "src/sksl/ir/SkSLSwizzle.h"
@ -95,10 +96,9 @@ std::unique_ptr<Expression> Swizzle::Convert(const Context& context,
case SwizzleComponent::ZERO:
if (constantZeroIdx == -1) {
// Synthesize a 'type(0)' argument at the end of the constructor.
ExpressionArray zeroArgs;
zeroArgs.push_back(IntLiteral::Make(context, offset, /*value=*/0));
constructorArgs.push_back(Constructor::Convert(context, offset, *numberType,
std::move(zeroArgs)));
constructorArgs.push_back(ConstructorScalarCast::Make(
context, offset, *numberType,
IntLiteral::Make(context, offset, /*value=*/0)));
constantZeroIdx = constantFieldIdx++;
}
swizzleComponents.push_back(constantZeroIdx);
@ -106,10 +106,9 @@ std::unique_ptr<Expression> Swizzle::Convert(const Context& context,
case SwizzleComponent::ONE:
if (constantOneIdx == -1) {
// Synthesize a 'type(1)' argument at the end of the constructor.
ExpressionArray oneArgs;
oneArgs.push_back(IntLiteral::Make(context, offset, /*value=*/1));
constructorArgs.push_back(Constructor::Convert(context, offset, *numberType,
std::move(oneArgs)));
constructorArgs.push_back(ConstructorScalarCast::Make(
context, offset, *numberType,
IntLiteral::Make(context, offset, /*value=*/1)));
constantOneIdx = constantFieldIdx++;
}
swizzleComponents.push_back(constantOneIdx);

View File

@ -9,6 +9,7 @@
#include "src/sksl/SkSLContext.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLFunctionReference.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/sksl/ir/SkSLType.h"
@ -269,10 +270,10 @@ std::unique_ptr<Expression> Type::coerceExpression(std::unique_ptr<Expression> e
ExpressionArray args;
args.push_back(std::move(expr));
if (!this->isScalar()) {
return Constructor::Convert(context, offset, *this, std::move(args));
if (this->isScalar()) {
return ConstructorScalarCast::Convert(context, offset, *this, std::move(args));
}
return Constructor::Convert(context, offset, this->scalarTypeForLiteral(), std::move(args));
return Constructor::Convert(context, offset, *this, std::move(args));
}
bool Type::isOrContainsArray() const {