blob: 9acfbda40754f6cae3f80184187a65096ffa2548 [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 "include/core/SkTypes.h"
#include "include/private/SkSLLayout.h"
#include "include/private/SkSLModifiers.h"
#include "include/private/SkSLProgramElement.h"
#include "include/private/SkSLStatement.h"
#include "include/private/SkTHash.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/SkSLUtil.h"
#include "src/sksl/analysis/SkSLProgramUsage.h"
#include "src/sksl/analysis/SkSLProgramVisitor.h"
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
#include "src/sksl/ir/SkSLFunctionDefinition.h"
#include "src/sksl/ir/SkSLInterfaceBlock.h"
#include "src/sksl/ir/SkSLProgram.h"
#include "src/sksl/ir/SkSLType.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/transform/SkSLTransform.h"
#include <algorithm>
#include <memory>
#include <string>
#include <string_view>
#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()) {
if (funcDef.declaration().returnType().matches(*fContext.fTypes.fHalf4)) {
// main() returns a half4, so make sure we don't dead-strip sk_FragColor.
this->addDeclaringElement(Compiler::FRAGCOLOR_NAME);
}
// Once we find main(), we can stop scanning.
return true;
}
}
return false;
}
static std::string_view GlobalVarBuiltinName(const ProgramElement& elem) {
return elem.as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>().var().name();
}
static std::string_view InterfaceBlockName(const ProgramElement& elem) {
return elem.as<InterfaceBlock>().instanceName();
}
void sortNewElements() {
std::sort(fNewElements.begin(),
fNewElements.end(),
[](const ProgramElement* a, const ProgramElement* b) {
if (a->kind() != b->kind()) {
return a->kind() < b->kind();
}
switch (a->kind()) {
case ProgramElement::Kind::kGlobalVar:
SkASSERT(GlobalVarBuiltinName(*a) != GlobalVarBuiltinName(*b));
return GlobalVarBuiltinName(*a) < GlobalVarBuiltinName(*b);
case ProgramElement::Kind::kInterfaceBlock:
SkASSERT(InterfaceBlockName(*a) != InterfaceBlockName(*b));
return InterfaceBlockName(*a) < InterfaceBlockName(*b);
default:
SkUNREACHABLE;
}
});
}
const Context& fContext;
std::vector<const ProgramElement*> fNewElements;
};
} // namespace
void FindAndDeclareBuiltinVariables(Program& program) {
const Context& context = *program.fContext;
BuiltinVariableScanner scanner(context);
// Find main() in the program and check its return type.
for (auto& e : program.fOwnedElements) {
scanner.visitProgramElement(*e);
}
if (ProgramConfig::IsFragment(program.fConfig->fKind)) {
// 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");
}
// Scan all the variables used by the program and declare any built-ins.
for (const auto& [var, counts] : program.fUsage->fVariableCounts) {
if (var->isBuiltin()) {
scanner.addDeclaringElement(std::string(var->name()));
// Set the FlipRT program input if we find sk_FragCoord or sk_Clockwise.
switch (var->modifiers().fLayout.fBuiltin) {
case SK_FRAGCOORD_BUILTIN:
if (context.fCaps.fCanUseFragCoord) {
program.fInputs.fUseFlipRTUniform =
!context.fConfig->fSettings.fForceNoRTFlip;
}
break;
case SK_CLOCKWISE_BUILTIN:
program.fInputs.fUseFlipRTUniform = !context.fConfig->fSettings.fForceNoRTFlip;
break;
}
}
}
// Sort the referenced builtin functions into a consistent order; otherwise our output will
// become non-deterministic. The exact order isn't particularly important.
scanner.sortNewElements();
// Add all the newly-declared elements to the program, and update ProgramUsage to match.
program.fSharedElements.insert(program.fSharedElements.begin(),
scanner.fNewElements.begin(),
scanner.fNewElements.end());
for (const ProgramElement* element : scanner.fNewElements) {
program.fUsage->add(*element);
}
}
} // namespace Transform
} // namespace SkSL