2016-07-01 15:22:01 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2016 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2020-10-05 18:18:49 +00:00
|
|
|
#define SK_OPTS_NS skslc_standalone
|
2021-12-21 14:33:35 +00:00
|
|
|
#include "include/core/SkGraphics.h"
|
|
|
|
#include "include/core/SkStream.h"
|
2022-02-01 23:25:15 +00:00
|
|
|
#include "include/private/SkStringView.h"
|
2021-12-21 14:33:35 +00:00
|
|
|
#include "src/core/SkCpu.h"
|
2021-10-28 18:35:40 +00:00
|
|
|
#include "src/core/SkOpts.h"
|
2020-10-05 18:18:49 +00:00
|
|
|
#include "src/opts/SkChecksum_opts.h"
|
2020-12-17 21:02:08 +00:00
|
|
|
#include "src/opts/SkVM_opts.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "src/sksl/SkSLCompiler.h"
|
|
|
|
#include "src/sksl/SkSLFileOutputStream.h"
|
2020-07-28 18:46:53 +00:00
|
|
|
#include "src/sksl/SkSLStringStream.h"
|
2020-09-15 19:37:24 +00:00
|
|
|
#include "src/sksl/SkSLUtil.h"
|
2021-04-13 14:41:57 +00:00
|
|
|
#include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
|
|
|
|
#include "src/sksl/codegen/SkSLVMCodeGenerator.h"
|
2020-07-28 18:46:53 +00:00
|
|
|
#include "src/sksl/ir/SkSLUnresolvedFunction.h"
|
2021-10-18 14:39:20 +00:00
|
|
|
#include "src/sksl/ir/SkSLVarDeclarations.h"
|
2021-12-10 17:03:35 +00:00
|
|
|
#include "src/sksl/tracing/SkVMDebugTrace.h"
|
2021-12-10 22:27:10 +00:00
|
|
|
#include "src/utils/SkShaderUtils.h"
|
2021-12-21 14:33:35 +00:00
|
|
|
#include "src/utils/SkVMVisualizer.h"
|
2016-07-01 15:22:01 +00:00
|
|
|
|
2020-11-20 21:28:50 +00:00
|
|
|
#include "spirv-tools/libspirv.hpp"
|
|
|
|
|
2020-10-05 18:18:49 +00:00
|
|
|
#include <fstream>
|
2020-11-13 21:13:18 +00:00
|
|
|
#include <limits.h>
|
2022-02-03 22:08:53 +00:00
|
|
|
#include <optional>
|
2020-10-02 17:41:21 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2021-12-21 14:33:35 +00:00
|
|
|
extern bool gSkVMAllowJIT;
|
|
|
|
|
2020-10-02 17:41:21 +00:00
|
|
|
void SkDebugf(const char format[], ...) {
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
vfprintf(stderr, format, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
2020-10-05 18:18:49 +00:00
|
|
|
namespace SkOpts {
|
|
|
|
decltype(hash_fn) hash_fn = skslc_standalone::hash_fn;
|
2020-12-17 21:02:08 +00:00
|
|
|
decltype(interpret_skvm) interpret_skvm = skslc_standalone::interpret_skvm;
|
2020-10-05 18:18:49 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 23:54:33 +00:00
|
|
|
enum class ResultCode {
|
|
|
|
kSuccess = 0,
|
|
|
|
kCompileError = 1,
|
|
|
|
kInputError = 2,
|
|
|
|
kOutputError = 3,
|
2020-11-20 21:28:50 +00:00
|
|
|
kConfigurationError = 4,
|
2020-11-19 23:54:33 +00:00
|
|
|
};
|
|
|
|
|
2020-12-17 21:02:08 +00:00
|
|
|
static std::unique_ptr<SkWStream> as_SkWStream(SkSL::OutputStream& s) {
|
|
|
|
struct Adapter : public SkWStream {
|
|
|
|
public:
|
|
|
|
Adapter(SkSL::OutputStream& out) : fOut(out), fBytesWritten(0) {}
|
|
|
|
|
|
|
|
bool write(const void* buffer, size_t size) override {
|
|
|
|
fOut.write(buffer, size);
|
|
|
|
fBytesWritten += size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
void flush() override {}
|
|
|
|
size_t bytesWritten() const override { return fBytesWritten; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
SkSL::OutputStream& fOut;
|
|
|
|
size_t fBytesWritten;
|
|
|
|
};
|
|
|
|
|
|
|
|
return std::make_unique<Adapter>(s);
|
|
|
|
}
|
|
|
|
|
2022-02-01 23:25:15 +00:00
|
|
|
static bool consume_suffix(std::string* str, const char suffix[]) {
|
|
|
|
if (!skstd::ends_with(*str, suffix)) {
|
2022-02-01 20:39:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
str->resize(str->length() - strlen(suffix));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-09-16 21:43:11 +00:00
|
|
|
// Given a string containing an SkSL program, searches for a #pragma settings comment, like so:
|
|
|
|
// /*#pragma settings Default Sharpen*/
|
|
|
|
// The passed-in Settings object will be updated accordingly. Any number of options can be provided.
|
2022-02-02 21:51:18 +00:00
|
|
|
static bool detect_shader_settings(const std::string& text,
|
2020-11-02 17:26:22 +00:00
|
|
|
SkSL::Program::Settings* settings,
|
2021-11-19 15:59:59 +00:00
|
|
|
const SkSL::ShaderCaps** caps,
|
2021-12-06 17:02:20 +00:00
|
|
|
std::unique_ptr<SkSL::SkVMDebugTrace>* debugTrace) {
|
2020-09-16 21:46:37 +00:00
|
|
|
using Factory = SkSL::ShaderCapsFactory;
|
|
|
|
|
2020-09-16 21:43:11 +00:00
|
|
|
// Find a matching comment and isolate the name portion.
|
|
|
|
static constexpr char kPragmaSettings[] = "/*#pragma settings ";
|
|
|
|
const char* settingsPtr = strstr(text.c_str(), kPragmaSettings);
|
|
|
|
if (settingsPtr != nullptr) {
|
|
|
|
// Subtract one here in order to preserve the leading space, which is necessary to allow
|
|
|
|
// consumeSuffix to find the first item.
|
|
|
|
settingsPtr += strlen(kPragmaSettings) - 1;
|
|
|
|
|
|
|
|
const char* settingsEnd = strstr(settingsPtr, "*/");
|
|
|
|
if (settingsEnd != nullptr) {
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string settingsText{settingsPtr, size_t(settingsEnd - settingsPtr)};
|
2020-09-16 21:43:11 +00:00
|
|
|
|
|
|
|
// Apply settings as requested. Since they can come in any order, repeat until we've
|
|
|
|
// consumed them all.
|
|
|
|
for (;;) {
|
|
|
|
const size_t startingLength = settingsText.length();
|
|
|
|
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " AddAndTrueToLoopCondition")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_addAndTrueCaps = Factory::AddAndTrueToLoopCondition();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_addAndTrueCaps.get();
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " CannotUseFractForNegativeValues")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_negativeFractCaps = Factory::CannotUseFractForNegativeValues();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_negativeFractCaps.get();
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " CannotUseFragCoord")) {
|
2020-09-18 18:42:58 +00:00
|
|
|
static auto s_noFragCoordCaps = Factory::CannotUseFragCoord();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_noFragCoordCaps.get();
|
2020-09-18 18:42:58 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " CannotUseMinAndAbsTogether")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_minAbsCaps = Factory::CannotUseMinAndAbsTogether();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_minAbsCaps.get();
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " Default")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_defaultCaps = Factory::Default();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_defaultCaps.get();
|
2020-09-16 21:43:11 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " EmulateAbsIntFunction")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_emulateAbsIntCaps = Factory::EmulateAbsIntFunction();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_emulateAbsIntCaps.get();
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " FramebufferFetchSupport")) {
|
2021-09-15 14:28:27 +00:00
|
|
|
static auto s_fbFetchSupport = Factory::FramebufferFetchSupport();
|
|
|
|
*caps = s_fbFetchSupport.get();
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " IncompleteShortIntPrecision")) {
|
2020-09-18 18:42:58 +00:00
|
|
|
static auto s_incompleteShortIntCaps = Factory::IncompleteShortIntPrecision();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_incompleteShortIntCaps.get();
|
2020-09-18 18:42:58 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " MustGuardDivisionEvenAfterExplicitZeroCheck")) {
|
2020-10-09 02:18:10 +00:00
|
|
|
static auto s_div0Caps = Factory::MustGuardDivisionEvenAfterExplicitZeroCheck();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_div0Caps.get();
|
2020-10-09 02:18:10 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " MustForceNegatedAtanParamToFloat")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_negativeAtanCaps = Factory::MustForceNegatedAtanParamToFloat();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_negativeAtanCaps.get();
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " MustForceNegatedLdexpParamToMultiply")) {
|
2021-06-08 00:51:42 +00:00
|
|
|
static auto s_negativeLdexpCaps =
|
|
|
|
Factory::MustForceNegatedLdexpParamToMultiply();
|
|
|
|
*caps = s_negativeLdexpCaps.get();
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " RemovePowWithConstantExponent")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_powCaps = Factory::RemovePowWithConstantExponent();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_powCaps.get();
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " RewriteDoWhileLoops")) {
|
2020-09-18 18:42:58 +00:00
|
|
|
static auto s_rewriteLoopCaps = Factory::RewriteDoWhileLoops();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_rewriteLoopCaps.get();
|
2020-09-18 18:42:58 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " RewriteSwitchStatements")) {
|
2021-09-24 15:33:54 +00:00
|
|
|
static auto s_rewriteSwitchCaps = Factory::RewriteSwitchStatements();
|
|
|
|
*caps = s_rewriteSwitchCaps.get();
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " RewriteMatrixVectorMultiply")) {
|
2021-03-23 21:12:03 +00:00
|
|
|
static auto s_rewriteMatVecMulCaps = Factory::RewriteMatrixVectorMultiply();
|
|
|
|
*caps = s_rewriteMatVecMulCaps.get();
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " RewriteMatrixComparisons")) {
|
2021-05-21 21:27:57 +00:00
|
|
|
static auto s_rewriteMatrixComparisons = Factory::RewriteMatrixComparisons();
|
|
|
|
*caps = s_rewriteMatrixComparisons.get();
|
2021-05-20 22:00:39 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " ShaderDerivativeExtensionString")) {
|
2020-09-18 16:03:42 +00:00
|
|
|
static auto s_derivativeCaps = Factory::ShaderDerivativeExtensionString();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_derivativeCaps.get();
|
2020-09-18 16:03:42 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " UnfoldShortCircuitAsTernary")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_ternaryCaps = Factory::UnfoldShortCircuitAsTernary();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_ternaryCaps.get();
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " UsesPrecisionModifiers")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_precisionCaps = Factory::UsesPrecisionModifiers();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_precisionCaps.get();
|
2020-09-16 21:43:11 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " Version110")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_version110Caps = Factory::Version110();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_version110Caps.get();
|
2020-09-16 21:43:11 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " Version450Core")) {
|
2020-09-16 21:46:37 +00:00
|
|
|
static auto s_version450CoreCaps = Factory::Version450Core();
|
2020-11-02 17:26:22 +00:00
|
|
|
*caps = s_version450CoreCaps.get();
|
2020-09-16 21:43:11 +00:00
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " AllowNarrowingConversions")) {
|
2021-08-04 17:27:05 +00:00
|
|
|
settings->fAllowNarrowingConversions = true;
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " ForceHighPrecision")) {
|
2020-09-16 21:43:11 +00:00
|
|
|
settings->fForceHighPrecision = true;
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " NoInline")) {
|
2020-10-12 16:33:27 +00:00
|
|
|
settings->fInlineThreshold = 0;
|
|
|
|
}
|
2022-05-10 22:49:12 +00:00
|
|
|
if (consume_suffix(&settingsText, " NoOptimize")) {
|
|
|
|
settings->fOptimize = false;
|
|
|
|
settings->fInlineThreshold = 0;
|
|
|
|
}
|
2022-04-14 20:27:25 +00:00
|
|
|
if (consume_suffix(&settingsText, " NoRTFlip")) {
|
|
|
|
settings->fForceNoRTFlip = true;
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " NoTraceVarInSkVMDebugTrace")) {
|
2021-12-01 16:40:49 +00:00
|
|
|
settings->fAllowTraceVarInSkVMDebugTrace = false;
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " InlineThresholdMax")) {
|
2020-11-13 21:13:18 +00:00
|
|
|
settings->fInlineThreshold = INT_MAX;
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " Sharpen")) {
|
2020-09-16 21:43:11 +00:00
|
|
|
settings->fSharpenTextures = true;
|
|
|
|
}
|
2022-02-01 20:39:43 +00:00
|
|
|
if (consume_suffix(&settingsText, " SkVMDebugTrace")) {
|
2021-11-12 13:50:55 +00:00
|
|
|
settings->fOptimize = false;
|
2021-12-06 17:02:20 +00:00
|
|
|
*debugTrace = std::make_unique<SkSL::SkVMDebugTrace>();
|
2021-11-04 21:38:35 +00:00
|
|
|
}
|
2020-09-16 21:43:11 +00:00
|
|
|
|
|
|
|
if (settingsText.empty()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (settingsText.length() == startingLength) {
|
|
|
|
printf("Unrecognized #pragma settings: %s\n", settingsText.c_str());
|
2020-11-11 22:29:28 +00:00
|
|
|
return false;
|
2020-09-16 21:43:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-11 22:29:28 +00:00
|
|
|
|
|
|
|
return true;
|
2020-09-16 21:43:11 +00:00
|
|
|
}
|
|
|
|
|
2016-07-01 15:22:01 +00:00
|
|
|
/**
|
2020-11-11 22:29:28 +00:00
|
|
|
* Displays a usage banner; used when the command line arguments don't make sense.
|
2016-07-01 15:22:01 +00:00
|
|
|
*/
|
2020-11-11 22:29:28 +00:00
|
|
|
static void show_usage() {
|
2020-11-18 17:04:33 +00:00
|
|
|
printf("usage: skslc <input> <output> <flags>\n"
|
|
|
|
" skslc <worklist>\n"
|
2020-11-11 22:29:28 +00:00
|
|
|
"\n"
|
|
|
|
"Allowed flags:\n"
|
|
|
|
"--settings: honor embedded /*#pragma settings*/ comments.\n"
|
|
|
|
"--nosettings: ignore /*#pragma settings*/ comments\n");
|
|
|
|
}
|
|
|
|
|
2022-02-03 22:08:53 +00:00
|
|
|
static bool set_flag(std::optional<bool>* flag, const char* name, bool value) {
|
2022-01-18 18:47:01 +00:00
|
|
|
if (flag->has_value()) {
|
|
|
|
printf("%s flag was specified multiple times\n", name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*flag = value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-11 22:29:28 +00:00
|
|
|
/**
|
|
|
|
* Handle a single input.
|
|
|
|
*/
|
2022-02-01 23:25:15 +00:00
|
|
|
ResultCode processCommand(const std::vector<std::string>& args) {
|
2022-02-03 22:08:53 +00:00
|
|
|
std::optional<bool> honorSettings;
|
2022-02-01 23:25:15 +00:00
|
|
|
std::vector<std::string> paths;
|
2022-01-18 18:47:01 +00:00
|
|
|
for (size_t i = 1; i < args.size(); ++i) {
|
2022-02-01 23:25:15 +00:00
|
|
|
const std::string& arg = args[i];
|
2022-01-18 18:47:01 +00:00
|
|
|
if (arg == "--settings") {
|
|
|
|
if (!set_flag(&honorSettings, "settings", true)) {
|
|
|
|
return ResultCode::kInputError;
|
|
|
|
}
|
|
|
|
} else if (arg == "--nosettings") {
|
|
|
|
if (!set_flag(&honorSettings, "settings", false)) {
|
|
|
|
return ResultCode::kInputError;
|
|
|
|
}
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (!skstd::starts_with(arg, "--")) {
|
2022-01-18 18:47:01 +00:00
|
|
|
paths.push_back(arg);
|
2020-09-16 21:46:37 +00:00
|
|
|
} else {
|
2020-11-11 22:29:28 +00:00
|
|
|
show_usage();
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kInputError;
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2022-01-18 18:47:01 +00:00
|
|
|
}
|
|
|
|
if (paths.size() != 2) {
|
2020-11-11 22:29:28 +00:00
|
|
|
show_usage();
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kInputError;
|
2016-07-01 15:22:01 +00:00
|
|
|
}
|
2020-09-16 21:46:37 +00:00
|
|
|
|
2022-01-18 18:47:01 +00:00
|
|
|
if (!honorSettings.has_value()) {
|
|
|
|
honorSettings = true;
|
|
|
|
}
|
|
|
|
|
2022-02-01 23:25:15 +00:00
|
|
|
const std::string& inputPath = paths[0];
|
|
|
|
const std::string& outputPath = paths[1];
|
2021-02-16 18:29:15 +00:00
|
|
|
SkSL::ProgramKind kind;
|
2022-02-01 23:25:15 +00:00
|
|
|
if (skstd::ends_with(inputPath, ".vert")) {
|
2021-02-16 18:29:15 +00:00
|
|
|
kind = SkSL::ProgramKind::kVertex;
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(inputPath, ".frag") || skstd::ends_with(inputPath, ".sksl")) {
|
2021-02-16 18:29:15 +00:00
|
|
|
kind = SkSL::ProgramKind::kFragment;
|
2022-06-15 18:10:13 +00:00
|
|
|
} else if (skstd::ends_with(inputPath, ".compute")) {
|
|
|
|
kind = SkSL::ProgramKind::kCompute;
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(inputPath, ".rtb")) {
|
2021-06-16 15:33:13 +00:00
|
|
|
kind = SkSL::ProgramKind::kRuntimeBlender;
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(inputPath, ".rtcf")) {
|
2021-04-12 13:49:20 +00:00
|
|
|
kind = SkSL::ProgramKind::kRuntimeColorFilter;
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(inputPath, ".rts")) {
|
2021-04-12 13:49:20 +00:00
|
|
|
kind = SkSL::ProgramKind::kRuntimeShader;
|
2016-07-01 15:22:01 +00:00
|
|
|
} else {
|
2021-08-27 15:21:12 +00:00
|
|
|
printf("input filename must end in '.vert', '.frag', '.rtb', '.rtcf', "
|
2021-12-21 14:33:35 +00:00
|
|
|
"'.rts' or '.sksl'\n");
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kInputError;
|
2016-07-01 15:22:01 +00:00
|
|
|
}
|
|
|
|
|
2020-11-11 22:29:28 +00:00
|
|
|
std::ifstream in(inputPath);
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string text((std::istreambuf_iterator<char>(in)),
|
2020-09-16 21:43:11 +00:00
|
|
|
std::istreambuf_iterator<char>());
|
2016-07-01 15:22:01 +00:00
|
|
|
if (in.rdstate()) {
|
2020-11-11 22:29:28 +00:00
|
|
|
printf("error reading '%s'\n", inputPath.c_str());
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kInputError;
|
2016-07-01 15:22:01 +00:00
|
|
|
}
|
2020-09-15 19:37:24 +00:00
|
|
|
|
2016-12-12 20:33:30 +00:00
|
|
|
SkSL::Program::Settings settings;
|
2021-11-19 15:59:59 +00:00
|
|
|
auto standaloneCaps = SkSL::ShaderCapsFactory::Standalone();
|
|
|
|
const SkSL::ShaderCaps* caps = standaloneCaps.get();
|
2021-12-06 17:02:20 +00:00
|
|
|
std::unique_ptr<SkSL::SkVMDebugTrace> debugTrace;
|
2022-01-18 18:47:01 +00:00
|
|
|
if (*honorSettings) {
|
2021-12-06 17:02:20 +00:00
|
|
|
if (!detect_shader_settings(text, &settings, &caps, &debugTrace)) {
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kInputError;
|
2020-11-11 22:29:28 +00:00
|
|
|
}
|
2020-09-16 21:46:37 +00:00
|
|
|
}
|
2020-11-11 22:29:28 +00:00
|
|
|
|
2021-07-07 13:41:17 +00:00
|
|
|
// This tells the compiler where the rt-flip uniform will live should it be required. For
|
|
|
|
// testing purposes we don't care where that is, but the compiler will report an error if we
|
2021-08-12 20:09:21 +00:00
|
|
|
// leave them at their default invalid values, or if the offset overlaps another uniform.
|
|
|
|
settings.fRTFlipOffset = 16384;
|
2021-07-07 13:41:17 +00:00
|
|
|
settings.fRTFlipSet = 0;
|
|
|
|
settings.fRTFlipBinding = 0;
|
|
|
|
|
2020-11-11 22:29:28 +00:00
|
|
|
auto emitCompileError = [&](SkSL::FileOutputStream& out, const char* errorText) {
|
2020-11-19 23:54:33 +00:00
|
|
|
// Overwrite the compiler output, if any, with an error message.
|
|
|
|
out.close();
|
2022-02-01 23:25:15 +00:00
|
|
|
SkSL::FileOutputStream errorStream(outputPath.c_str());
|
2020-11-19 23:54:33 +00:00
|
|
|
errorStream.writeText("### Compilation failed:\n\n");
|
|
|
|
errorStream.writeText(errorText);
|
|
|
|
errorStream.close();
|
|
|
|
// Also emit the error directly to stdout.
|
|
|
|
puts(errorText);
|
2020-11-11 22:29:28 +00:00
|
|
|
};
|
|
|
|
|
2021-02-11 20:46:11 +00:00
|
|
|
auto compileProgram = [&](const auto& writeFn) -> ResultCode {
|
2022-02-01 23:25:15 +00:00
|
|
|
SkSL::FileOutputStream out(outputPath.c_str());
|
2021-02-11 20:46:11 +00:00
|
|
|
SkSL::Compiler compiler(caps);
|
2016-11-21 15:39:35 +00:00
|
|
|
if (!out.isValid()) {
|
2020-11-11 22:29:28 +00:00
|
|
|
printf("error writing '%s'\n", outputPath.c_str());
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kOutputError;
|
2016-11-21 15:39:35 +00:00
|
|
|
}
|
2016-12-12 20:33:30 +00:00
|
|
|
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, text, settings);
|
2020-11-20 19:45:00 +00:00
|
|
|
if (!program || !writeFn(compiler, *program, out)) {
|
2020-11-11 22:29:28 +00:00
|
|
|
emitCompileError(out, compiler.errorText().c_str());
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kCompileError;
|
2016-10-12 13:39:56 +00:00
|
|
|
}
|
2017-03-31 17:56:23 +00:00
|
|
|
if (!out.close()) {
|
2020-11-11 22:29:28 +00:00
|
|
|
printf("error writing '%s'\n", outputPath.c_str());
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kOutputError;
|
2017-03-31 17:56:23 +00:00
|
|
|
}
|
2020-11-20 19:45:00 +00:00
|
|
|
return ResultCode::kSuccess;
|
|
|
|
};
|
|
|
|
|
2021-12-01 21:31:04 +00:00
|
|
|
auto compileProgramForSkVM = [&](const auto& writeFn) -> ResultCode {
|
|
|
|
if (kind == SkSL::ProgramKind::kVertex) {
|
|
|
|
printf("%s: SkVM does not support vertex programs\n", outputPath.c_str());
|
|
|
|
return ResultCode::kOutputError;
|
|
|
|
}
|
|
|
|
if (kind == SkSL::ProgramKind::kFragment) {
|
|
|
|
// Handle .sksl and .frag programs as runtime shaders.
|
|
|
|
kind = SkSL::ProgramKind::kRuntimeShader;
|
|
|
|
}
|
|
|
|
return compileProgram(writeFn);
|
|
|
|
};
|
|
|
|
|
2022-02-01 23:25:15 +00:00
|
|
|
if (skstd::ends_with(outputPath, ".spirv")) {
|
2020-11-20 19:45:00 +00:00
|
|
|
return compileProgram(
|
|
|
|
[](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
|
|
|
|
return compiler.toSPIRV(program, out);
|
|
|
|
});
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(outputPath, ".asm.frag") ||
|
|
|
|
skstd::ends_with(outputPath, ".asm.vert")) {
|
2020-11-20 21:28:50 +00:00
|
|
|
return compileProgram(
|
|
|
|
[](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
|
|
|
|
// Compile program to SPIR-V assembly in a string-stream.
|
|
|
|
SkSL::StringStream assembly;
|
|
|
|
if (!compiler.toSPIRV(program, assembly)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Convert the string-stream to a SPIR-V disassembly.
|
|
|
|
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
|
2022-02-02 21:51:18 +00:00
|
|
|
const std::string& spirv(assembly.str());
|
2020-11-20 21:28:50 +00:00
|
|
|
std::string disassembly;
|
|
|
|
if (!tools.Disassemble((const uint32_t*)spirv.data(),
|
|
|
|
spirv.size() / 4, &disassembly)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Finally, write the disassembly to our output stream.
|
|
|
|
out.write(disassembly.data(), disassembly.size());
|
|
|
|
return true;
|
|
|
|
});
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(outputPath, ".glsl")) {
|
2020-11-20 19:45:00 +00:00
|
|
|
return compileProgram(
|
|
|
|
[](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
|
|
|
|
return compiler.toGLSL(program, out);
|
|
|
|
});
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(outputPath, ".metal")) {
|
2020-11-20 19:45:00 +00:00
|
|
|
return compileProgram(
|
|
|
|
[](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
|
|
|
|
return compiler.toMetal(program, out);
|
|
|
|
});
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(outputPath, ".hlsl")) {
|
2021-12-09 20:24:51 +00:00
|
|
|
return compileProgram(
|
|
|
|
[](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
|
|
|
|
return compiler.toHLSL(program, out);
|
|
|
|
});
|
2022-04-02 03:04:51 +00:00
|
|
|
} else if (skstd::ends_with(outputPath, ".wgsl")) {
|
|
|
|
return compileProgram(
|
|
|
|
[](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
|
|
|
|
return compiler.toWGSL(program, out);
|
|
|
|
});
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(outputPath, ".skvm")) {
|
2021-12-01 21:31:04 +00:00
|
|
|
return compileProgramForSkVM(
|
2021-11-12 13:50:55 +00:00
|
|
|
[&](SkSL::Compiler&, SkSL::Program& program, SkSL::OutputStream& out) {
|
2020-12-22 17:46:57 +00:00
|
|
|
skvm::Builder builder{skvm::Features{}};
|
2021-11-12 13:50:55 +00:00
|
|
|
if (!SkSL::testingOnly_ProgramToSkVMShader(program, &builder,
|
2021-12-06 17:02:20 +00:00
|
|
|
debugTrace.get())) {
|
2020-12-02 14:27:10 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-17 21:02:08 +00:00
|
|
|
std::unique_ptr<SkWStream> redirect = as_SkWStream(out);
|
2021-12-06 17:02:20 +00:00
|
|
|
if (debugTrace) {
|
|
|
|
debugTrace->dump(redirect.get());
|
2021-11-15 13:46:50 +00:00
|
|
|
}
|
2020-12-17 21:02:08 +00:00
|
|
|
builder.done().dump(redirect.get());
|
2020-12-02 14:27:10 +00:00
|
|
|
return true;
|
|
|
|
});
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(outputPath, ".stage")) {
|
2021-02-11 20:46:11 +00:00
|
|
|
return compileProgram(
|
2021-02-08 18:49:53 +00:00
|
|
|
[](SkSL::Compiler&, SkSL::Program& program, SkSL::OutputStream& out) {
|
|
|
|
class Callbacks : public SkSL::PipelineStage::Callbacks {
|
|
|
|
public:
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string getMangledName(const char* name) override {
|
|
|
|
return std::string(name) + "_0";
|
2021-02-17 20:41:57 +00:00
|
|
|
}
|
|
|
|
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string declareUniform(const SkSL::VarDeclaration* decl) override {
|
2021-02-08 18:49:53 +00:00
|
|
|
fOutput += decl->description();
|
2022-02-02 21:51:18 +00:00
|
|
|
return std::string(decl->var().name());
|
2021-02-08 18:49:53 +00:00
|
|
|
}
|
|
|
|
|
2021-02-09 21:52:53 +00:00
|
|
|
void defineFunction(const char* decl,
|
|
|
|
const char* body,
|
|
|
|
bool /*isMain*/) override {
|
2022-02-02 21:51:18 +00:00
|
|
|
fOutput += std::string(decl) + "{" + body + "}";
|
2021-02-08 18:49:53 +00:00
|
|
|
}
|
|
|
|
|
2021-09-30 19:20:44 +00:00
|
|
|
void declareFunction(const char* decl) override {
|
2022-02-02 21:51:18 +00:00
|
|
|
fOutput += std::string(decl) + ";";
|
2021-09-30 19:20:44 +00:00
|
|
|
}
|
|
|
|
|
2021-02-10 15:19:27 +00:00
|
|
|
void defineStruct(const char* definition) override {
|
|
|
|
fOutput += definition;
|
|
|
|
}
|
|
|
|
|
2021-02-17 20:39:06 +00:00
|
|
|
void declareGlobal(const char* declaration) override {
|
|
|
|
fOutput += declaration;
|
|
|
|
}
|
|
|
|
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string sampleShader(int index, std::string coords) override {
|
2022-02-03 21:53:32 +00:00
|
|
|
return "child_" + std::to_string(index) + ".eval(" + coords + ")";
|
2021-07-23 19:50:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string sampleColorFilter(int index, std::string color) override {
|
2022-02-03 21:53:32 +00:00
|
|
|
return "child_" + std::to_string(index) + ".eval(" + color + ")";
|
2021-02-08 18:49:53 +00:00
|
|
|
}
|
|
|
|
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string sampleBlender(int index,
|
|
|
|
std::string src,
|
|
|
|
std::string dst) override {
|
2022-02-03 21:53:32 +00:00
|
|
|
return "child_" + std::to_string(index) + ".eval(" + src + ", " +
|
2021-07-23 19:51:05 +00:00
|
|
|
dst + ")";
|
|
|
|
}
|
|
|
|
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string toLinearSrgb(std::string color) override {
|
2021-12-07 21:05:39 +00:00
|
|
|
return "toLinearSrgb(" + color + ")";
|
|
|
|
}
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string fromLinearSrgb(std::string color) override {
|
2021-12-07 21:05:39 +00:00
|
|
|
return "fromLinearSrgb(" + color + ")";
|
|
|
|
}
|
|
|
|
|
2022-02-02 21:51:18 +00:00
|
|
|
std::string fOutput;
|
2021-02-08 18:49:53 +00:00
|
|
|
};
|
2021-06-09 20:57:45 +00:00
|
|
|
// The .stage output looks almost like valid SkSL, but not quite.
|
|
|
|
// The PipelineStageGenerator bridges the gap between the SkSL in `program`,
|
|
|
|
// and the C++ FP builder API (see GrSkSLFP). In that API, children don't need
|
|
|
|
// to be declared (so they don't emit declarations here). Children are sampled
|
|
|
|
// by index, not name - so all children here are just "child_N".
|
|
|
|
// The input color and coords have names in the original SkSL (as parameters to
|
|
|
|
// main), but those are ignored here. References to those variables become
|
|
|
|
// "_coords" and "_inColor". At runtime, those variable names are irrelevant
|
|
|
|
// when the new SkSL is emitted inside the FP - references to those variables
|
|
|
|
// are replaced with strings from EmitArgs, and might be varyings or differently
|
|
|
|
// named parameters.
|
2021-02-08 18:49:53 +00:00
|
|
|
Callbacks callbacks;
|
2021-06-09 21:24:31 +00:00
|
|
|
SkSL::PipelineStage::ConvertProgram(program, "_coords", "_inColor",
|
|
|
|
"_canvasColor", &callbacks);
|
2021-12-10 22:27:10 +00:00
|
|
|
out.writeString(SkShaderUtils::PrettyPrint(callbacks.fOutput));
|
2021-02-08 18:49:53 +00:00
|
|
|
return true;
|
|
|
|
});
|
2022-02-01 23:25:15 +00:00
|
|
|
} else if (skstd::ends_with(outputPath, ".html")) {
|
2021-12-21 14:33:35 +00:00
|
|
|
settings.fAllowTraceVarInSkVMDebugTrace = false;
|
|
|
|
|
|
|
|
SkCpu::CacheRuntimeFeatures();
|
|
|
|
gSkVMAllowJIT = true;
|
|
|
|
return compileProgramForSkVM(
|
|
|
|
[&](SkSL::Compiler&, SkSL::Program& program, SkSL::OutputStream& out) {
|
|
|
|
if (!debugTrace) {
|
|
|
|
debugTrace = std::make_unique<SkSL::SkVMDebugTrace>();
|
|
|
|
debugTrace->setSource(text.c_str());
|
|
|
|
}
|
|
|
|
auto visualizer = std::make_unique<skvm::viz::Visualizer>(debugTrace.get());
|
|
|
|
skvm::Builder builder(skvm::Features{}, /*createDuplicates=*/true);
|
|
|
|
if (!SkSL::testingOnly_ProgramToSkVMShader(program, &builder, debugTrace.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<SkWStream> redirect = as_SkWStream(out);
|
|
|
|
skvm::Program p = builder.done(
|
|
|
|
/*debug_name=*/nullptr, /*allow_jit=*/true, std::move(visualizer));
|
|
|
|
#if defined(SKVM_JIT)
|
|
|
|
SkDynamicMemoryWStream asmFile;
|
|
|
|
p.disassemble(&asmFile);
|
|
|
|
auto dumpData = asmFile.detachAsData();
|
|
|
|
std::string dumpString(static_cast<const char*>(dumpData->data()),dumpData->size());
|
|
|
|
p.visualize(redirect.get(), dumpString.c_str());
|
|
|
|
#else
|
|
|
|
p.visualize(redirect.get(), nullptr);
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
});
|
2016-10-12 13:39:56 +00:00
|
|
|
} else {
|
2022-04-02 03:04:51 +00:00
|
|
|
printf("expected output path to end with one of: .glsl, .html, .metal, .hlsl, .wgsl, "
|
|
|
|
".spirv, .asm.vert, .asm.frag, .skvm, .stage (got '%s')\n",
|
2021-12-21 14:33:35 +00:00
|
|
|
outputPath.c_str());
|
2020-11-20 21:28:50 +00:00
|
|
|
return ResultCode::kConfigurationError;
|
2020-11-11 22:29:28 +00:00
|
|
|
}
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kSuccess;
|
2020-11-11 22:29:28 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 17:04:33 +00:00
|
|
|
/**
|
|
|
|
* Processes multiple inputs in a single invocation of skslc.
|
|
|
|
*/
|
2020-11-19 23:54:33 +00:00
|
|
|
ResultCode processWorklist(const char* worklistPath) {
|
2022-02-01 23:25:15 +00:00
|
|
|
std::string inputPath(worklistPath);
|
|
|
|
if (!skstd::ends_with(inputPath, ".worklist")) {
|
2020-11-18 17:04:33 +00:00
|
|
|
printf("expected .worklist file, found: %s\n\n", worklistPath);
|
|
|
|
show_usage();
|
2020-11-20 21:28:50 +00:00
|
|
|
return ResultCode::kConfigurationError;
|
2020-11-18 17:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The worklist contains one line per argument to pass to skslc. When a blank line is reached,
|
|
|
|
// those arguments will be passed to `processCommand`.
|
2020-11-19 23:54:33 +00:00
|
|
|
auto resultCode = ResultCode::kSuccess;
|
2022-02-01 23:25:15 +00:00
|
|
|
std::vector<std::string> args = {"skslc"};
|
2020-11-18 17:04:33 +00:00
|
|
|
std::ifstream in(worklistPath);
|
2022-02-01 23:25:15 +00:00
|
|
|
for (std::string line; std::getline(in, line); ) {
|
2020-11-18 17:04:33 +00:00
|
|
|
if (in.rdstate()) {
|
|
|
|
printf("error reading '%s'\n", worklistPath);
|
2020-11-19 23:54:33 +00:00
|
|
|
return ResultCode::kInputError;
|
2020-11-18 17:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!line.empty()) {
|
2020-11-11 22:29:28 +00:00
|
|
|
// We found an argument. Remember it.
|
2020-11-18 17:04:33 +00:00
|
|
|
args.push_back(std::move(line));
|
2020-11-11 22:29:28 +00:00
|
|
|
} else {
|
2020-11-18 17:04:33 +00:00
|
|
|
// We found a blank line. If we have any arguments stored up, process them as a command.
|
|
|
|
if (!args.empty()) {
|
2020-11-19 23:54:33 +00:00
|
|
|
ResultCode outcome = processCommand(args);
|
|
|
|
resultCode = std::max(resultCode, outcome);
|
2020-11-11 22:29:28 +00:00
|
|
|
|
|
|
|
// Clear every argument except the first ("skslc").
|
|
|
|
args.resize(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-18 17:04:33 +00:00
|
|
|
// If the worklist ended with a list of arguments but no blank line, process those now.
|
2020-11-11 22:29:28 +00:00
|
|
|
if (args.size() > 1) {
|
2020-11-19 23:54:33 +00:00
|
|
|
ResultCode outcome = processCommand(args);
|
|
|
|
resultCode = std::max(resultCode, outcome);
|
2020-11-11 22:29:28 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 23:54:33 +00:00
|
|
|
// Return the "worst" status we encountered. For our purposes, compilation errors are the least
|
|
|
|
// serious, because they are expected to occur in unit tests. Other types of errors are not
|
|
|
|
// expected at all during a build.
|
|
|
|
return resultCode;
|
2020-11-18 17:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, const char** argv) {
|
|
|
|
if (argc == 2) {
|
|
|
|
// Worklists are the only two-argument case for skslc, and we don't intend to support
|
|
|
|
// nested worklists, so we can process them here.
|
2020-11-19 23:54:33 +00:00
|
|
|
return (int)processWorklist(argv[1]);
|
2020-11-18 17:04:33 +00:00
|
|
|
} else {
|
|
|
|
// Process non-worklist inputs.
|
2022-02-01 23:25:15 +00:00
|
|
|
std::vector<std::string> args;
|
2020-11-18 17:04:33 +00:00
|
|
|
for (int index=0; index<argc; ++index) {
|
|
|
|
args.push_back(argv[index]);
|
|
|
|
}
|
|
|
|
|
2020-11-19 23:54:33 +00:00
|
|
|
return (int)processCommand(args);
|
2020-11-18 17:04:33 +00:00
|
|
|
}
|
2016-07-01 15:22:01 +00:00
|
|
|
}
|