blob: 9e2dce8233e709092d086a07dc344013e7e73f92 [file] [log] [blame]
/*
* Copyright 2022 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/SkSpan.h"
#include "include/private/SkSLDefines.h"
#include "include/private/SkSLIRNode.h"
#include "include/private/SkSLLayout.h"
#include "include/private/SkSLModifiers.h"
#include "include/private/SkSLStatement.h"
#include "include/private/SkTArray.h"
#include "include/private/SkTHash.h"
#include "include/sksl/SkSLPosition.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
#include "src/sksl/codegen/SkSLRasterPipelineCodeGenerator.h"
#include "src/sksl/ir/SkSLBlock.h"
#include "src/sksl/ir/SkSLConstructorCompound.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
#include "src/sksl/ir/SkSLFunctionDefinition.h"
#include "src/sksl/ir/SkSLLiteral.h"
#include "src/sksl/ir/SkSLReturnStatement.h"
#include "src/sksl/ir/SkSLType.h"
#include "src/sksl/ir/SkSLVariable.h"
#include <optional>
#include <string>
#include <vector>
namespace SkSL {
namespace RP {
class Generator {
public:
Generator(const SkSL::Program& program) : fProgram(program) {}
/** Converts the SkSL main() function into a set of Instructions. */
bool writeProgram(const FunctionDefinition& function);
/**
* Converts an SkSL function into a set of Instructions. Returns nullopt if the function
* contained unsupported statements or expressions.
*/
std::optional<SlotRange> writeFunction(const IRNode& callSite,
const FunctionDefinition& function,
SkSpan<const SlotRange> args);
/** Implements low-level slot creation; slots will not be known to the debugger. */
SlotRange createSlots(int numSlots);
/** Creates slots associated with an SkSL variable or return value. */
SlotRange createSlots(std::string name,
const Type& type,
Position pos,
bool isFunctionReturnValue);
/** Looks up the slots associated with an SkSL variable; creates the slot if necessary. */
SlotRange getSlots(const Variable& v);
/**
* Looks up the slots associated with an SkSL function's return value; creates the range if
* necessary. Note that recursion is never supported, so we don't need to maintain return values
* in a stack; we can just statically allocate one slot per function call-site.
*/
SlotRange getFunctionSlots(const IRNode& callSite, const FunctionDeclaration& f);
/** The Builder stitches our instructions together into Raster Pipeline code. */
Builder* builder() { return &fBuilder; }
/** Appends a statement to the program. */
bool writeStatement(const Statement& s);
bool writeBlock(const Block& b);
bool writeReturnStatement(const ReturnStatement& r);
/** Pushes an expression to the value stack. */
bool pushExpression(const Expression& e);
bool pushConstructorCompound(const ConstructorCompound& c);
bool pushLiteral(const Literal& l);
/** Pops an expression from the value stack and copies it into slots. */
void popToSlotRange(SlotRange r) { fBuilder.pop_slots(r); }
private:
[[maybe_unused]] const SkSL::Program& fProgram;
Builder fBuilder;
SkTHashMap<const IRNode*, SlotRange> fSlotMap;
int fSlotCount = 0;
SkTArray<SlotRange> fFunctionStack;
};
SlotRange Generator::createSlots(int numSlots) {
SlotRange range = {fSlotCount, numSlots};
fSlotCount += numSlots;
return range;
}
SlotRange Generator::createSlots(std::string name,
const Type& type,
Position pos,
bool isFunctionReturnValue) {
// TODO(skia:13676): `name`, `pos` and `isFunctionReturnValue` will be used by the debugger.
// For now, ignore these and just create the raw slots.
return this->createSlots(type.slotCount());
}
SlotRange Generator::getSlots(const Variable& v) {
SlotRange* entry = fSlotMap.find(&v);
if (entry != nullptr) {
return *entry;
}
SlotRange range = this->createSlots(std::string(v.name()),
v.type(),
v.fPosition,
/*isFunctionReturnValue=*/false);
fSlotMap.set(&v, range);
return range;
}
SlotRange Generator::getFunctionSlots(const IRNode& callSite, const FunctionDeclaration& f) {
SlotRange* entry = fSlotMap.find(&callSite);
if (entry != nullptr) {
return *entry;
}
SlotRange range = this->createSlots("[" + std::string(f.name()) + "].result",
f.returnType(),
f.fPosition,
/*isFunctionReturnValue=*/true);
fSlotMap.set(&callSite, range);
return range;
}
std::optional<SlotRange> Generator::writeFunction(const IRNode& callSite,
const FunctionDefinition& function,
SkSpan<const SlotRange> args) {
fFunctionStack.push_back(this->getFunctionSlots(callSite, function.declaration()));
if (!this->writeStatement(*function.body())) {
return std::nullopt;
}
SlotRange functionResult = fFunctionStack.back();
fFunctionStack.pop_back();
return functionResult;
}
bool Generator::writeStatement(const Statement& s) {
switch (s.kind()) {
case Statement::Kind::kBlock:
return this->writeBlock(s.as<Block>());
case Statement::Kind::kReturn:
return this->writeReturnStatement(s.as<ReturnStatement>());
case Statement::Kind::kNop:
return true;
default:
// Unsupported statement
return false;
}
}
bool Generator::writeBlock(const Block& b) {
for (const std::unique_ptr<Statement>& stmt : b.children()) {
if (!this->writeStatement(*stmt)) {
return false;
}
}
return true;
}
bool Generator::writeReturnStatement(const ReturnStatement& r) {
// TODO(skia:13676): update the return mask!
if (r.expression()) {
if (!this->pushExpression(*r.expression())) {
return false;
}
this->popToSlotRange(fFunctionStack.back());
}
return true;
}
bool Generator::pushExpression(const Expression& e) {
switch (e.kind()) {
case Expression::Kind::kConstructorCompound:
return this->pushConstructorCompound(e.as<ConstructorCompound>());
case Expression::Kind::kLiteral:
return this->pushLiteral(e.as<Literal>());
default:
// Unsupported expression
return false;
}
}
bool Generator::pushConstructorCompound(const ConstructorCompound& c) {
for (const std::unique_ptr<Expression> &arg : c.arguments()) {
if (!this->pushExpression(*arg)) {
return false;
}
}
return true;
}
bool Generator::pushLiteral(const Literal& l) {
switch (l.type().numberKind()) {
case Type::NumberKind::kFloat:
fBuilder.push_literal_f(l.floatValue());
return true;
case Type::NumberKind::kSigned:
fBuilder.push_literal_i(l.intValue());
return true;
case Type::NumberKind::kUnsigned:
fBuilder.push_literal_u(l.intValue());
return true;
case Type::NumberKind::kBoolean:
fBuilder.push_literal_i(l.boolValue() ? ~0 : 0);
return true;
default:
SkUNREACHABLE;
}
}
bool Generator::writeProgram(const FunctionDefinition& function) {
// Assign slots to the parameters of main; copy src and dst into those slots as appropriate.
SkSTArray<2, SlotRange> args;
for (const SkSL::Variable* param : function.declaration().parameters()) {
switch (param->modifiers().fLayout.fBuiltin) {
case SK_MAIN_COORDS_BUILTIN: {
// Coordinates are passed via RG.
SlotRange fragCoord = this->getSlots(*param);
SkASSERT(fragCoord.count == 2);
fBuilder.store_src_rg(fragCoord);
args.push_back(fragCoord);
break;
}
case SK_INPUT_COLOR_BUILTIN: {
// Input colors are passed via RGBA.
SlotRange srcColor = this->getSlots(*param);
SkASSERT(srcColor.count == 4);
fBuilder.store_src(srcColor);
args.push_back(srcColor);
break;
}
case SK_DEST_COLOR_BUILTIN: {
// Dest colors are passed via dRGBA.
SlotRange destColor = this->getSlots(*param);
SkASSERT(destColor.count == 4);
fBuilder.store_dst(destColor);
args.push_back(destColor);
break;
}
default: {
SkDEBUGFAIL("Invalid parameter to main()");
return false;
}
}
}
// Initialize the program.
fBuilder.init_lane_masks();
// Invoke main().
std::optional<SlotRange> mainResult = this->writeFunction(function, function, args);
if (!mainResult.has_value()) {
return false;
}
// Move the result of main() from slots into RGBA. Allow dRGBA to remain in a trashed state.
SkASSERT(mainResult->count == 4);
fBuilder.load_src(*mainResult);
return true;
}
} // namespace RP
std::unique_ptr<RP::Program> MakeRasterPipelineProgram(const SkSL::Program& program,
const FunctionDefinition& function) {
// TODO(skia:13676): add mechanism for uniform passing
RP::Generator generator(program);
if (!generator.writeProgram(function)) {
return nullptr;
}
return generator.builder()->finish();
}
} // namespace SkSL