blob: f17e28c7496e00f22fe7225558156feeb2ff22c1 [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/transform/SkSLTransform.h"
#include "include/core/SkTypes.h"
#include "include/private/SkSLProgramElement.h"
#include "include/private/SkSLProgramKind.h"
#include "src/sksl/SkSLBuiltinMap.h"
#include "src/sksl/SkSLBuiltinTypes.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/SkSLThreadContext.h"
#include "src/sksl/analysis/SkSLProgramVisitor.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
#include "src/sksl/ir/SkSLFunctionDefinition.h"
#include "src/sksl/ir/SkSLType.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#ifdef SK_DEBUG
#include "src/sksl/ir/SkSLInterfaceBlock.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#endif
#include <memory>
#include <string>
#include <vector>
namespace SkSL {
namespace Transform {
namespace {
class BuiltinVariableScanner : public ProgramVisitor {
public:
BuiltinVariableScanner(const Context& context) : fContext(context) {}
void addDeclaringElement(const std::string& name) {
// If this is the *first* time we've seen this builtin, findAndInclude will return the
// corresponding ProgramElement.
BuiltinMap& builtins = *fContext.fBuiltins;
if (const ProgramElement* decl = builtins.findAndInclude(name)) {
SkASSERT(decl->is<GlobalVarDeclaration>() || decl->is<InterfaceBlock>());
fNewElements.push_back(decl);
}
}
bool visitProgramElement(const ProgramElement& pe) override {
if (pe.is<FunctionDefinition>()) {
const FunctionDefinition& funcDef = pe.as<FunctionDefinition>();
// We synthesize writes to sk_FragColor if main() returns a color, even if it's
// otherwise unreferenced. Check main's return type to see if it's half4.
if (funcDef.declaration().isMain() &&
funcDef.declaration().returnType().matches(*fContext.fTypes.fHalf4)) {
fPreserveFragColor = true;
}
}
return INHERITED::visitProgramElement(pe);
}
bool visitExpression(const Expression& e) override {
if (e.is<VariableReference>()) {
const Variable* var = e.as<VariableReference>().variable();
if (var->isBuiltin()) {
this->addDeclaringElement(std::string(var->name()));
}
ThreadContext::Compiler().updateInputsForBuiltinVariable(*var);
}
return INHERITED::visitExpression(e);
}
const Context& fContext;
std::vector<const ProgramElement*> fNewElements;
bool fPreserveFragColor = false;
using INHERITED = ProgramVisitor;
using INHERITED::visitProgramElement;
};
} // namespace
void FindAndDeclareBuiltinVariables(const Context& context,
ProgramKind programKind,
std::vector<const ProgramElement*>& sharedElements) {
BuiltinVariableScanner scanner(context);
for (auto& e : ThreadContext::ProgramElements()) {
scanner.visitProgramElement(*e);
}
for (auto& e : ThreadContext::SharedElements()) {
scanner.visitProgramElement(*e);
}
if (scanner.fPreserveFragColor) {
// main() returns a half4, so make sure we don't dead-strip sk_FragColor.
scanner.addDeclaringElement(Compiler::FRAGCOLOR_NAME);
}
if (ProgramConfig::IsFragment(programKind)) {
// Vulkan requires certain builtin variables be present, even if they're unused. At one
// time, validation errors would result if sk_Clockwise was missing. Now, it's just (Adreno)
// driver bugs that drop or corrupt draws if they're missing.
scanner.addDeclaringElement("sk_Clockwise");
}
sharedElements.insert(sharedElements.begin(), scanner.fNewElements.begin(),
scanner.fNewElements.end());
}
} // namespace Transform
} // namespace SkSL