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:
parent
f151351f47
commit
4449ca6cd4
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -136,7 +136,6 @@ std::unique_ptr<FunctionDefinition> FunctionDefinition::Convert(const Context& c
|
||||
this->copyBuiltinFunctionIfNeeded(func);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return INHERITED::visitExpression(expr);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
|
||||
uniform vec2 u_skRTFlip;
|
||||
in T {
|
||||
int x;
|
||||
};
|
||||
|
@ -8,7 +8,6 @@ struct Outputs {
|
||||
};
|
||||
struct T {
|
||||
int x;
|
||||
float2 u_skRTFlip;
|
||||
};
|
||||
struct Globals {
|
||||
constant T* _anonInterface0;
|
||||
|
Loading…
Reference in New Issue
Block a user