Add WGSL support for indexed-swizzle lvalues.
Assignments like `vec.wzyx[r] = value;` are not natively supported
by WGSL, since `vec.wzyx` is not an lvalue in WGSL.
However, we already had a transform pass for these for use in
SPIR-V and Raster Pipeline. WGSL now uses this transform as well.
This change exposed a subtle long-standing issue--the Context
used in a CodeGenerator, by default, was missing various fields,
such as a ProgramConfig. They were missing because the front-end
moves them from the Context into the Program at the end of
compilation, but occasionally the back-end needs to create new IR,
and while assembling IR, we expect these fields to be populated.
e.g. this would cause checks like `type->isAllowedInES2()` to fail
when inside the backend, because the "strict ES2" flag is stored
inside the ProgramConfig.
The CodeGenerator base class now assembles a fully-populated
Context during construction. This mirrors a very similar fix
which was needed in SkRP codegen: http://review.skia.org/656437
Change-Id: Ia4670aba2f9fdccc484c96cccb656feb09926e9f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/706522
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index b6cbda2..8a51c77 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -426,6 +426,8 @@
"shared/PrefixExpressionsES3.sksl",
"shared/StructArrayFollowedByScalar.sksl",
"shared/Structs.sksl",
+ "shared/SwizzleIndexLookup.sksl",
+ "shared/SwizzleIndexStore.sksl",
"shared/TernaryComplexNesting.sksl",
"shared/TernaryExpression.sksl",
"shared/TernaryNesting.sksl",
diff --git a/resources/sksl/BUILD.bazel b/resources/sksl/BUILD.bazel
index 290f70f..fcc6177 100644
--- a/resources/sksl/BUILD.bazel
+++ b/resources/sksl/BUILD.bazel
@@ -1104,6 +1104,8 @@
"shared/PrefixExpressionsES3.sksl",
"shared/StructArrayFollowedByScalar.sksl",
"shared/Structs.sksl",
+ "shared/SwizzleIndexLookup.sksl",
+ "shared/SwizzleIndexStore.sksl",
"shared/TernaryComplexNesting.sksl",
"shared/TernaryExpression.sksl",
"shared/TernaryNesting.sksl",
diff --git a/src/sksl/codegen/SkSLCodeGenerator.h b/src/sksl/codegen/SkSLCodeGenerator.h
index fd58648..14a6bd5 100644
--- a/src/sksl/codegen/SkSLCodeGenerator.h
+++ b/src/sksl/codegen/SkSLCodeGenerator.h
@@ -8,6 +8,8 @@
#ifndef SKSL_CODEGENERATOR
#define SKSL_CODEGENERATOR
+#include "src/sksl/SkSLContext.h"
+#include "src/sksl/SkSLModifiersPool.h"
#include "src/sksl/SkSLOutputStream.h"
#include "src/sksl/ir/SkSLProgram.h"
@@ -19,12 +21,19 @@
*/
class CodeGenerator {
public:
- CodeGenerator(const Context* context, const Program* program, OutputStream* out)
- : fContext(*context)
- , fProgram(*program)
- , fOut(out) {}
+ CodeGenerator(const Context* context, const Program* program, OutputStream* stream)
+ : fProgram(*program)
+ , fContext(fProgram.fContext->fTypes,
+ fProgram.fContext->fCaps,
+ *fProgram.fContext->fErrors)
+ , fOut(stream) {
+ fContext.fModifiersPool = &fModifiersPool;
+ fContext.fConfig = fProgram.fConfig.get();
+ fContext.fModule = fProgram.fContext->fModule;
+ fContext.fSymbolTable = fProgram.fSymbols;
+ }
- virtual ~CodeGenerator() {}
+ virtual ~CodeGenerator() = default;
virtual bool generateCode() = 0;
@@ -48,8 +57,9 @@
static constexpr float kSharpenTexturesBias = -.475f;
#endif
- const Context& fContext;
const Program& fProgram;
+ Context fContext;
+ ModifiersPool fModifiersPool;
OutputStream* fOut;
};
diff --git a/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp b/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
index d53a05d..043d487 100644
--- a/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
@@ -60,6 +60,7 @@
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLVariableReference.h"
+#include "src/sksl/transform/SkSLTransform.h"
#include <algorithm>
#include <cstddef>
@@ -995,7 +996,13 @@
if (e.is<IndexExpression>()) {
const IndexExpression& idx = e.as<IndexExpression>();
if (idx.base()->type().isVector()) {
- return std::make_unique<VectorComponentLValue>(this->assembleIndexExpression(idx));
+ if (std::unique_ptr<Expression> rewrite =
+ Transform::RewriteIndexedSwizzle(fContext, idx)) {
+ return std::make_unique<VectorComponentLValue>(
+ this->assembleExpression(*rewrite, Precedence::kAssignment));
+ } else {
+ return std::make_unique<VectorComponentLValue>(this->assembleIndexExpression(idx));
+ }
} else {
return std::make_unique<PointerLValue>(this->assembleIndexExpression(idx));
}
diff --git a/tests/sksl/shared/SwizzleIndexLookup.wgsl b/tests/sksl/shared/SwizzleIndexLookup.wgsl
new file mode 100644
index 0000000..dd45d38
--- /dev/null
+++ b/tests/sksl/shared/SwizzleIndexLookup.wgsl
@@ -0,0 +1,114 @@
+struct FSIn {
+ @builtin(front_facing) sk_Clockwise: bool,
+ @builtin(position) sk_FragCoord: vec4<f32>,
+};
+struct FSOut {
+ @location(0) sk_FragColor: vec4<f32>,
+};
+struct _GlobalUniforms {
+ colorGreen: vec4<f32>,
+ colorRed: vec4<f32>,
+ testMatrix3x3: mat3x3<f32>,
+ testMatrix4x4: mat4x4<f32>,
+};
+@binding(0) @group(0) var<uniform> _globalUniforms: _GlobalUniforms;
+fn test3x3_b() -> bool {
+ var expected: vec3<f32> = vec3<f32>(3.0, 2.0, 1.0);
+ {
+ var c: i32 = 0;
+ loop {
+ if c < 3 {
+ {
+ var vec: vec3<f32> = _globalUniforms.testMatrix3x3[c];
+ {
+ var r: i32 = 0;
+ loop {
+ if r < 3 {
+ {
+ if (vec.zyx[r] != expected[r]) {
+ {
+ return false;
+ }
+ }
+ }
+ } else {
+ break;
+ }
+ continuing {
+ r = r + i32(1);
+ }
+ }
+ }
+ expected = expected + 3.0;
+ }
+ } else {
+ break;
+ }
+ continuing {
+ c = c + i32(1);
+ }
+ }
+ }
+ return true;
+}
+fn test4x4_b() -> bool {
+ var expected: vec4<f32> = vec4<f32>(4.0, 3.0, 2.0, 1.0);
+ {
+ var c: i32 = 0;
+ loop {
+ if c < 4 {
+ {
+ var vec: vec4<f32> = _globalUniforms.testMatrix4x4[c];
+ {
+ var r: i32 = 0;
+ loop {
+ if r < 4 {
+ {
+ if (vec.wzyx[r] != expected[r]) {
+ {
+ return false;
+ }
+ }
+ }
+ } else {
+ break;
+ }
+ continuing {
+ r = r + i32(1);
+ }
+ }
+ }
+ expected = expected + 4.0;
+ }
+ } else {
+ break;
+ }
+ continuing {
+ c = c + i32(1);
+ }
+ }
+ }
+ return true;
+}
+fn main(coords: vec2<f32>) -> vec4<f32> {
+ var _skTemp0: vec4<f32>;
+ var _skTemp1: bool;
+ let _skTemp2 = test3x3_b();
+ if _skTemp2 {
+ let _skTemp3 = test4x4_b();
+ _skTemp1 = _skTemp3;
+ } else {
+ _skTemp1 = false;
+ }
+ if _skTemp1 {
+ _skTemp0 = _globalUniforms.colorGreen;
+ } else {
+ _skTemp0 = _globalUniforms.colorRed;
+ }
+ return _skTemp0;
+}
+@fragment fn fragmentMain(_stageIn: FSIn) -> FSOut {
+ var _stageOut: FSOut;
+ _stageOut.sk_FragColor = main(_stageIn.sk_FragCoord.xy);
+ return _stageOut;
+}
diff --git a/tests/sksl/shared/SwizzleIndexStore.wgsl b/tests/sksl/shared/SwizzleIndexStore.wgsl
new file mode 100644
index 0000000..370c0fc
--- /dev/null
+++ b/tests/sksl/shared/SwizzleIndexStore.wgsl
@@ -0,0 +1,118 @@
+struct FSIn {
+ @builtin(front_facing) sk_Clockwise: bool,
+ @builtin(position) sk_FragCoord: vec4<f32>,
+};
+struct FSOut {
+ @location(0) sk_FragColor: vec4<f32>,
+};
+struct _GlobalUniforms {
+ colorGreen: vec4<f32>,
+ colorRed: vec4<f32>,
+ testMatrix3x3: mat3x3<f32>,
+ testMatrix4x4: mat4x4<f32>,
+};
+@binding(0) @group(0) var<uniform> _globalUniforms: _GlobalUniforms;
+fn test3x3_b() -> bool {
+ var expected: vec3<f32> = vec3<f32>(3.0, 2.0, 1.0);
+ var vec: vec3<f32>;
+ {
+ var c: i32 = 0;
+ loop {
+ if c < 3 {
+ {
+ {
+ var r: i32 = 0;
+ loop {
+ if r < 3 {
+ {
+ let _skTemp0 = vec3<i32>(2, 1, 0)[r];
+ vec[_skTemp0] = _globalUniforms.testMatrix3x3[c][r];
+ }
+ } else {
+ break;
+ }
+ continuing {
+ r = r + i32(1);
+ }
+ }
+ }
+ if (any(vec != expected)) {
+ {
+ return false;
+ }
+ }
+ expected = expected + 3.0;
+ }
+ } else {
+ break;
+ }
+ continuing {
+ c = c + i32(1);
+ }
+ }
+ }
+ return true;
+}
+fn test4x4_b() -> bool {
+ var expected: vec4<f32> = vec4<f32>(4.0, 3.0, 2.0, 1.0);
+ var vec: vec4<f32>;
+ {
+ var c: i32 = 0;
+ loop {
+ if c < 4 {
+ {
+ {
+ var r: i32 = 0;
+ loop {
+ if r < 4 {
+ {
+ let _skTemp1 = vec4<i32>(3, 2, 1, 0)[r];
+ vec[_skTemp1] = _globalUniforms.testMatrix4x4[c][r];
+ }
+ } else {
+ break;
+ }
+ continuing {
+ r = r + i32(1);
+ }
+ }
+ }
+ if (any(vec != expected)) {
+ {
+ return false;
+ }
+ }
+ expected = expected + 4.0;
+ }
+ } else {
+ break;
+ }
+ continuing {
+ c = c + i32(1);
+ }
+ }
+ }
+ return true;
+}
+fn main(coords: vec2<f32>) -> vec4<f32> {
+ var _skTemp2: vec4<f32>;
+ var _skTemp3: bool;
+ let _skTemp4 = test3x3_b();
+ if _skTemp4 {
+ let _skTemp5 = test4x4_b();
+ _skTemp3 = _skTemp5;
+ } else {
+ _skTemp3 = false;
+ }
+ if _skTemp3 {
+ _skTemp2 = _globalUniforms.colorGreen;
+ } else {
+ _skTemp2 = _globalUniforms.colorRed;
+ }
+ return _skTemp2;
+}
+@fragment fn fragmentMain(_stageIn: FSIn) -> FSOut {
+ var _stageOut: FSOut;
+ _stageOut.sk_FragColor = main(_stageIn.sk_FragCoord.xy);
+ return _stageOut;
+}