Add support for array-cast syntax in SkSL.

Compiling a program with "allow narrowing conversions" actually fixes up
narrowing casts in the program by inserting casts wherever they would be
needed for type-correctness. For instance, compiling the statement
    `half h = myFloat;`
inserts an appropriate narrowing cast:
    `half h = half(myFloat);`.

The Pipeline stage code generator relies on this behavior, as when it
re-emits a runtime effect into a complete SkSL program, the narrowing-
conversions flag will no longer be set, but that is okay, because the
emitted code now contains typecasts anywhere they would be necessary.

Logically, this implies that anything which supports narrowing
conversions must be castable between high and low precision. In GLSL and
SPIR-V, such a cast is trivial, because the types are the same and the
precision qualifiers are treated as individual hints on each variable.
In Metal, we dodge the issue by only emitting full-precision types. But
we also need to emit raw SkSL from an SkSL program (that is what the
Pipeline stage generator does).

SkSL already supported every typical cast, but GLSL lacked any syntax
for casting an array to a different type. This meant SkSL had no array
casting syntax as well. SkSL now has array-cast syntax, but it is only
allowed for casting low/high-precision arrays to the same base type.
(You can't cast an int array to float, or a signed array to unsigned.)

Change-Id: Ia20933541c3bd4a946c1ea38209f93008acdb9cb
Bug: skia:12248
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/437687
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
This commit is contained in:
John Stiles 2021-08-10 16:03:44 -04:00 committed by SkCQ
parent 23c616fc05
commit 26487162fe
10 changed files with 365 additions and 4 deletions

View File

@ -272,6 +272,7 @@ sksl_shared_tests = [
"/sksl/intrinsics/Trunc.sksl",
"/sksl/intrinsics/UintBitsToFloat.sksl",
"/sksl/intrinsics/Unpack.sksl",
"/sksl/shared/ArrayCast.sksl",
"/sksl/shared/ArrayComparison.sksl",
"/sksl/shared/ArrayConstructors.sksl",
"/sksl/shared/ArrayIndexTypes.sksl",

View File

@ -0,0 +1,22 @@
/*#pragma settings UsesPrecisionModifiers*/
uniform half4 colorGreen, colorRed;
half4 main(float2 coords) {
float f[4] = float[4](1, 2, 3, 4);
half h[4] = half[4](f);
f = float[4](h);
h = half[4](f);
int3 i3[3] = int3[3](int3(1), int3(2), int3(3));
short3 s3[3] = short3[3](i3);
i3 = int3[3](s3);
s3 = short3[3](i3);
half2x2 h2x2[2] = half2x2[2](half2x2(1, 2, 3, 4), half2x2(5, 6, 7, 8));
float2x2 f2x2[2] = float2x2[2](h2x2);
f2x2 = float2x2[2](h2x2);
h2x2 = half2x2[2](f2x2);
return (f == h && i3 == s3 && f2x2 == h2x2) ? colorGreen : colorRed;
}

View File

@ -1659,6 +1659,16 @@ void IRGenerator::start(const ParsedModule& base,
fSymbolTable->addAlias("uint2", fContext.fTypes.fUInt2.get());
fSymbolTable->addAlias("uint3", fContext.fTypes.fUInt3.get());
fSymbolTable->addAlias("uint4", fContext.fTypes.fUInt4.get());
fSymbolTable->addAlias("short", fContext.fTypes.fShort.get());
fSymbolTable->addAlias("short2", fContext.fTypes.fShort2.get());
fSymbolTable->addAlias("short3", fContext.fTypes.fShort3.get());
fSymbolTable->addAlias("short4", fContext.fTypes.fShort4.get());
fSymbolTable->addAlias("ushort", fContext.fTypes.fUShort.get());
fSymbolTable->addAlias("ushort2", fContext.fTypes.fUShort2.get());
fSymbolTable->addAlias("ushort3", fContext.fTypes.fUShort3.get());
fSymbolTable->addAlias("ushort4", fContext.fTypes.fUShort4.get());
}
}

View File

@ -435,10 +435,8 @@ void PipelineStageCodeGenerator::writeExpression(const Expression& expr,
case Expression::Kind::kIntLiteral:
this->write(expr.description());
break;
case Expression::Kind::kConstructorArrayCast:
this->writeExpression(*expr.as<ConstructorArrayCast>().argument(), parentPrecedence);
break;
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorArrayCast:
case Expression::Kind::kConstructorCompound:
case Expression::Kind::kConstructorCompoundCast:
case Expression::Kind::kConstructorDiagonalMatrix:

View File

@ -6,6 +6,7 @@
*/
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
namespace SkSL {
@ -22,6 +23,20 @@ std::unique_ptr<Expression> ConstructorArray::Convert(const Context& context,
return nullptr;
}
// If there is a single argument containing an array of matching size and the types are
// coercible, this is actually a cast. i.e., `half[10](myFloat10Array)`. This isn't a GLSL
// feature, but the Pipeline stage code generator needs this functionality so that code which
// was originally compiled with "allow narrowing conversions" enabled can be later recompiled
// without narrowing conversions (we patch over these conversions with an explicit cast).
if (args.size() == 1) {
const Expression& expr = *args.front();
const Type& exprType = expr.type();
if (exprType.isArray() && exprType.canCoerceTo(type, /*allowNarrowing=*/true)) {
return ConstructorArrayCast::Make(context, offset, type, std::move(args.front()));
}
}
// Check that the number of constructor arguments matches the array size.
if (type.columns() != args.count()) {
context.fErrors.error(offset, String::printf("invalid arguments to '%s' constructor "

View File

@ -225,6 +225,7 @@ SKSL_TEST_ES3(SkSLArrayNarrowingConversions, "runtime/ArrayNarrowingConversion
SKSL_TEST_ES3(SkSLArrayComparison, "shared/ArrayComparison.sksl")
SKSL_TEST_ES3(SkSLArrayConstructors, "shared/ArrayConstructors.sksl")
SKSL_TEST_ES3(SkSLArrayCast, "shared/ArrayCast.sksl")
SKSL_TEST(SkSLArrayTypes, "shared/ArrayTypes.sksl")
SKSL_TEST(SkSLAssignment, "shared/Assignment.sksl")
SKSL_TEST(SkSLCastsRoundTowardZero, "shared/CastsRoundTowardZero.sksl")

View File

@ -10,5 +10,5 @@ half4 main(float2 coords)
;
const float cf2[2] = float[2](1.0, 2.0);
;
return half4(((i2 == s2 && f2 == h2) && i2 == int[2](1, 2)) && h2 == cf2 ? colorGreen : colorRed);
return half4(((i2 == int[2](s2) && f2 == float[2](h2)) && i2 == int[2](1, 2)) && float[2](h2) == cf2 ? colorGreen : colorRed);
}

View File

@ -0,0 +1,233 @@
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %_entrypoint_v "_entrypoint" %sk_FragColor %sk_Clockwise
OpExecutionMode %_entrypoint_v OriginUpperLeft
OpName %sk_FragColor "sk_FragColor"
OpName %sk_Clockwise "sk_Clockwise"
OpName %_UniformBuffer "_UniformBuffer"
OpMemberName %_UniformBuffer 0 "colorGreen"
OpMemberName %_UniformBuffer 1 "colorRed"
OpName %_entrypoint_v "_entrypoint_v"
OpName %main "main"
OpName %f "f"
OpName %h "h"
OpName %i3 "i3"
OpName %s3 "s3"
OpName %h2x2 "h2x2"
OpName %f2x2 "f2x2"
OpDecorate %sk_FragColor RelaxedPrecision
OpDecorate %sk_FragColor Location 0
OpDecorate %sk_FragColor Index 0
OpDecorate %sk_Clockwise BuiltIn FrontFacing
OpMemberDecorate %_UniformBuffer 0 Offset 0
OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision
OpMemberDecorate %_UniformBuffer 1 Offset 16
OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision
OpDecorate %_UniformBuffer Block
OpDecorate %10 Binding 0
OpDecorate %10 DescriptorSet 0
OpDecorate %_arr_float_int_4 ArrayStride 16
OpDecorate %h RelaxedPrecision
OpDecorate %39 RelaxedPrecision
OpDecorate %_arr_v3int_int_3 ArrayStride 16
OpDecorate %55 RelaxedPrecision
OpDecorate %_arr_mat2v2float_int_2 ArrayStride 32
OpDecorate %61 RelaxedPrecision
OpDecorate %62 RelaxedPrecision
OpDecorate %63 RelaxedPrecision
OpDecorate %68 RelaxedPrecision
OpDecorate %69 RelaxedPrecision
OpDecorate %70 RelaxedPrecision
OpDecorate %74 RelaxedPrecision
OpDecorate %75 RelaxedPrecision
OpDecorate %79 RelaxedPrecision
OpDecorate %98 RelaxedPrecision
OpDecorate %118 RelaxedPrecision
OpDecorate %152 RelaxedPrecision
OpDecorate %154 RelaxedPrecision
OpDecorate %155 RelaxedPrecision
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%sk_FragColor = OpVariable %_ptr_Output_v4float Output
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%_UniformBuffer = OpTypeStruct %v4float %v4float
%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
%10 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
%void = OpTypeVoid
%15 = OpTypeFunction %void
%v2float = OpTypeVector %float 2
%float_0 = OpConstant %float 0
%19 = OpConstantComposite %v2float %float_0 %float_0
%_ptr_Function_v2float = OpTypePointer Function %v2float
%23 = OpTypeFunction %v4float %_ptr_Function_v2float
%int = OpTypeInt 32 1
%int_4 = OpConstant %int 4
%_arr_float_int_4 = OpTypeArray %float %int_4
%_ptr_Function__arr_float_int_4 = OpTypePointer Function %_arr_float_int_4
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%float_3 = OpConstant %float 3
%float_4 = OpConstant %float 4
%_ptr_Function__arr_float_int_4_0 = OpTypePointer Function %_arr_float_int_4
%v3int = OpTypeVector %int 3
%int_3 = OpConstant %int 3
%_arr_v3int_int_3 = OpTypeArray %v3int %int_3
%_ptr_Function__arr_v3int_int_3 = OpTypePointer Function %_arr_v3int_int_3
%int_1 = OpConstant %int 1
%47 = OpConstantComposite %v3int %int_1 %int_1 %int_1
%int_2 = OpConstant %int 2
%49 = OpConstantComposite %v3int %int_2 %int_2 %int_2
%50 = OpConstantComposite %v3int %int_3 %int_3 %int_3
%_ptr_Function__arr_v3int_int_3_0 = OpTypePointer Function %_arr_v3int_int_3
%mat2v2float = OpTypeMatrix %v2float 2
%_arr_mat2v2float_int_2 = OpTypeArray %mat2v2float %int_2
%_ptr_Function__arr_mat2v2float_int_2 = OpTypePointer Function %_arr_mat2v2float_int_2
%float_5 = OpConstant %float 5
%float_6 = OpConstant %float 6
%float_7 = OpConstant %float 7
%float_8 = OpConstant %float 8
%_ptr_Function__arr_mat2v2float_int_2_0 = OpTypePointer Function %_arr_mat2v2float_int_2
%false = OpConstantFalse %bool
%v3bool = OpTypeVector %bool 3
%v2bool = OpTypeVector %bool 2
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%int_0 = OpConstant %int 0
%_entrypoint_v = OpFunction %void None %15
%16 = OpLabel
%20 = OpVariable %_ptr_Function_v2float Function
OpStore %20 %19
%22 = OpFunctionCall %v4float %main %20
OpStore %sk_FragColor %22
OpReturn
OpFunctionEnd
%main = OpFunction %v4float None %23
%24 = OpFunctionParameter %_ptr_Function_v2float
%25 = OpLabel
%f = OpVariable %_ptr_Function__arr_float_int_4 Function
%h = OpVariable %_ptr_Function__arr_float_int_4_0 Function
%i3 = OpVariable %_ptr_Function__arr_v3int_int_3 Function
%s3 = OpVariable %_ptr_Function__arr_v3int_int_3_0 Function
%h2x2 = OpVariable %_ptr_Function__arr_mat2v2float_int_2 Function
%f2x2 = OpVariable %_ptr_Function__arr_mat2v2float_int_2_0 Function
%144 = OpVariable %_ptr_Function_v4float Function
%35 = OpCompositeConstruct %_arr_float_int_4 %float_1 %float_2 %float_3 %float_4
OpStore %f %35
%38 = OpLoad %_arr_float_int_4 %f
OpStore %h %38
%39 = OpLoad %_arr_float_int_4 %h
OpStore %f %39
%40 = OpLoad %_arr_float_int_4 %f
OpStore %h %40
%51 = OpCompositeConstruct %_arr_v3int_int_3 %47 %49 %50
OpStore %i3 %51
%54 = OpLoad %_arr_v3int_int_3 %i3
OpStore %s3 %54
%55 = OpLoad %_arr_v3int_int_3 %s3
OpStore %i3 %55
%56 = OpLoad %_arr_v3int_int_3 %i3
OpStore %s3 %56
%61 = OpCompositeConstruct %v2float %float_1 %float_2
%62 = OpCompositeConstruct %v2float %float_3 %float_4
%63 = OpCompositeConstruct %mat2v2float %61 %62
%68 = OpCompositeConstruct %v2float %float_5 %float_6
%69 = OpCompositeConstruct %v2float %float_7 %float_8
%70 = OpCompositeConstruct %mat2v2float %68 %69
%71 = OpCompositeConstruct %_arr_mat2v2float_int_2 %63 %70
OpStore %h2x2 %71
%74 = OpLoad %_arr_mat2v2float_int_2 %h2x2
OpStore %f2x2 %74
%75 = OpLoad %_arr_mat2v2float_int_2 %h2x2
OpStore %f2x2 %75
%76 = OpLoad %_arr_mat2v2float_int_2 %f2x2
OpStore %h2x2 %76
%78 = OpLoad %_arr_float_int_4 %f
%79 = OpLoad %_arr_float_int_4 %h
%80 = OpCompositeExtract %float %78 0
%81 = OpCompositeExtract %float %79 0
%82 = OpFOrdEqual %bool %80 %81
%83 = OpCompositeExtract %float %78 1
%84 = OpCompositeExtract %float %79 1
%85 = OpFOrdEqual %bool %83 %84
%86 = OpLogicalAnd %bool %85 %82
%87 = OpCompositeExtract %float %78 2
%88 = OpCompositeExtract %float %79 2
%89 = OpFOrdEqual %bool %87 %88
%90 = OpLogicalAnd %bool %89 %86
%91 = OpCompositeExtract %float %78 3
%92 = OpCompositeExtract %float %79 3
%93 = OpFOrdEqual %bool %91 %92
%94 = OpLogicalAnd %bool %93 %90
OpSelectionMerge %96 None
OpBranchConditional %94 %95 %96
%95 = OpLabel
%97 = OpLoad %_arr_v3int_int_3 %i3
%98 = OpLoad %_arr_v3int_int_3 %s3
%99 = OpCompositeExtract %v3int %97 0
%100 = OpCompositeExtract %v3int %98 0
%101 = OpIEqual %v3bool %99 %100
%103 = OpAll %bool %101
%104 = OpCompositeExtract %v3int %97 1
%105 = OpCompositeExtract %v3int %98 1
%106 = OpIEqual %v3bool %104 %105
%107 = OpAll %bool %106
%108 = OpLogicalAnd %bool %107 %103
%109 = OpCompositeExtract %v3int %97 2
%110 = OpCompositeExtract %v3int %98 2
%111 = OpIEqual %v3bool %109 %110
%112 = OpAll %bool %111
%113 = OpLogicalAnd %bool %112 %108
OpBranch %96
%96 = OpLabel
%114 = OpPhi %bool %false %25 %113 %95
OpSelectionMerge %116 None
OpBranchConditional %114 %115 %116
%115 = OpLabel
%117 = OpLoad %_arr_mat2v2float_int_2 %f2x2
%118 = OpLoad %_arr_mat2v2float_int_2 %h2x2
%119 = OpCompositeExtract %mat2v2float %117 0
%120 = OpCompositeExtract %mat2v2float %118 0
%122 = OpCompositeExtract %v2float %119 0
%123 = OpCompositeExtract %v2float %120 0
%124 = OpFOrdEqual %v2bool %122 %123
%125 = OpAll %bool %124
%126 = OpCompositeExtract %v2float %119 1
%127 = OpCompositeExtract %v2float %120 1
%128 = OpFOrdEqual %v2bool %126 %127
%129 = OpAll %bool %128
%130 = OpLogicalAnd %bool %125 %129
%131 = OpCompositeExtract %mat2v2float %117 1
%132 = OpCompositeExtract %mat2v2float %118 1
%133 = OpCompositeExtract %v2float %131 0
%134 = OpCompositeExtract %v2float %132 0
%135 = OpFOrdEqual %v2bool %133 %134
%136 = OpAll %bool %135
%137 = OpCompositeExtract %v2float %131 1
%138 = OpCompositeExtract %v2float %132 1
%139 = OpFOrdEqual %v2bool %137 %138
%140 = OpAll %bool %139
%141 = OpLogicalAnd %bool %136 %140
%142 = OpLogicalAnd %bool %141 %130
OpBranch %116
%116 = OpLabel
%143 = OpPhi %bool %false %96 %142 %115
OpSelectionMerge %148 None
OpBranchConditional %143 %146 %147
%146 = OpLabel
%149 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
%152 = OpLoad %v4float %149
OpStore %144 %152
OpBranch %148
%147 = OpLabel
%153 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
%154 = OpLoad %v4float %153
OpStore %144 %154
OpBranch %148
%148 = OpLabel
%155 = OpLoad %v4float %144
OpReturnValue %155
OpFunctionEnd

View File

@ -0,0 +1,21 @@
#version 400
precision mediump float;
precision mediump sampler2D;
out mediump vec4 sk_FragColor;
uniform mediump vec4 colorGreen;
uniform mediump vec4 colorRed;
mediump vec4 main() {
highp float f[4] = float[4](1.0, 2.0, 3.0, 4.0);
mediump float h[4] = f;
f = h;
h = f;
highp ivec3 i3[3] = ivec3[3](ivec3(1), ivec3(2), ivec3(3));
mediump ivec3 s3[3] = i3;
i3 = s3;
s3 = i3;
mediump mat2 h2x2[2] = mat2[2](mat2(1.0, 2.0, 3.0, 4.0), mat2(5.0, 6.0, 7.0, 8.0));
highp mat2 f2x2[2] = h2x2;
f2x2 = h2x2;
h2x2 = f2x2;
return (f == h && i3 == s3) && f2x2 == h2x2 ? colorGreen : colorRed;
}

View File

@ -0,0 +1,60 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Uniforms {
float4 colorGreen;
float4 colorRed;
};
struct Inputs {
};
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
template <typename T1, typename T2, size_t N>
bool operator==(thread const array<T1, N>& left, thread const array<T2, N>& right);
template <typename T1, typename T2, size_t N>
bool operator!=(thread const array<T1, N>& left, thread const array<T2, N>& right);
thread bool operator==(const float2x2 left, const float2x2 right);
thread bool operator!=(const float2x2 left, const float2x2 right);
template <typename T1, typename T2, size_t N>
bool operator==(thread const array<T1, N>& left, thread const array<T2, N>& right) {
for (size_t index = 0; index < N; ++index) {
if (!all(left[index] == right[index])) {
return false;
}
}
return true;
}
template <typename T1, typename T2, size_t N>
bool operator!=(thread const array<T1, N>& left, thread const array<T2, N>& right) {
return !(left == right);
}
thread bool operator==(const float2x2 left, const float2x2 right) {
return all(left[0] == right[0]) &&
all(left[1] == right[1]);
}
thread bool operator!=(const float2x2 left, const float2x2 right) {
return !(left == right);
}
fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
array<float, 4> f = array<float, 4>{1.0, 2.0, 3.0, 4.0};
array<float, 4> h = f;
f = h;
h = f;
array<int3, 3> i3 = array<int3, 3>{int3(1), int3(2), int3(3)};
array<int3, 3> s3 = i3;
i3 = s3;
s3 = i3;
array<float2x2, 2> h2x2 = array<float2x2, 2>{float2x2(float2(1.0, 2.0), float2(3.0, 4.0)), float2x2(float2(5.0, 6.0), float2(7.0, 8.0))};
array<float2x2, 2> f2x2 = h2x2;
f2x2 = h2x2;
h2x2 = f2x2;
_out.sk_FragColor = (f == h && i3 == s3) && f2x2 == h2x2 ? _uniforms.colorGreen : _uniforms.colorRed;
return _out;
}