fixed Metal matrix constructors
Bug: skia:8544 Change-Id: Ie1e96ab1ef6e8c032fa510be36c035f91a1f6851 Reviewed-on: https://skia-review.googlesource.com/c/185687 Reviewed-by: Chris Dalton <csmartdalton@google.com> Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
This commit is contained in:
parent
57796b340e
commit
842d31b141
@ -18,6 +18,7 @@ skia_sksl_sources = [
|
||||
"$_src/sksl/SkSLJIT.cpp",
|
||||
"$_src/sksl/SkSLLexer.cpp",
|
||||
"$_src/sksl/SkSLMetalCodeGenerator.cpp",
|
||||
"$_src/sksl/SkSLOutputStream.cpp",
|
||||
"$_src/sksl/SkSLParser.cpp",
|
||||
"$_src/sksl/SkSLPipelineStageCodeGenerator.cpp",
|
||||
"$_src/sksl/SkSLSPIRVCodeGenerator.cpp",
|
||||
|
@ -241,6 +241,7 @@ tests_sources = [
|
||||
"$_tests/SkSLGLSLTest.cpp",
|
||||
"$_tests/SkSLJITTest.cpp",
|
||||
"$_tests/SkSLMemoryLayoutTest.cpp",
|
||||
"$_tests/SkSLMetalTest.cpp",
|
||||
"$_tests/SkSLSPIRVTest.cpp",
|
||||
"$_tests/SkUTFTest.cpp",
|
||||
"$_tests/SortTest.cpp",
|
||||
|
44
src/sksl/SkSLDefines.h
Normal file
44
src/sksl/SkSLDefines.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SKSL_DEFINES
|
||||
#define SKSL_DEFINES
|
||||
#ifdef SKSL_STANDALONE
|
||||
#if defined(_WIN32) || defined(__SYMBIAN32__)
|
||||
#define SKSL_BUILD_FOR_WIN
|
||||
#endif
|
||||
#else
|
||||
#ifdef SK_BUILD_FOR_WIN
|
||||
#define SKSL_BUILD_FOR_WIN
|
||||
#endif // SK_BUILD_FOR_WIN
|
||||
#endif // SKSL_STANDALONE
|
||||
|
||||
#ifdef SKSL_STANDALONE
|
||||
#define SkASSERT(x)
|
||||
#define SkAssertResult(x) x
|
||||
#define SkDEBUGCODE(x)
|
||||
#else
|
||||
#include "SkTypes.h"
|
||||
#endif
|
||||
|
||||
#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define SKSL_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
|
||||
#else
|
||||
#define SKSL_PRINTF_LIKE(A, B)
|
||||
#endif
|
||||
|
||||
#define ABORT(...) (printf(__VA_ARGS__), sksl_abort())
|
||||
|
||||
#if _MSC_VER
|
||||
#define NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
#endif
|
||||
|
||||
#endif
|
@ -120,7 +120,7 @@ void MetalCodeGenerator::writeExpression(const Expression& expr, Precedence pare
|
||||
this->writeBoolLiteral((BoolLiteral&) expr);
|
||||
break;
|
||||
case Expression::kConstructor_Kind:
|
||||
this->writeConstructor((Constructor&) expr);
|
||||
this->writeConstructor((Constructor&) expr, parentPrecedence);
|
||||
break;
|
||||
case Expression::kIntLiteral_Kind:
|
||||
this->writeIntLiteral((IntLiteral&) expr);
|
||||
@ -296,31 +296,109 @@ void MetalCodeGenerator::writeSpecialIntrinsic(const FunctionCall & c, SpecialIn
|
||||
}
|
||||
}
|
||||
|
||||
void MetalCodeGenerator::writeConstructor(const Constructor& c) {
|
||||
this->writeType(c.fType);
|
||||
this->write("(");
|
||||
const char* separator = "";
|
||||
int scalarCount = 0;
|
||||
for (const auto& arg : c.fArguments) {
|
||||
this->write(separator);
|
||||
separator = ", ";
|
||||
if (Type::kMatrix_Kind == c.fType.kind() && Type::kScalar_Kind == arg->fType.kind()) {
|
||||
// float2x2(float, float, float, float) doesn't work in Metal 1, so we need to merge to
|
||||
// float2x2(float2, float2).
|
||||
if (!scalarCount) {
|
||||
this->writeType(c.fType.componentType());
|
||||
this->write(to_string(c.fType.rows()));
|
||||
this->write("(");
|
||||
}
|
||||
++scalarCount;
|
||||
}
|
||||
this->writeExpression(*arg, kSequence_Precedence);
|
||||
if (scalarCount && scalarCount == c.fType.rows()) {
|
||||
this->write(")");
|
||||
scalarCount = 0;
|
||||
}
|
||||
// If it hasn't already been written, writes a constructor for 'matrix' which takes a single value
|
||||
// of type 'arg'.
|
||||
String MetalCodeGenerator::getMatrixConstructHelper(const Type& matrix, const Type& arg) {
|
||||
String key = matrix.name() + arg.name();
|
||||
auto found = fMatrixConstructHelpers.find(key);
|
||||
if (found != fMatrixConstructHelpers.end()) {
|
||||
return found->second;
|
||||
}
|
||||
String name;
|
||||
int columns = matrix.columns();
|
||||
int rows = matrix.rows();
|
||||
if (arg.isNumber()) {
|
||||
// creating a matrix from a single scalar value
|
||||
name = "float" + to_string(columns) + "x" + to_string(rows) + "_from_float";
|
||||
fExtraFunctions.printf("float%dx%d %s(float x) {\n",
|
||||
columns, rows, name.c_str());
|
||||
fExtraFunctions.printf(" return float%dx%d(", columns, rows);
|
||||
for (int i = 0; i < columns; ++i) {
|
||||
if (i > 0) {
|
||||
fExtraFunctions.writeText(", ");
|
||||
}
|
||||
fExtraFunctions.printf("float%d(", rows);
|
||||
for (int j = 0; j < rows; ++j) {
|
||||
if (j > 0) {
|
||||
fExtraFunctions.writeText(", ");
|
||||
}
|
||||
if (i == j) {
|
||||
fExtraFunctions.writeText("x");
|
||||
} else {
|
||||
fExtraFunctions.writeText("0");
|
||||
}
|
||||
}
|
||||
fExtraFunctions.writeText(")");
|
||||
}
|
||||
fExtraFunctions.writeText(");\n}\n");
|
||||
}
|
||||
else if (matrix.rows() == 2 && matrix.columns() == 2) {
|
||||
// float2x2(float4) doesn't work, need to split it into float2x2(float2, float2)
|
||||
name = "float2x2_from_float4";
|
||||
fExtraFunctions.printf(
|
||||
"float2x2 %s(float4 v) {\n"
|
||||
" return float2x2(float2(v[0], v[1]), float2(v[2], v[3]));\n"
|
||||
"}\n",
|
||||
name.c_str()
|
||||
);
|
||||
}
|
||||
else {
|
||||
SkASSERT(false);
|
||||
name = "<error>";
|
||||
}
|
||||
fMatrixConstructHelpers[key] = name;
|
||||
return name;
|
||||
}
|
||||
|
||||
bool MetalCodeGenerator::canCoerce(const Type& t1, const Type& t2) {
|
||||
if (t1.columns() != t2.columns() || t1.rows() != t2.rows()) {
|
||||
return false;
|
||||
}
|
||||
if (t1.columns() > 1) {
|
||||
return this->canCoerce(t1.componentType(), t2.componentType());
|
||||
}
|
||||
return ((t1 == *fContext.fFloat_Type || t1 == *fContext.fHalf_Type) &&
|
||||
(t2 == *fContext.fFloat_Type || t2 == *fContext.fHalf_Type));
|
||||
}
|
||||
|
||||
void MetalCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
|
||||
if (c.fArguments.size() == 1 && this->canCoerce(c.fType, c.fArguments[0]->fType)) {
|
||||
this->writeExpression(*c.fArguments[0], parentPrecedence);
|
||||
return;
|
||||
}
|
||||
if (c.fType.kind() == Type::kMatrix_Kind && c.fArguments.size() == 1) {
|
||||
const Expression& arg = *c.fArguments[0];
|
||||
String name = this->getMatrixConstructHelper(c.fType, arg.fType);
|
||||
this->write(name);
|
||||
this->write("(");
|
||||
this->writeExpression(arg, kSequence_Precedence);
|
||||
this->write(")");
|
||||
} else {
|
||||
this->writeType(c.fType);
|
||||
this->write("(");
|
||||
const char* separator = "";
|
||||
int scalarCount = 0;
|
||||
for (const auto& arg : c.fArguments) {
|
||||
this->write(separator);
|
||||
separator = ", ";
|
||||
if (Type::kMatrix_Kind == c.fType.kind() && Type::kScalar_Kind == arg->fType.kind()) {
|
||||
// float2x2(float, float, float, float) doesn't work in Metal 1, so we need to merge
|
||||
// to float2x2(float2, float2).
|
||||
if (!scalarCount) {
|
||||
this->writeType(c.fType.componentType());
|
||||
this->write(to_string(c.fType.rows()));
|
||||
this->write("(");
|
||||
}
|
||||
++scalarCount;
|
||||
}
|
||||
this->writeExpression(*arg, kSequence_Precedence);
|
||||
if (scalarCount && scalarCount == c.fType.rows()) {
|
||||
this->write(")");
|
||||
scalarCount = 0;
|
||||
}
|
||||
}
|
||||
this->write(")");
|
||||
}
|
||||
this->write(")");
|
||||
}
|
||||
|
||||
void MetalCodeGenerator::writeFragCoord() {
|
||||
|
@ -184,9 +184,13 @@ protected:
|
||||
|
||||
void writeInverseHack(const Expression& mat);
|
||||
|
||||
String getMatrixConstructHelper(const Type& matrix, const Type& arg);
|
||||
|
||||
void writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind);
|
||||
|
||||
void writeConstructor(const Constructor& c);
|
||||
bool canCoerce(const Type& t1, const Type& t2);
|
||||
|
||||
void writeConstructor(const Constructor& c, Precedence parentPrecedence);
|
||||
|
||||
void writeFieldAccess(const FieldAccess& f);
|
||||
|
||||
@ -268,6 +272,7 @@ protected:
|
||||
std::unordered_map<const FunctionDeclaration*, Requirements> fRequirements;
|
||||
bool fSetupFragPositionGlobal = false;
|
||||
bool fSetupFragPositionLocal = false;
|
||||
std::unordered_map<String, String> fMatrixConstructHelpers;
|
||||
int fUniformBuffer = -1;
|
||||
|
||||
typedef CodeGenerator INHERITED;
|
||||
|
30
src/sksl/SkSLOutputStream.cpp
Normal file
30
src/sksl/SkSLOutputStream.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkSLOutputStream.h"
|
||||
|
||||
namespace SkSL {
|
||||
|
||||
void OutputStream::writeString(String s) {
|
||||
this->write(s.c_str(), s.size());
|
||||
}
|
||||
|
||||
void OutputStream::printf(const char format[], ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
this->appendVAList(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void OutputStream::appendVAList(const char format[], va_list args) {
|
||||
char buffer[kBufferSize];
|
||||
int length = vsnprintf(buffer, kBufferSize, format, args);
|
||||
SkASSERT(length >= 0 && length < (int) kBufferSize);
|
||||
this->write(buffer, length);
|
||||
}
|
||||
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
#ifndef SKSL_OUTPUTSTREAM
|
||||
#define SKSL_OUTPUTSTREAM
|
||||
|
||||
#include "SkSLDefines.h"
|
||||
#include "SkSLString.h"
|
||||
|
||||
namespace SkSL {
|
||||
@ -24,11 +25,16 @@ public:
|
||||
|
||||
virtual void write(const void* s, size_t size) = 0;
|
||||
|
||||
void writeString(String s) {
|
||||
this->write(s.c_str(), s.size());
|
||||
}
|
||||
void writeString(String s);
|
||||
|
||||
void printf(const char format[], ...) SKSL_PRINTF_LIKE(2, 3);
|
||||
|
||||
void appendVAList(const char format[], va_list args);
|
||||
|
||||
virtual ~OutputStream() {}
|
||||
|
||||
private:
|
||||
static const int kBufferSize = 1024;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -12,29 +12,25 @@
|
||||
#include <memory>
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "assert.h"
|
||||
#include "SkSLDefines.h"
|
||||
#include "SkSLString.h"
|
||||
#include "SkSLStringStream.h"
|
||||
|
||||
#if !defined(SKSL_STANDALONE) && SK_SUPPORT_GPU
|
||||
#ifndef SKSL_STANDALONE
|
||||
#include "SkTypes.h"
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "GrContextOptions.h"
|
||||
#include "GrShaderCaps.h"
|
||||
#endif
|
||||
|
||||
#ifdef SKSL_STANDALONE
|
||||
#if defined(_WIN32) || defined(__SYMBIAN32__)
|
||||
#define SKSL_BUILD_FOR_WIN
|
||||
#endif
|
||||
#else
|
||||
#ifdef SK_BUILD_FOR_WIN
|
||||
#define SKSL_BUILD_FOR_WIN
|
||||
#endif // SK_BUILD_FOR_WIN
|
||||
#endif // SK_SUPPORT_GPU
|
||||
#endif // SKSL_STANDALONE
|
||||
|
||||
class GrShaderCaps;
|
||||
|
||||
namespace SkSL {
|
||||
|
||||
class OutputStream;
|
||||
class StringStream;
|
||||
|
||||
#if defined(SKSL_STANDALONE) || !SK_SUPPORT_GPU
|
||||
|
||||
// we're being compiled standalone, so we don't have access to caps...
|
||||
@ -392,30 +388,8 @@ public:
|
||||
|
||||
void write_stringstream(const StringStream& d, OutputStream& out);
|
||||
|
||||
#if _MSC_VER
|
||||
#define NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
#endif
|
||||
|
||||
NORETURN void sksl_abort();
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef SKSL_STANDALONE
|
||||
#define SkASSERT(x)
|
||||
#define SkAssertResult(x) x
|
||||
#define SkDEBUGCODE(x)
|
||||
#endif
|
||||
|
||||
#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define SKSL_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
|
||||
#else
|
||||
#define SKSL_PRINTF_LIKE(A, B)
|
||||
#endif
|
||||
|
||||
#define ABORT(...) (printf(__VA_ARGS__), sksl_abort())
|
||||
|
||||
#endif
|
||||
|
126
tests/SkSLMetalTest.cpp
Normal file
126
tests/SkSLMetalTest.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkSLCompiler.h"
|
||||
|
||||
#include "Test.h"
|
||||
|
||||
static void test(skiatest::Reporter* r, const char* src, const SkSL::Program::Settings& settings,
|
||||
const char* expected, SkSL::Program::Inputs* inputs,
|
||||
SkSL::Program::Kind kind = SkSL::Program::kFragment_Kind) {
|
||||
SkSL::Compiler compiler;
|
||||
SkSL::String output;
|
||||
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, SkSL::String(src),
|
||||
settings);
|
||||
if (!program) {
|
||||
SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
|
||||
}
|
||||
REPORTER_ASSERT(r, program);
|
||||
*inputs = program->fInputs;
|
||||
REPORTER_ASSERT(r, compiler.toMetal(*program, &output));
|
||||
if (program) {
|
||||
SkSL::String skExpected(expected);
|
||||
if (output != skExpected) {
|
||||
SkDebugf("MSL MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src,
|
||||
expected, output.c_str());
|
||||
}
|
||||
REPORTER_ASSERT(r, output == skExpected);
|
||||
}
|
||||
}
|
||||
|
||||
static void test(skiatest::Reporter* r, const char* src, const GrShaderCaps& caps,
|
||||
const char* expected, SkSL::Program::Kind kind = SkSL::Program::kFragment_Kind) {
|
||||
SkSL::Program::Settings settings;
|
||||
settings.fCaps = ∩︀
|
||||
SkSL::Program::Inputs inputs;
|
||||
test(r, src, settings, expected, &inputs, kind);
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLMetalHelloWorld, r) {
|
||||
test(r,
|
||||
"void main() { sk_FragColor = float4(0.75); }",
|
||||
*SkSL::ShaderCapsFactory::Default(),
|
||||
"#include <metal_stdlib>\n"
|
||||
"#include <simd/simd.h>\n"
|
||||
"using namespace metal;\n"
|
||||
"struct Inputs {\n"
|
||||
"};\n"
|
||||
"struct Outputs {\n"
|
||||
" float4 sk_FragColor [[color(0)]];\n"
|
||||
"};\n"
|
||||
"struct sksl_synthetic_uniforms {\n"
|
||||
" float u_skRTHeight;\n"
|
||||
"};\n"
|
||||
"fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {\n"
|
||||
" Outputs _outputStruct;\n"
|
||||
" thread Outputs* _out = &_outputStruct;\n"
|
||||
" _out->sk_FragColor = float4(0.75);\n"
|
||||
" return *_out;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLMetalMatrices, r) {
|
||||
test(r,
|
||||
"void main() {"
|
||||
"float2x2 m1 = float2x2(float4(1, 2, 3, 4));"
|
||||
"float2x2 m2 = float2x2(float4(0));"
|
||||
"float2x2 m3 = float2x2(m1);"
|
||||
"float2x2 m4 = float2x2(1);"
|
||||
"float2x2 m5 = float2x2(m1[0][0]);"
|
||||
"float2x2 m6 = float2x2(1, 2, 3, 4);"
|
||||
"float2x2 m7 = float2x2(5, 6, 7, 8);"
|
||||
"float3x3 m8 = float3x3(1);"
|
||||
"float3x3 m9 = float3x3(2);"
|
||||
"float4x4 m10 = float4x4(1);"
|
||||
"float4x4 m11 = float4x4(2);"
|
||||
"sk_FragColor = half4(m1[0][0] + m2[0][0] + m3[0][0] + m4[0][0] + m5[0][0] + m6[0][0] + "
|
||||
"m7[0][0] + m8[0][0] + m9[0][0] + m10[0][0] + m11[0][0]);"
|
||||
"}",
|
||||
*SkSL::ShaderCapsFactory::Default(),
|
||||
"#include <metal_stdlib>\n"
|
||||
"#include <simd/simd.h>\n"
|
||||
"using namespace metal;\n"
|
||||
"struct Inputs {\n"
|
||||
"};\n"
|
||||
"struct Outputs {\n"
|
||||
" float4 sk_FragColor [[color(0)]];\n"
|
||||
"};\n"
|
||||
"struct sksl_synthetic_uniforms {\n"
|
||||
" float u_skRTHeight;\n"
|
||||
"};\n"
|
||||
"float2x2 float2x2_from_float(float x) {\n"
|
||||
" return float2x2(float2(x, 0), float2(0, x));\n"
|
||||
"}\n"
|
||||
"float2x2 float2x2_from_float4(float4 v) {\n"
|
||||
" return float2x2(float2(v[0], v[1]), float2(v[2], v[3]));\n"
|
||||
"}\n"
|
||||
"float3x3 float3x3_from_float(float x) {\n"
|
||||
" return float3x3(float3(x, 0, 0), float3(0, x, 0), float3(0, 0, x));\n"
|
||||
"}\n"
|
||||
"float4x4 float4x4_from_float(float x) {\n"
|
||||
" return float4x4(float4(x, 0, 0, 0), float4(0, x, 0, 0), float4(0, 0, x, 0), float4(0,"
|
||||
" 0, 0, x));\n"
|
||||
"}\n"
|
||||
"fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant sksl_synthetic_uniforms& "
|
||||
"_anonInterface0 [[buffer(1)]], bool _frontFacing [[front_facing]], float4 "
|
||||
"_fragCoord [[position]]) {\n"
|
||||
" Outputs _outputStruct;\n"
|
||||
" thread Outputs* _out = &_outputStruct;\n"
|
||||
" float2x2 m5 = float2x2_from_float(float2x2_from_float4(float4(1.0, 2.0, 3.0, "
|
||||
"4.0))[0][0]);\n"
|
||||
" _out->sk_FragColor = float4((((((((((float2x2_from_float4(float4(1.0, 2.0, 3.0, "
|
||||
"4.0))[0][0] + float2x2_from_float4(float4(0.0))[0][0]) + "
|
||||
"float2x2_from_float4(float4(1.0, 2.0, 3.0, 4.0))[0][0]) + "
|
||||
"float2x2_from_float(1.0)[0][0]) + m5[0][0]) + float2x2(float2(1.0, 2.0), "
|
||||
"float2(3.0, 4.0))[0][0]) + float2x2(float2(5.0, 6.0), float2(7.0, 8.0))[0][0]) + "
|
||||
"float3x3_from_float(1.0)[0][0]) + float3x3_from_float(2.0)[0][0]) + "
|
||||
"float4x4_from_float(1.0)[0][0]) + float4x4_from_float(2.0)[0][0]);\n"
|
||||
" return *_out;\n"
|
||||
"}\n"
|
||||
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user