Interpreter: Fix construction of Matrices
Per GLSL, constructing a matrix from a scalar produces a matrix with the scalar value along the diagonal, and zero elsewhere. Constructing a matrix from another matrix copies the overlapping values, and fills in the remainder with the identity matrix. Doing either of these with existing opcodes was going to be quite verbose and tricky, so I just made new opcodes. I've also got some (currently disabled) test cases for other matrix behavior, all of which fail in various ways today. Change-Id: Ia86a183395f1ac7e2f23ee1d6bb4af461f5ba93a Reviewed-on: https://skia-review.googlesource.com/c/skia/+/215823 Reviewed-by: Ethan Nicholas <ethannicholas@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
c7e9f78d10
commit
29e013deb4
@ -74,6 +74,11 @@ enum class ByteCodeInstruction : uint16_t {
|
||||
// count byte, and get the slot to load from the top of the stack.
|
||||
kLoadExtended,
|
||||
kLoadExtendedGlobal,
|
||||
// Followed by four bytes: srcCols, srcRows, dstCols, dstRows. Consumes the src matrix from the
|
||||
// stack, and replaces it with the dst matrix. Per GLSL rules, there are no restrictions on
|
||||
// dimensions. Any overlapping values are copied, and any other values are filled in with the
|
||||
// identity matrix.
|
||||
kMatrixToMatrix,
|
||||
VECTOR(kNegateF),
|
||||
VECTOR(kNegateI),
|
||||
VECTOR(kMix),
|
||||
@ -94,6 +99,10 @@ enum class ByteCodeInstruction : uint16_t {
|
||||
VECTOR(kRemainderU),
|
||||
// Followed by a byte indicating the number of slots being returned
|
||||
kReturn,
|
||||
// Followed by two bytes indicating columns and rows of matrix (2, 3, or 4 each).
|
||||
// Takes a single value from the top of the stack, and converts to a CxR matrix with that value
|
||||
// replicated along the diagonal (and zero elsewhere), per the GLSL matrix construction rules.
|
||||
kScalarToMatrix,
|
||||
VECTOR(kSin),
|
||||
VECTOR(kSqrt),
|
||||
// kStore/kStoreGlobal are followed by a byte indicating the local/global slot to store
|
||||
|
@ -443,10 +443,12 @@ void ByteCodeGenerator::writeConstructor(const Constructor& c) {
|
||||
this->writeExpression(*arg);
|
||||
}
|
||||
if (c.fArguments.size() == 1) {
|
||||
TypeCategory inCategory = type_category(c.fArguments[0]->fType);
|
||||
TypeCategory outCategory = type_category(c.fType);
|
||||
int inCount = c.fArguments[0]->fType.columns();
|
||||
int outCount = c.fType.columns();
|
||||
const Type& inType = c.fArguments[0]->fType;
|
||||
const Type& outType = c.fType;
|
||||
TypeCategory inCategory = type_category(inType);
|
||||
TypeCategory outCategory = type_category(outType);
|
||||
int inCount = SlotCount(inType);
|
||||
int outCount = SlotCount(outType);
|
||||
if (inCategory != outCategory) {
|
||||
SkASSERT(inCount == outCount);
|
||||
if (inCategory == TypeCategory::kFloat) {
|
||||
@ -464,10 +466,23 @@ void ByteCodeGenerator::writeConstructor(const Constructor& c) {
|
||||
SkASSERT(false);
|
||||
}
|
||||
}
|
||||
if (inCount != outCount) {
|
||||
if (inType.kind() == Type::kMatrix_Kind && outType.kind() == Type::kMatrix_Kind) {
|
||||
this->write(ByteCodeInstruction::kMatrixToMatrix);
|
||||
this->write8(inType.columns());
|
||||
this->write8(inType.rows());
|
||||
this->write8(outType.columns());
|
||||
this->write8(outType.rows());
|
||||
} else if (inCount != outCount) {
|
||||
SkASSERT(inCount == 1);
|
||||
for (; inCount != outCount; ++inCount) {
|
||||
this->write(ByteCodeInstruction::kDup);
|
||||
if (outType.kind() == Type::kMatrix_Kind) {
|
||||
this->write(ByteCodeInstruction::kScalarToMatrix);
|
||||
this->write8(outType.columns());
|
||||
this->write8(outType.rows());
|
||||
} else {
|
||||
SkASSERT(outType.kind() == Type::kVector_Kind);
|
||||
for (; inCount != outCount; ++inCount) {
|
||||
this->write(ByteCodeInstruction::kDup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +165,14 @@ static const uint8_t* disassemble_instruction(const uint8_t* ip) {
|
||||
case ByteCodeInstruction::kLoadExtended: printf("loadextended %d", READ8()); break;
|
||||
case ByteCodeInstruction::kLoadExtendedGlobal: printf("loadextendedglobal %d", READ8());
|
||||
break;
|
||||
case ByteCodeInstruction::kMatrixToMatrix: {
|
||||
int srcCols = READ8();
|
||||
int srcRows = READ8();
|
||||
int dstCols = READ8();
|
||||
int dstRows = READ8();
|
||||
printf("matrixtomatrix %dx%d %dx%d", srcCols, srcRows, dstCols, dstRows);
|
||||
break;
|
||||
}
|
||||
VECTOR_DISASSEMBLE(kMix, "mix")
|
||||
VECTOR_DISASSEMBLE(kMultiplyF, "multiplyf")
|
||||
VECTOR_DISASSEMBLE(kMultiplyI, "multiplyi")
|
||||
@ -189,6 +197,12 @@ static const uint8_t* disassemble_instruction(const uint8_t* ip) {
|
||||
VECTOR_DISASSEMBLE(kRemainderS, "remainders")
|
||||
VECTOR_DISASSEMBLE(kRemainderU, "remainderu")
|
||||
case ByteCodeInstruction::kReturn: printf("return %d", READ8()); break;
|
||||
case ByteCodeInstruction::kScalarToMatrix: {
|
||||
int cols = READ8();
|
||||
int rows = READ8();
|
||||
printf("scalartomatrix %dx%d", cols, rows);
|
||||
break;
|
||||
}
|
||||
VECTOR_DISASSEMBLE(kSin, "sin")
|
||||
VECTOR_DISASSEMBLE(kSqrt, "sqrt")
|
||||
case ByteCodeInstruction::kStore: printf("store %d", READ8()); break;
|
||||
@ -517,6 +531,29 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
break;
|
||||
}
|
||||
|
||||
case ByteCodeInstruction::kMatrixToMatrix: {
|
||||
int srcCols = READ8();
|
||||
int srcRows = READ8();
|
||||
int dstCols = READ8();
|
||||
int dstRows = READ8();
|
||||
SkASSERT(srcCols >= 2 && srcCols <= 4);
|
||||
SkASSERT(srcRows >= 2 && srcRows <= 4);
|
||||
SkASSERT(dstCols >= 2 && dstCols <= 4);
|
||||
SkASSERT(dstRows >= 2 && dstRows <= 4);
|
||||
SkMatrix44 m;
|
||||
for (int c = srcCols - 1; c >= 0; --c) {
|
||||
for (int r = srcRows - 1; r >= 0; --r) {
|
||||
m.set(r, c, POP().fFloat);
|
||||
}
|
||||
}
|
||||
for (int c = 0; c < dstCols; ++c) {
|
||||
for (int r = 0; r < dstRows; ++r) {
|
||||
PUSH(m.get(r, c));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// stack looks like: X1 Y1 Z1 W1 X2 Y2 Z2 W2 T
|
||||
case ByteCodeInstruction::kMix4:
|
||||
sp[-5] = mix(sp[-5].fFloat, sp[-1].fFloat, sp[0].fFloat);
|
||||
@ -612,6 +649,18 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
}
|
||||
}
|
||||
|
||||
case ByteCodeInstruction::kScalarToMatrix: {
|
||||
int cols = READ8();
|
||||
int rows = READ8();
|
||||
Value v = POP();
|
||||
for (int c = 0; c < cols; ++c) {
|
||||
for (int r = 0; r < rows; ++r) {
|
||||
PUSH(c == r ? v : 0.0f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
VECTOR_UNARY_FN(kSin, sinf, fFloat)
|
||||
VECTOR_UNARY_FN(kSqrt, sqrtf, fFloat)
|
||||
|
||||
|
@ -49,6 +49,7 @@ void test(skiatest::Reporter* r, const char* src, SkSL::Interpreter::Value* in,
|
||||
separator = ", ";
|
||||
}
|
||||
printf(")\n");
|
||||
interpreter.disassemble(*main);
|
||||
}
|
||||
REPORTER_ASSERT(r, valid);
|
||||
} else {
|
||||
@ -149,6 +150,63 @@ DEF_TEST(SkSLInterpreterRemainder, r) {
|
||||
0, 2, 4, 0, 0);
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLInterpreterMatrix, r) {
|
||||
SkSL::Interpreter::Value in[4];
|
||||
SkSL::Interpreter::Value expected[1];
|
||||
|
||||
// Constructing matrix from scalar produces a diagonal matrix
|
||||
in[0] = 1.0f;
|
||||
expected[0] = 2.0f;
|
||||
test(r, "float main(float x) { float4x4 m = float4x4(x); return m[1][1] + m[1][2] + m[2][2]; }",
|
||||
in, 1, expected);
|
||||
|
||||
// With non-square matrix
|
||||
test(r, "float main(float x) { float3x2 m = float3x2(x); return m[0][0] + m[1][1] + m[2][1]; }",
|
||||
in, 1, expected);
|
||||
|
||||
// Constructing from a different-sized matrix fills the remaining space with the identity matrix
|
||||
test(r, "float main(float x) {"
|
||||
"float3x2 m = float3x2(x);"
|
||||
"float4x4 m2 = float4x4(m);"
|
||||
"return m2[0][0] + m2[3][3]; }",
|
||||
in, 1, expected);
|
||||
|
||||
// Constructing a matrix from vectors or scalars fills in values in column-major order
|
||||
in[0] = 1.0f;
|
||||
in[1] = 2.0f;
|
||||
in[2] = 4.0f;
|
||||
in[3] = 8.0f;
|
||||
expected[0] = 6.0f;
|
||||
test(r, "float main(float4 v) { float2x2 m = float2x2(v); return m[0][1] + m[1][0]; }",
|
||||
in, 1, expected);
|
||||
|
||||
expected[0] = 10.0f;
|
||||
test(r, "float main(float4 v) {"
|
||||
"float2x2 m = float2x2(v.x, v.y, v.w, v.z);"
|
||||
"return m[0][1] + m[1][0]; }",
|
||||
in, 1, expected);
|
||||
|
||||
#if 0
|
||||
// Addition of matrices
|
||||
test(r, "void main(inout half4 color) {"
|
||||
"half4x4 m = half4x4(color, color, color, color);"
|
||||
"m += m; color = m[0]; }",
|
||||
1, 2, 3, 4, 2, 4, 6, 8);
|
||||
|
||||
// Matrix * Vector multiplication
|
||||
test(r, "void main(inout half4 color) {"
|
||||
"half4x4 m = half4x4(color, color, color, color);"
|
||||
"color = m * color; }",
|
||||
1, 2, 3, 4, 10, 20, 30, 40);
|
||||
|
||||
// Matrix * Matrix multiplication
|
||||
test(r, "void main(inout half4 color) {"
|
||||
"half4x4 m = half4x4(color, color, color, color);"
|
||||
"m = m * m; color = m[2]; }",
|
||||
1, 2, 3, 4, 10, 20, 30, 40);
|
||||
#endif
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLInterpreterTernary, r) {
|
||||
test(r, "void main(inout half4 color) { color.r = color.g > color.b ? color.g : color.b; }",
|
||||
0, 1, 2, 0, 2, 1, 2, 0);
|
||||
|
Loading…
Reference in New Issue
Block a user