blob: b8871e61e4cbd70179a8360cce8f3c0da78b38a9 [file] [log] [blame]
/*
* Copyright 2021 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/sksl/ir/SkSLVariable.h"
#include "include/private/SkSLLayout.h"
#include "include/private/SkStringView.h"
#include "include/sksl/SkSLErrorReporter.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLMangler.h"
#include "src/sksl/SkSLModifiersPool.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/SkSLThreadContext.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include <type_traits>
#include <utility>
namespace SkSL {
Variable::~Variable() {
// Unhook this Variable from its associated VarDeclaration, since we're being deleted.
if (fDeclaration) {
fDeclaration->setVar(nullptr);
}
}
const Expression* Variable::initialValue() const {
return fDeclaration ? fDeclaration->value().get() : nullptr;
}
std::string Variable::mangledName() const {
// Only private variables need to use name mangling.
std::string_view name = this->name();
if (!skstd::starts_with(name, '$')) {
return std::string(name);
}
// The $ prefix will fail to compile in GLSL, so replace it with `sk_Priv`.
name.remove_prefix(1);
return "sk_Priv" + std::string(name);
}
std::unique_ptr<Variable> Variable::Convert(const Context& context, Position pos,
Position modifiersPos, const Modifiers& modifiers, const Type* baseType, Position namePos,
std::string_view name, bool isArray, std::unique_ptr<Expression> arraySize,
Variable::Storage storage) {
if (modifiers.fLayout.fLocation == 0 && modifiers.fLayout.fIndex == 0 &&
(modifiers.fFlags & Modifiers::kOut_Flag) &&
ProgramConfig::IsFragment(context.fConfig->fKind) && name != Compiler::FRAGCOLOR_NAME) {
context.fErrors->error(modifiersPos,
"out location=0, index=0 is reserved for sk_FragColor");
}
if (!context.fConfig->fIsBuiltinCode && skstd::starts_with(name, '$')) {
context.fErrors->error(namePos, "name '" + std::string(name) + "' is reserved");
}
if (baseType->isUnsizedArray()) {
if (!ProgramConfig::IsCompute(ThreadContext::Context().fConfig->fKind)) {
context.fErrors->error(pos, "unsized arrays are not permitted here");
} else if (storage != Variable::Storage::kGlobal) {
context.fErrors->error(pos, "unsized arrays must be global");
} else if (!(modifiers.fFlags & (Modifiers::kIn_Flag | Modifiers::kOut_Flag))) {
context.fErrors->error(pos, "unsized arrays must be declared 'in' and/or 'out'");
}
}
if (ProgramConfig::IsCompute(ThreadContext::Context().fConfig->fKind) &&
modifiers.fLayout.fBuiltin == -1) {
if (storage == Variable::Storage::kGlobal &&
(modifiers.fFlags & (Modifiers::kIn_Flag | Modifiers::kOut_Flag))) {
if (baseType->typeKind() != Type::TypeKind::kTexture && !baseType->isArray() &&
!isArray) {
context.fErrors->error(pos, "unsupported compute shader in / out type");
}
if (baseType->typeKind() != Type::TypeKind::kTexture &&
(modifiers.fLayout.fBinding == -1 || modifiers.fLayout.fSet == -1)) {
context.fErrors->error(pos,
"compute shader in / out variables must have a layout binding and set");
}
}
}
return Make(context, pos, modifiersPos, modifiers, baseType, name, isArray,
std::move(arraySize), storage);
}
std::unique_ptr<Variable> Variable::Make(const Context& context, Position pos,
Position modifiersPos, const Modifiers& modifiers, const Type* baseType,
std::string_view name, bool isArray, std::unique_ptr<Expression> arraySize,
Variable::Storage storage) {
const Type* type = baseType;
int arraySizeValue = 0;
if (isArray) {
SkASSERT(arraySize);
arraySizeValue = type->convertArraySize(context, pos, std::move(arraySize));
if (!arraySizeValue) {
return nullptr;
}
type = ThreadContext::SymbolTable()->addArrayDimension(type, arraySizeValue);
}
return std::make_unique<Variable>(pos, modifiersPos, context.fModifiersPool->add(modifiers),
name, type, context.fConfig->fIsBuiltinCode, storage);
}
Variable::ScratchVariable Variable::MakeScratchVariable(const Context& context,
std::string_view baseName,
const Type* type,
const Modifiers& modifiers,
SymbolTable* symbolTable,
std::unique_ptr<Expression> initialValue) {
// $floatLiteral or $intLiteral aren't real types that we can use for scratch variables, so
// replace them if they ever appear here. If this happens, we likely forgot to coerce a type
// somewhere during compilation.
if (type->isLiteral()) {
SkDEBUGFAIL("found a $literal type in MakeScratchVariable");
type = &type->scalarTypeForLiteral();
}
// Out-parameters aren't supported.
SkASSERT(!(modifiers.fFlags & Modifiers::kOut_Flag));
// Provide our new variable with a unique name, and add it to our symbol table.
const std::string* name =
symbolTable->takeOwnershipOfString(context.fMangler->uniqueName(baseName, symbolTable));
// Create our new variable and add it to the symbol table.
ScratchVariable result;
auto var = std::make_unique<Variable>(initialValue ? initialValue->fPosition : Position(),
/*modifiersPosition=*/Position(),
context.fModifiersPool->add(Modifiers{}),
name->c_str(),
type,
symbolTable->isBuiltin(),
Variable::Storage::kLocal);
// If we are creating an array type, reduce it to base type plus array-size.
int arraySize = 0;
if (type->isArray()) {
arraySize = type->columns();
type = &type->componentType();
}
// Create our variable declaration.
result.fVarDecl = VarDeclaration::Make(context, var.get(), type, arraySize,
std::move(initialValue));
result.fVarSymbol = symbolTable->add(std::move(var));
return result;
}
} // namespace SkSL