Allow builtin code to reference builtin variables.

The builtin variable scanner did not check builtin code for the presence
of sk_FragColor, etc. We currently get away with this because none of
the existing builtin code uses a builtin variable.

Now FindAndDeclareBuiltinVariables checks shared program elements too.

Change-Id: Ifb3ee3857ef73b18d9e4f406970f0f67681dd4be
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/525042
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2022-03-28 09:42:24 -04:00 committed by SkCQ
parent f151351f47
commit 4449ca6cd4
8 changed files with 91 additions and 84 deletions

View File

@ -479,6 +479,19 @@ std::unique_ptr<Program> Compiler::convertProgram(ProgramKind kind,
return DSLParser(this, settings, kind, std::move(text)).program();
}
void Compiler::updateInputsForBuiltinVariable(const Variable& var) {
switch (var.modifiers().fLayout.fBuiltin) {
case SK_FRAGCOORD_BUILTIN:
if (fContext->fCaps.canUseFragCoord()) {
ThreadContext::Inputs().fUseFlipRTUniform = true;
}
break;
case SK_CLOCKWISE_BUILTIN:
ThreadContext::Inputs().fUseFlipRTUniform = true;
break;
}
}
std::unique_ptr<Expression> Compiler::convertIdentifier(Position pos, std::string_view name) {
const Symbol* result = (*fSymbolTable)[name];
if (!result) {
@ -498,17 +511,6 @@ std::unique_ptr<Expression> Compiler::convertIdentifier(Position pos, std::strin
}
case Symbol::Kind::kVariable: {
const Variable* var = &result->as<Variable>();
const Modifiers& modifiers = var->modifiers();
switch (modifiers.fLayout.fBuiltin) {
case SK_FRAGCOORD_BUILTIN:
if (fContext->fCaps.canUseFragCoord()) {
ThreadContext::Inputs().fUseFlipRTUniform = true;
}
break;
case SK_CLOCKWISE_BUILTIN:
ThreadContext::Inputs().fUseFlipRTUniform = true;
break;
}
// default to kRead_RefKind; this will be corrected later if the variable is written to
return VariableReference::Make(pos, var, VariableReference::RefKind::kRead);
}

View File

@ -150,6 +150,9 @@ public:
std::unique_ptr<Expression> convertIdentifier(Position pos, std::string_view name);
/** Updates the Program's Inputs when a builtin variable is referenced. */
void updateInputsForBuiltinVariable(const Variable& var);
bool toSPIRV(Program& program, OutputStream& out);
bool toSPIRV(Program& program, std::string* out);

View File

@ -136,7 +136,6 @@ std::unique_ptr<FunctionDefinition> FunctionDefinition::Convert(const Context& c
this->copyBuiltinFunctionIfNeeded(func);
}
}
}
return INHERITED::visitExpression(expr);
}

View File

@ -33,80 +33,85 @@
#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) {
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>() && e.as<VariableReference>().variable()->isBuiltin()) {
this->addDeclaringElement(
std::string(e.as<VariableReference>().variable()->name()));
}
return INHERITED::visitExpression(e);
}
const Context& fContext;
std::vector<const ProgramElement*> fNewElements;
bool fPreserveFragColor = false;
using INHERITED = ProgramVisitor;
using INHERITED::visitProgramElement;
};
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);
}
switch (programKind) {
case ProgramKind::kFragment:
// 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");
break;
default:
break;
if (programKind == ProgramKind::kFragment) {
// 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());
scanner.fNewElements.end());
}
} // namespace Transform
} // namespace SkSL
} // namespace Transform
} // namespace SkSL

View File

@ -22,6 +22,10 @@ enum class ProgramKind : int8_t;
namespace Transform {
/**
* Scans the finished program for built-in variables like `sk_FragColor` and adds them to the
* program's shared elements.
*/
void FindAndDeclareBuiltinVariables(const Context& context, ProgramKind programKind,
std::vector<const ProgramElement*>& sharedElements);

View File

@ -1,35 +1,31 @@
### Compilation failed:
error: SPIR-V validation error: Member index 0 is missing a location assignment
%T = OpTypeStruct %int %v2float
%T = OpTypeStruct %int
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %4 %sk_Clockwise
OpEntryPoint Fragment %main "main" %3 %sk_Clockwise
OpExecutionMode %main OriginUpperLeft
OpName %T "T"
OpMemberName %T 0 "x"
OpMemberName %T 1 "u_skRTFlip"
OpName %sk_Clockwise "sk_Clockwise"
OpName %main "main"
OpMemberDecorate %T 0 Offset 0
OpMemberDecorate %T 1 Offset 16384
OpDecorate %T Block
OpDecorate %sk_Clockwise BuiltIn FrontFacing
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%T = OpTypeStruct %int %v2float
%T = OpTypeStruct %int
%_ptr_Input_T = OpTypePointer Input %T
%4 = OpVariable %_ptr_Input_T Input
%3 = OpVariable %_ptr_Input_T Input
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%void = OpTypeVoid
%14 = OpTypeFunction %void
%main = OpFunction %void None %14
%15 = OpLabel
%11 = OpTypeFunction %void
%main = OpFunction %void None %11
%12 = OpLabel
OpReturn
OpFunctionEnd

View File

@ -1,5 +1,4 @@
uniform vec2 u_skRTFlip;
in T {
int x;
};

View File

@ -8,7 +8,6 @@ struct Outputs {
};
struct T {
int x;
float2 u_skRTFlip;
};
struct Globals {
constant T* _anonInterface0;