Honor lowp/mediump/highp precision qualifiers in IRGenerator.

This CL does not update the DSLParser to honor these precision
qualifiers; that will be done in a followup.

Change-Id: Ib629bc99c0e6c7afb550a381d4e3b6ccc26aa64e
Bug: skia:12248
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/436337
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
This commit is contained in:
John Stiles 2021-08-04 16:26:47 -04:00
parent 020143148f
commit b806da4501
11 changed files with 246 additions and 18 deletions

View File

@ -57,6 +57,7 @@ sksl_error_tests = [
"/sksl/errors/ForTypeMismatch.sksl",
"/sksl/errors/FunctionParameterOfVoid.sksl",
"/sksl/errors/GenericArgumentMismatch.sksl",
"/sksl/errors/PrecisionQualifiersDisallowed.sksl",
"/sksl/errors/IfTypeMismatch.sksl",
"/sksl/errors/InVarWithInitializerExpression.sksl",
"/sksl/errors/InterfaceBlockScope.sksl",
@ -533,6 +534,7 @@ sksl_rte_error_tests = [
"/sksl/runtime_errors/IllegalArrayOps.rts",
"/sksl/runtime_errors/IllegalIndexing.rts",
"/sksl/runtime_errors/IllegalOperators.rts",
"/sksl/runtime_errors/IllegalPrecisionQualifiers.rts",
"/sksl/runtime_errors/IllegalRecursionComplex.rts",
"/sksl/runtime_errors/IllegalRecursionMutual.rts",
"/sksl/runtime_errors/IllegalRecursionSimple.rts",

View File

@ -0,0 +1,4 @@
// Precision qualifiers are only allowed in a Runtime Effect.
void h() { highp int x; }
void m() { mediump int x; }
void l() { lowp int x; }

View File

@ -1,9 +1,61 @@
uniform half4 colorGreen;
uniform half4 colorGreen, colorRed;
bool test_scalar() {
mediump float mp = 0.5;
highp float hp = mp;
highp int ihp = 2;
mediump int imp = ihp;
return mp == hp && ihp == imp;
}
bool test_vector() {
mediump vec2 mp2 = vec2(2);
highp vec2 hp2 = mp2;
mediump vec3 mp3 = vec3(3);
highp vec3 hp3 = mp3;
mediump vec4 mp4 = vec4(4);
highp vec4 hp4 = mp4;
highp ivec2 ihp2 = ivec2(2);
mediump ivec2 imp2 = ihp2;
highp ivec3 ihp3 = ivec3(3);
mediump ivec3 imp3 = ihp3;
highp ivec4 ihp4 = ivec4(4);
mediump ivec4 imp4 = ihp4;
return mp2 == hp2 && hp3 == mp3 && mp4 == hp4 &&
imp2 == ihp2 && ihp3 == imp3 && imp4 == ihp4;
}
bool test_matrix() {
mediump mat2 mp2 = mat2(2);
highp mat2 hp2 = mp2;
mediump mat3 mp3 = mat3(3);
highp mat3 hp3 = mp3;
mediump mat4 mp4 = mat4(4);
highp mat4 hp4 = mp4;
return mp2 == hp2 && hp3 == mp3 && mp4 == hp4;
}
bool test_array() {
mediump float mf[1]; mf[0] = 1;
highp float hf[1]; hf[0] = 1;
mediump vec2 mv[2]; mv[0] = vec2(0, 1); mv[1] = vec2(2, 3);
highp vec2 hv[2]; hv[0] = vec2(0, 1); hv[1] = vec2(2, 3);
return mf[0] == hf[0] && hv[0] == mv[0] && mv[1] == hv[1];
}
vec4 main(vec2 coords) {
highp vec4 zero = vec4(0);
mediump vec4 green = colorGreen;
lowp vec4 one = vec4(1);
highp vec4 zero = vec4(0);
mediump vec4 one = vec4(1);
lowp vec4 green = colorGreen;
green = green * one + zero;
return green * one + zero;
highp vec4 red = colorRed;
red = (red + zero) * one;
return (test_scalar() && test_vector() && test_matrix() && test_array()) ? green : red;
}

View File

@ -0,0 +1,14 @@
// Expect 10 errors
struct S { int i; };
mediump highp int no_multiple_qualifiers;
mediump lowp int no_multiple_qualifiers_2;
highp lowp int no_multiple_qualifiers_3;
highp mediump lowp int no_multiple_qualifiers_4;
highp S no_structs;
mediump bool no_bools;
uniform lowp colorFilter no_opaque;
uniform highp half no_half_highp;
uniform mediump half no_half_mediump;
uniform lowp half no_lowp_mediump;

View File

@ -294,7 +294,8 @@ void IRGenerator::checkVarDeclaration(int offset, const Modifiers& modifiers, co
"float3, or float4 variables");
}
}
int permitted = Modifiers::kConst_Flag;
int permitted = Modifiers::kConst_Flag | Modifiers::kHighp_Flag | Modifiers::kMediump_Flag |
Modifiers::kLowp_Flag;
if (storage == Variable::Storage::kGlobal) {
permitted |= Modifiers::kIn_Flag | Modifiers::kOut_Flag | Modifiers::kUniform_Flag |
Modifiers::kFlat_Flag | Modifiers::kNoPerspective_Flag;
@ -393,6 +394,11 @@ StatementArray IRGenerator::convertVarDeclarations(const ASTNode& decls,
if (!baseType) {
return {};
}
baseType = baseType->applyPrecisionQualifiers(fContext, modifiers, fSymbolTable.get(),
decls.fOffset);
if (!baseType) {
return {};
}
this->checkVarDeclaration(decls.fOffset, modifiers, baseType, storage);

View File

@ -441,6 +441,72 @@ CoercionCost Type::coercionCost(const Type& other) const {
return CoercionCost::Impossible();
}
const Type* Type::applyPrecisionQualifiers(const Context& context,
const Modifiers& modifiers,
SymbolTable* symbols,
int offset) const {
// SkSL doesn't support low precision, so `lowp` is interpreted as medium precision.
bool highp = modifiers.fFlags & Modifiers::kHighp_Flag;
bool mediump = modifiers.fFlags & Modifiers::kMediump_Flag;
bool lowp = modifiers.fFlags & Modifiers::kLowp_Flag;
if (!lowp && !mediump && !highp) {
// No precision qualifiers here. Return the type as-is.
return this;
}
if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
// We want to discourage precision modifiers internally. Instead, use the type that
// corresponds to the precision you need. (e.g. half vs float, short vs int)
context.fErrors.error(offset, "precision qualifiers are not allowed");
return nullptr;
}
if ((int(lowp) + int(mediump) + int(highp)) != 1) {
context.fErrors.error(offset, "only one precision qualifier can be used");
return nullptr;
}
const Type& component = this->componentType();
if (component.highPrecision()) {
if (highp) {
// Type is already high precision, and we are requesting high precision. Return as-is.
return this;
}
// Ascertain the mediump equivalent type for this type, if any.
const Type* mediumpType;
switch (component.numberKind()) {
case Type::NumberKind::kFloat:
mediumpType = context.fTypes.fHalf.get();
break;
case Type::NumberKind::kSigned:
mediumpType = context.fTypes.fShort.get();
break;
case Type::NumberKind::kUnsigned:
mediumpType = context.fTypes.fUShort.get();
break;
default:
mediumpType = nullptr;
break;
}
if (mediumpType) {
// Convert the mediump component type into the final vector/matrix/array type as needed.
return this->isArray()
? symbols->addArrayDimension(mediumpType, this->columns())
: &mediumpType->toCompound(context, this->columns(), this->rows());
}
}
context.fErrors.error(offset, "type '" + this->displayName() +
"' does not support precision qualifiers");
return nullptr;
}
const Type& Type::toCompound(const Context& context, int columns, int rows) const {
SkASSERT(this->isScalar());
if (columns == 1 && rows == 1) {

View File

@ -23,6 +23,7 @@ namespace SkSL {
class BuiltinTypes;
class Context;
class SymbolTable;
struct CoercionCost {
static CoercionCost Free() { return { 0, 0, false }; }
@ -510,6 +511,16 @@ public:
*/
const Type& toCompound(const Context& context, int columns, int rows) const;
/**
* Returns a type which honors the precision qualifiers set in Modifiers. e.g., kMediump_Flag
* when applied to `float2` will return `half2`. Generates an error if the precision qualifiers
* don't make sense, e.g. `highp bool` or `mediump MyStruct`.
*/
const Type* applyPrecisionQualifiers(const Context& context,
const Modifiers& modifiers,
SymbolTable* symbols,
int offset) const;
/**
* Coerces the passed-in expression to this type. If the types are incompatible, reports an
* error and returns null.

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 2: precision qualifiers are not allowed
error: 3: precision qualifiers are not allowed
error: 4: precision qualifiers are not allowed
3 errors

View File

@ -1,6 +1,10 @@
### Compilation failed:
error: 4: 'highp' is not permitted here
error: 5: 'mediump' is not permitted here
error: 6: 'lowp' is not permitted here
3 errors
4 registers, 8 instructions:
0 r0 = uniform32 ptr0 4
1 r1 = uniform32 ptr0 8
2 r2 = uniform32 ptr0 C
3 r3 = uniform32 ptr0 10
loop:
4 store32 ptr1 r0
5 store32 ptr2 r1
6 store32 ptr3 r2
7 store32 ptr4 r3

View File

@ -1,6 +1,56 @@
### Compilation failed:
error: 4: 'highp' is not permitted here
error: 5: 'mediump' is not permitted here
error: 6: 'lowp' is not permitted here
3 errors
uniform half4 colorGreen;
uniform half4 colorRed;
bool test_vector_0()
{
half2 mp2 = half2(2.0);
float2 hp2 = float2(mp2);
half3 mp3 = half3(3.0);
float3 hp3 = float3(mp3);
half4 mp4 = half4(4.0);
float4 hp4 = float4(mp4);
int2 ihp2 = int2(2);
short2 imp2 = short2(ihp2);
int3 ihp3 = int3(3);
short3 imp3 = short3(ihp3);
int4 ihp4 = int4(4);
short4 imp4 = short4(ihp4);
return ((((float2(mp2) == hp2 && hp3 == float3(mp3)) && float4(mp4) == hp4) && int2(imp2) == ihp2) && ihp3 == int3(imp3)) && int4(imp4) == ihp4;
}
bool test_matrix_0()
{
half2x2 mp2 = half2x2(2.0);
float2x2 hp2 = float2x2(mp2);
half3x3 mp3 = half3x3(3.0);
float3x3 hp3 = float3x3(mp3);
half4x4 mp4 = half4x4(4.0);
float4x4 hp4 = float4x4(mp4);
return (float2x2(mp2) == hp2 && hp3 == float3x3(mp3)) && float4x4(mp4) == hp4;
}
bool test_array_0()
{
half mf[1];
mf[0] = 1.0;
float hf[1];
hf[0] = 1.0;
half2 mv[2];
mv[0] = half2(0.0, 1.0);
mv[1] = half2(2.0, 3.0);
float2 hv[2];
hv[0] = float2(0.0, 1.0);
hv[1] = float2(2.0, 3.0);
return (float(mf[0]) == hf[0] && hv[0] == float2(mv[0])) && float2(mv[1]) == hv[1];
}
float4 main(float2 coords)
{
float4 zero = float4(0.0);
half4 one = half4(1.0);
half4 green = colorGreen;
green = half4(float4(green * one) + zero);
float4 red = float4(colorRed);
red = (red + zero) * float4(one);
half _0_mp = 0.5;
float _1_hp = float(_0_mp);
int _2_ihp = 2;
short _3_imp = short(_2_ihp);
return half4((((float(_0_mp) == _1_hp && _2_ihp == int(_3_imp)) && test_vector_0()) && test_matrix_0()) && test_array_0() ? float4(green) : red);
}

View File

@ -0,0 +1,13 @@
### Compilation failed:
error: 5: only one precision qualifier can be used
error: 6: only one precision qualifier can be used
error: 7: only one precision qualifier can be used
error: 8: only one precision qualifier can be used
error: 9: type 'S' does not support precision qualifiers
error: 10: type 'bool' does not support precision qualifiers
error: 11: type 'colorFilter' does not support precision qualifiers
error: 12: type 'half' does not support precision qualifiers
error: 13: type 'half' does not support precision qualifiers
error: 14: type 'half' does not support precision qualifiers
10 errors