staticbool check_modifiers(const Context& context, Position pos, ModifierFlags modifierFlags) { const ModifierFlags permitted = ModifierFlag::kInline |
ModifierFlag::kNoInline |
(context.fConfig->isBuiltinCode() ? ModifierFlag::kES3 |
ModifierFlag::kPure |
ModifierFlag::kExport
: ModifierFlag::kNone);
modifierFlags.checkPermittedFlags(context, pos, permitted); if (modifierFlags.isInline() && modifierFlags.isNoInline()) {
context.fErrors->error(pos, "functions cannot be both 'inline' and 'noinline'"); returnfalse;
} returntrue;
}
staticbool check_return_type(const Context& context, Position pos, const Type& returnType) {
ErrorReporter& errors = *context.fErrors; if (returnType.isArray()) {
errors.error(pos, "functions may not return type '" + returnType.displayName() + "'"); returnfalse;
} if (context.fConfig->strictES2Mode() && returnType.isOrContainsArray()) {
errors.error(pos, "functions may not return structs containing arrays"); returnfalse;
} if (!context.fConfig->isBuiltinCode() && returnType.componentType().isOpaque()) {
errors.error(pos, "functions may not return opaque type '" + returnType.displayName() + "'"); returnfalse;
} returntrue;
}
staticbool check_parameters(const Context& context,
TArray<std::unique_ptr<Variable>>& parameters,
ModifierFlags modifierFlags,
IntrinsicKind intrinsicKind) { // Check modifiers on each function parameter. for (auto& param : parameters) { const Type& type = param->type();
ModifierFlags permittedFlags = ModifierFlag::kConst | ModifierFlag::kIn;
LayoutFlags permittedLayoutFlags = LayoutFlag::kNone; if (!type.isOpaque()) {
permittedFlags |= ModifierFlag::kOut;
} if (type.isStorageTexture()) { // We allow `readonly`, `writeonly` and `layout(pixel-format)` on storage textures.
permittedFlags |= ModifierFlag::kReadOnly | ModifierFlag::kWriteOnly;
permittedLayoutFlags |= LayoutFlag::kAllPixelFormats;
// Intrinsics are allowed to accept any pixel format, but user code must explicitly // specify a pixel format like `layout(rgba32f)`. if (intrinsicKind == kNotIntrinsic &&
!(param->layout().fFlags & LayoutFlag::kAllPixelFormats)) {
context.fErrors->error(param->fPosition, "storage texture parameters must specify " "a pixel format layout-qualifier"); returnfalse;
}
}
param->modifierFlags().checkPermittedFlags(context, param->modifiersPosition(),
permittedFlags);
param->layout().checkPermittedLayout(context, param->modifiersPosition(),
permittedLayoutFlags);
// Public Runtime Effects aren't allowed to pass shader/colorFilter/blender types to // function calls. You can pass other opaque types to functions safely; this restriction is // specific to "child" objects. if (!ProgramConfig::AllowsPrivateIdentifiers(context.fConfig->fKind) &&
type.isEffectChild()) {
context.fErrors->error(param->fPosition, "parameters of type '" + type.displayName() + "' not allowed"); returnfalse;
}
// Pure functions should not change any state, and should be safe to eliminate if their // result is not used; this is incompatible with out-parameters, so we forbid it here. // (We don't exhaustively guard against pure functions changing global state in other ways, // though, since they aren't allowed in user code.) if (modifierFlags.isPure() && (param->modifierFlags() & ModifierFlag::kOut)) {
context.fErrors->error(param->modifiersPosition(), "pure functions cannot have out parameters"); returnfalse;
}
} returntrue;
}
auto paramIsCoords = [&](int idx) { const Variable& p = *parameters[idx]; return type_is_valid_for_coords(p.type()) && p.modifierFlags() == ModifierFlag::kNone;
};
auto paramIsColor = [&](int idx) { const Variable& p = *parameters[idx]; return type_is_valid_for_color(p.type()) && p.modifierFlags() == ModifierFlag::kNone;
};
auto paramIsConstInAttributes = [&](int idx) { const Variable& p = *parameters[idx]; return typeIsValidForAttributes(p.type()) && p.modifierFlags() == ModifierFlag::kConst;
};
auto paramIsConstInVaryings = [&](int idx) { const Variable& p = *parameters[idx]; return typeIsValidForVaryings(p.type()) && p.modifierFlags() == ModifierFlag::kConst;
};
auto paramIsOutColor = [&](int idx) { const Variable& p = *parameters[idx]; return type_is_valid_for_color(p.type()) && p.modifierFlags() == ModifierFlag::kOut;
};
switch (kind) { case ProgramKind::kRuntimeColorFilter: case ProgramKind::kPrivateRuntimeColorFilter: { // (half4|float4) main(half4|float4) if (!type_is_valid_for_color(returnType)) {
errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'"); returnfalse;
} bool validParams = (parameters.size() == 1 && paramIsColor(0)); if (!validParams) {
errors.error(pos, "'main' parameter must be 'vec4', 'float4', or 'half4'"); returnfalse;
} break;
} case ProgramKind::kRuntimeShader: case ProgramKind::kPrivateRuntimeShader: { // (half4|float4) main(float2) if (!type_is_valid_for_color(returnType)) {
errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'"); returnfalse;
} if (!(parameters.size() == 1 && paramIsCoords(0))) {
errors.error(pos, "'main' parameter must be 'float2' or 'vec2'"); returnfalse;
} break;
} case ProgramKind::kRuntimeBlender: case ProgramKind::kPrivateRuntimeBlender: { // (half4|float4) main(half4|float4, half4|float4) if (!type_is_valid_for_color(returnType)) {
errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'"); returnfalse;
} if (!(parameters.size() == 2 && paramIsColor(0) && paramIsColor(1))) {
errors.error(pos, "'main' parameters must be (vec4|float4|half4, " "vec4|float4|half4)"); returnfalse;
} break;
} case ProgramKind::kMeshVertex: { // Varyings main(const Attributes) if (!typeIsValidForVaryings(returnType)) {
errors.error(pos, "'main' must return 'Varyings'."); returnfalse;
} if (!(parameters.size() == 1 && paramIsConstInAttributes(0))) {
errors.error(pos, "'main' parameter must be 'const Attributes'."); returnfalse;
} break;
} case ProgramKind::kMeshFragment: { // float2 main(const Varyings) -or- float2 main(const Varyings, out half4|float4) if (!type_is_valid_for_coords(returnType)) {
errors.error(pos, "'main' must return: 'vec2' or 'float2'"); returnfalse;
} if (!((parameters.size() == 1 && paramIsConstInVaryings(0)) ||
(parameters.size() == 2 && paramIsConstInVaryings(0) && paramIsOutColor(1)))) {
errors.error(pos, "'main' parameters must be (const Varyings, (out (half4|float4))?)"); returnfalse;
} break;
} case ProgramKind::kFragment: case ProgramKind::kGraphiteFragment: case ProgramKind::kGraphiteFragmentES2: { bool validParams = (parameters.size() == 0) ||
(parameters.size() == 1 && paramIsCoords(0)); if (!validParams) {
errors.error(pos, "shader 'main' must be main() or main(float2)"); returnfalse;
} break;
} case ProgramKind::kVertex: case ProgramKind::kGraphiteVertex: case ProgramKind::kGraphiteVertexES2: case ProgramKind::kCompute: if (!returnType.matches(*context.fTypes.fVoid)) {
errors.error(pos, "'main' must return 'void'"); returnfalse;
} if (parameters.size()) {
errors.error(pos, "shader 'main' must have zero parameters"); returnfalse;
} break;
} returntrue;
}
/** * Given a concrete type (`float3`) and a generic type (`$genType`), returns the index of the * concrete type within the generic type's typelist. Returns -1 if there is no match.
*/ staticint find_generic_index(const Type& concreteType, const Type& genericType, bool allowNarrowing) {
SkSpan<const Type* const> genericTypes = genericType.coercibleTypes(); for (size_t index = 0; index < genericTypes.size(); ++index) { if (concreteType.canCoerceTo(*genericTypes[index], allowNarrowing)) { return index;
}
} return -1;
}
/** Returns true if the types match, or if `concreteType` can be found in `maybeGenericType`. */ staticbool type_generically_matches(const Type& concreteType, const Type& maybeGenericType) { return maybeGenericType.isGeneric()
? find_generic_index(concreteType, maybeGenericType, /*allowNarrowing=*/false) != -1
: concreteType.matches(maybeGenericType);
}
/** * Checks a parameter list (params) against the parameters of a function that was declared earlier * (otherParams). Returns true if they match, even if the parameters in `otherParams` contain * generic types.
*/ staticbool parameters_match(SkSpan<const std::unique_ptr<Variable>> params,
SkSpan<Variable* const> otherParams) { // If the param lists are different lengths, they're definitely not a match. if (params.size() != otherParams.size()) { returnfalse;
}
// Figure out a consistent generic index (or bail if we find a contradiction). int genericIndex = -1; for (size_t i = 0; i < params.size(); ++i) { const Type* paramType = ¶ms[i]->type(); const Type* otherParamType = &otherParams[i]->type();
if (otherParamType->isGeneric()) { int genericIndexForThisParam = find_generic_index(*paramType, *otherParamType, /*allowNarrowing=*/false); if (genericIndexForThisParam == -1) { // The type wasn't a match for this generic at all; these params can't be a match. returnfalse;
} if (genericIndex != -1 && genericIndex != genericIndexForThisParam) { // The generic index mismatches from what we determined on a previous parameter. returnfalse;
}
genericIndex = genericIndexForThisParam;
}
}
// Now that we've determined a generic index (if we needed one), do a parameter check. for (size_t i = 0; i < params.size(); i++) { const Type* paramType = ¶ms[i]->type(); const Type* otherParamType = &otherParams[i]->type();
// Make generic types concrete. if (otherParamType->isGeneric()) {
SkASSERT(genericIndex != -1);
SkASSERT(genericIndex < (int)otherParamType->coercibleTypes().size());
otherParamType = otherParamType->coercibleTypes()[genericIndex];
} // Detect type mismatches. if (!paramType->matches(*otherParamType)) { returnfalse;
}
} returntrue;
}
/** * Checks for a previously existing declaration of this function, reporting errors if there is an * incompatible symbol. Returns true and sets outExistingDecl to point to the existing declaration * (or null if none) on success, returns false on error.
*/ staticbool find_existing_declaration(const Context& context,
Position pos,
ModifierFlags modifierFlags,
IntrinsicKind intrinsicKind,
std::string_view name,
TArray<std::unique_ptr<Variable>>& parameters,
Position returnTypePos, const Type* returnType,
FunctionDeclaration** outExistingDecl) { auto invalidDeclDescription = [&]() -> std::string {
TArray<Variable*> paramPtrs;
paramPtrs.reserve_exact(parameters.size()); for (std::unique_ptr<Variable>& param : parameters) {
paramPtrs.push_back(param.get());
} return FunctionDeclaration(context,
pos,
modifierFlags,
name,
std::move(paramPtrs),
returnType,
intrinsicKind)
.description();
};
ErrorReporter& errors = *context.fErrors;
Symbol* entry = context.fSymbolTable->findMutable(name);
*outExistingDecl = nullptr; if (entry) { if (!entry->is<FunctionDeclaration>()) {
errors.error(pos, "symbol '" + std::string(name) + "' was already defined"); returnfalse;
} for (FunctionDeclaration* other = &entry->as<FunctionDeclaration>(); other;
other = other->mutableNextOverload()) {
SkASSERT(name == other->name()); if (!parameters_match(parameters, other->parameters())) { continue;
} if (!type_generically_matches(*returnType, other->returnType())) {
errors.error(returnTypePos, "functions '" + invalidDeclDescription() + "' and '" +
other->description() + "' differ only in return type"); returnfalse;
} for (int i = 0; i < parameters.size(); i++) { if (parameters[i]->modifierFlags() != other->parameters()[i]->modifierFlags() ||
parameters[i]->layout() != other->parameters()[i]->layout()) {
errors.error(parameters[i]->fPosition, "modifiers on parameter " + std::to_string(i + 1) + " differ between declaration and definition"); returnfalse;
}
} if (other->isIntrinsic()) {
errors.error(pos, "duplicate definition of intrinsic function '" +
std::string(name) + "'"); returnfalse;
} if (modifierFlags != other->modifierFlags()) {
errors.error(pos, "functions '" + invalidDeclDescription() + "' and '" +
other->description() + "' differ only in modifiers"); returnfalse;
}
*outExistingDecl = other; break;
} if (!*outExistingDecl && entry->as<FunctionDeclaration>().isMain()) {
errors.error(pos, "duplicate definition of 'main'"); returnfalse;
}
} returntrue;
}
FunctionDeclaration::FunctionDeclaration(const Context& context,
Position pos,
ModifierFlags modifierFlags,
std::string_view name,
TArray<Variable*> parameters, const Type* returnType,
IntrinsicKind intrinsicKind)
: INHERITED(pos, kIRNodeKind, name, /*type=*/nullptr)
, fDefinition(nullptr)
, fParameters(std::move(parameters))
, fReturnType(returnType)
, fModifierFlags(modifierFlags)
, fIntrinsicKind(intrinsicKind)
, fModuleType(context.fConfig->fModuleType)
, fIsMain(name == "main") { int builtinColorIndex = 0; for (const Variable* param : fParameters) { // None of the parameters are allowed to be be null.
SkASSERT(param);
// Keep track of arguments to main for runtime effects. if (fIsMain) { if (ProgramConfig::IsRuntimeShader(context.fConfig->fKind) ||
ProgramConfig::IsFragment(context.fConfig->fKind)) { // If this is a runtime shader, a float2 param is supposed to be the coords. // For testing purposes, we have .sksl inputs that are treated as both runtime // effects and fragment shaders. To make that work, fragment shaders are allowed to // have a coords parameter as well. if (type_is_valid_for_coords(param->type())) {
fHasMainCoordsParameter = true;
}
} elseif (ProgramConfig::IsRuntimeColorFilter(context.fConfig->fKind) ||
ProgramConfig::IsRuntimeBlender(context.fConfig->fKind)) { // If this is a runtime color filter or blender, the params are an input color, // followed by a destination color for blenders. if (type_is_valid_for_color(param->type())) { switch (builtinColorIndex++) { case 0: fHasMainInputColorParameter = true; break; case 1: fHasMainDestColorParameter = true; break; default: /* unknown color parameter */ break;
}
}
}
}
}
}
FunctionDeclaration* FunctionDeclaration::Convert(const Context& context,
Position pos, const Modifiers& modifiers,
std::string_view name,
TArray<std::unique_ptr<Variable>> parameters,
Position returnTypePos, const Type* returnType) { // No layout flag is permissible on a function.
modifiers.fLayout.checkPermittedLayout(context, pos, /*permittedLayoutFlags=*/LayoutFlag::kNone);
// If requested, apply the `noinline` modifier to every function. This allows us to test Runtime // Effects without any inlining, even when the code is later added to a paint.
ModifierFlags modifierFlags = modifiers.fFlags; if (context.fConfig->fSettings.fForceNoInline) {
modifierFlags &= ~ModifierFlag::kInline;
modifierFlags |= ModifierFlag::kNoInline;
}
std::string FunctionDeclaration::mangledName() const { if ((this->isBuiltin() && !this->definition()) || this->isMain()) { // Builtins without a definition (like `sin` or `sqrt`) must use their real names. return std::string(this->name());
} // Built-in functions can have a $ prefix, which will fail to compile in GLSL. Remove the // $ and add a unique mangling specifier, so user code can't conflict with the name.
std::string_view name = this->name(); constchar* builtinMarker = ""; if (skstd::starts_with(name, '$')) {
name.remove_prefix(1);
builtinMarker = "Q"; // a unique, otherwise-unused mangle character
} // Rename function to `funcname_returntypeparamtypes`.
std::string result = std::string(name) + "_" + builtinMarker +
this->returnType().abbreviatedName(); for (const Variable* p : this->parameters()) {
result += p->type().abbreviatedName();
} return result;
}
std::string FunctionDeclaration::description() const {
std::string result = (fModifierFlags ? fModifierFlags.description() + " " : std::string()) +
this->returnType().displayName() + " " + std::string(this->name()) + "("; auto separator = SkSL::String::Separator(); for (const Variable* p : this->parameters()) {
result += separator();
result += p->description();
}
result += ")"; return result;
}
bool FunctionDeclaration::matches(const FunctionDeclaration& f) const { if (this->name() != f.name()) { returnfalse;
}
SkSpan<Variable* const> parameters = this->parameters();
SkSpan<Variable* const> otherParameters = f.parameters(); if (parameters.size() != otherParameters.size()) { returnfalse;
} for (size_t i = 0; i < parameters.size(); i++) { if (!parameters[i]->type().matches(otherParameters[i]->type())) { returnfalse;
}
} returntrue;
}
outParameterTypes->reserve_exact(arguments.size()); int genericIndex = -1; for (int i = 0; i < arguments.size(); i++) { // Non-generic parameters are final as-is. const Type& parameterType = parameters[i]->type(); if (!parameterType.isGeneric()) {
outParameterTypes->push_back(¶meterType); continue;
} // We use the first generic parameter we find to lock in the generic index; // e.g. if we find `float3` here, all `$genType`s will be assumed to be `float3`. if (genericIndex == -1) {
genericIndex = find_generic_index(arguments[i]->type(), parameterType, /*allowNarrowing=*/true); if (genericIndex == -1) { // The passed-in type wasn't a match for ANY of the generic possibilities. // This function isn't a match at all. returnfalse;
}
}
outParameterTypes->push_back(parameterType.coercibleTypes()[genericIndex]);
} // Apply the generic index to our return type. const Type& returnType = this->returnType(); if (returnType.isGeneric()) { if (genericIndex == -1) { // We don't support functions with a generic return type and no other generics. returnfalse;
}
*outReturnType = returnType.coercibleTypes()[genericIndex];
} else {
*outReturnType = &returnType;
} returntrue;
}
} // namespace SkSL
Messung V0.5
¤ Dauer der Verarbeitung: 0.42 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.