ethannicholas 2016-09-26 11:58:52 -07:00 committed by Commit bot
parent 1d08998e4f
commit b12b3c6908
39 changed files with 1224 additions and 462 deletions

View File

@ -18,7 +18,7 @@
],
'all_dependent_settings': {
'include_dirs': [
'../src/sksl',
'<(skia_src_path)/sksl',
],
},
},

View File

@ -7,6 +7,7 @@
#include "GrGLContext.h"
#include "GrGLGLSL.h"
#include "SkSLCompiler.h"
////////////////////////////////////////////////////////////////////////////////
@ -63,6 +64,17 @@ GrGLContext* GrGLContext::Create(const GrGLInterface* interface, const GrContext
return new GrGLContext(args);
}
GrGLContext::~GrGLContext() {
delete fCompiler;
}
SkSL::Compiler* GrGLContext::compiler() const {
if (!fCompiler) {
fCompiler = new SkSL::Compiler();
}
return fCompiler;
}
GrGLContextInfo::GrGLContextInfo(const ConstructorArgs& args) {
fInterface.reset(SkRef(args.fInterface));
fGLVersion = args.fGLVersion;

View File

@ -15,6 +15,9 @@
#include "GrGLUtil.h"
struct GrContextOptions;
namespace SkSL {
class Compiler;
}
/**
* Encapsulates information about an OpenGL context including the OpenGL
@ -39,6 +42,8 @@ public:
const GrGLExtensions& extensions() const { return fInterface->fExtensions; }
virtual ~GrGLContextInfo() {}
protected:
struct ConstructorArgs {
const GrGLInterface* fInterface;
@ -64,7 +69,7 @@ protected:
};
/**
* Extension of GrGLContextInfo that also provides access to GrGLInterface.
* Extension of GrGLContextInfo that also provides access to GrGLInterface and SkSL::Compiler.
*/
class GrGLContext : public GrGLContextInfo {
public:
@ -76,8 +81,16 @@ public:
const GrGLInterface* interface() const { return fInterface; }
SkSL::Compiler* compiler() const;
~GrGLContext() override;
private:
GrGLContext(const ConstructorArgs& args) : INHERITED(args) {}
GrGLContext(const ConstructorArgs& args)
: INHERITED(args)
, fCompiler(nullptr) {}
mutable SkSL::Compiler* fCompiler;
typedef GrGLContextInfo INHERITED;
};

View File

@ -3790,20 +3790,11 @@ bool GrGLGpu::createCopyProgram(int progIdx) {
fshaderTxt.append(";");
uTexture.appendDecl(glslCaps, &fshaderTxt);
fshaderTxt.append(";");
const char* fsOutName;
if (glslCaps->mustDeclareFragmentShaderOutput()) {
oFragColor.appendDecl(glslCaps, &fshaderTxt);
fshaderTxt.append(";");
fsOutName = oFragColor.c_str();
} else {
fsOutName = "gl_FragColor";
}
fshaderTxt.appendf(
"// Copy Program FS\n"
"void main() {"
" %s = %s(u_texture, v_texCoord);"
" sk_FragColor = %s(u_texture, v_texCoord);"
"}",
fsOutName,
GrGLSLTexture2DFunctionName(kVec2f_GrSLType, kSamplerTypes[progIdx], this->glslGeneration())
);
@ -3936,14 +3927,6 @@ bool GrGLGpu::createMipmapProgram(int progIdx) {
}
uTexture.appendDecl(glslCaps, &fshaderTxt);
fshaderTxt.append(";");
const char* fsOutName;
if (glslCaps->mustDeclareFragmentShaderOutput()) {
oFragColor.appendDecl(glslCaps, &fshaderTxt);
fshaderTxt.append(";");
fsOutName = oFragColor.c_str();
} else {
fsOutName = "gl_FragColor";
}
const char* sampleFunction = GrGLSLTexture2DFunctionName(kVec2f_GrSLType,
kTexture2DSampler_GrSLType,
this->glslGeneration());
@ -3954,19 +3937,19 @@ bool GrGLGpu::createMipmapProgram(int progIdx) {
if (oddWidth && oddHeight) {
fshaderTxt.appendf(
" %s = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1) + "
" %s(u_texture, v_texCoord2) + %s(u_texture, v_texCoord3)) * 0.25;",
fsOutName, sampleFunction, sampleFunction, sampleFunction, sampleFunction
" sk_FragColor = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1) + "
" %s(u_texture, v_texCoord2) + %s(u_texture, v_texCoord3)) * 0.25;",
sampleFunction, sampleFunction, sampleFunction, sampleFunction
);
} else if (oddWidth || oddHeight) {
fshaderTxt.appendf(
" %s = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1)) * 0.5;",
fsOutName, sampleFunction, sampleFunction
" sk_FragColor = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1)) * 0.5;",
sampleFunction, sampleFunction
);
} else {
fshaderTxt.appendf(
" %s = %s(u_texture, v_texCoord0);",
fsOutName, sampleFunction
" sk_FragColor = %s(u_texture, v_texCoord0);",
sampleFunction
);
}
@ -4053,20 +4036,11 @@ bool GrGLGpu::createWireRectProgram() {
&fshaderTxt);
uColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
fshaderTxt.append(";");
const char* fsOutName;
if (this->glCaps().glslCaps()->mustDeclareFragmentShaderOutput()) {
oFragColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
fshaderTxt.append(";");
fsOutName = oFragColor.c_str();
} else {
fsOutName = "gl_FragColor";
}
fshaderTxt.appendf(
"// Write Rect Program FS\n"
"void main() {"
" %s = %s;"
" sk_FragColor = %s;"
"}",
fsOutName,
uColor.c_str()
);

View File

@ -9,6 +9,8 @@
#include "gl/GrGLGpu.h"
#include "gl/GrGLSLPrettyPrint.h"
#include "SkTraceEvent.h"
#include "SkSLCompiler.h"
#include "ir/SkSLProgram.h"
#define GL_CALL(X) GR_GL_CALL(gpu->glInterface(), X)
#define GL_CALL_RET(R, X) GR_GL_CALL_RET(gpu->glInterface(), R, X)
@ -18,6 +20,90 @@ static const bool c_PrintShaders{false};
static void print_shader_source(const char** strings, int* lengths, int count);
static SkSL::GLCaps skslcaps_for_context(const GrGLContext& context) {
GrGLStandard standard = context.standard();
const GrGLCaps* caps = context.caps();
const GrGLSLCaps* glslCaps = caps->glslCaps();
SkSL::GLCaps result;
switch (standard) {
case kGL_GrGLStandard:
result.fStandard = SkSL::GLCaps::kGL_Standard;
break;
case kGLES_GrGLStandard:
result.fStandard = SkSL::GLCaps::kGLES_Standard;
break;
default:
SkASSERT(false);
result.fStandard = SkSL::GLCaps::kGL_Standard;
}
switch (glslCaps->generation()) {
case k110_GrGLSLGeneration:
if (kGLES_GrGLStandard == standard) {
// ES2's shader language is based on GLSL 1.20 but is version 1.00 of the ES
// language
result.fVersion = 100;
} else {
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 110;
}
break;
case k130_GrGLSLGeneration:
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 130;
break;
case k140_GrGLSLGeneration:
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 140;
break;
case k150_GrGLSLGeneration:
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 150;
break;
case k330_GrGLSLGeneration:
if (kGLES_GrGLStandard == standard) {
result.fVersion = 300;
} else {
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 330;
}
break;
case k400_GrGLSLGeneration:
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 400;
break;
case k310es_GrGLSLGeneration:
SkASSERT(kGLES_GrGLStandard == standard);
result.fVersion = 310;
break;
case k320es_GrGLSLGeneration:
SkASSERT(kGLES_GrGLStandard == standard);
result.fVersion = 320;
break;
}
result.fIsCoreProfile = caps->isCoreProfile();
result.fUsesPrecisionModifiers = glslCaps->usesPrecisionModifiers();
result.fMustDeclareFragmentShaderOutput = glslCaps->mustDeclareFragmentShaderOutput();
result.fCanUseMinAndAbsTogether = glslCaps->canUseMinAndAbsTogether();
return result;
}
static void dump_string(std::string s) {
// on Android, SkDebugf only displays the first 1K characters of output, which results in
// incomplete shader source code. Print each line individually to avoid this problem.
size_t index = 0;
for (;;) {
size_t next = s.find("\n", index);
if (next == std::string::npos) {
SkDebugf("%s", s.substr(index).c_str());
break;
} else {
SkDebugf("%s", s.substr(index, next - index + 1).c_str());
index = next + 1;
}
}
}
GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
GrGLuint programId,
GrGLenum type,
@ -33,15 +119,39 @@ GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
return 0;
}
std::string sksl;
#ifdef SK_DEBUG
SkString prettySource = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
const GrGLchar* sourceStr = prettySource.c_str();
GrGLint sourceLength = static_cast<GrGLint>(prettySource.size());
GR_GL_CALL(gli, ShaderSource(shaderId, 1, &sourceStr, &sourceLength));
sksl = std::string(prettySource.c_str());
#else
GR_GL_CALL(gli, ShaderSource(shaderId, count, strings, lengths));
for (int i = 0; i < count; i++) {
sksl.append(strings[i], lengths[i]);
}
#endif
std::string glsl;
SkSL::Compiler& compiler = *glCtx.compiler();
SkSL::GLCaps caps = skslcaps_for_context(glCtx);
SkASSERT(type == GR_GL_VERTEX_SHADER || type == GR_GL_FRAGMENT_SHADER);
SkDEBUGCODE(bool result = )compiler.toGLSL(type == GR_GL_VERTEX_SHADER
? SkSL::Program::kVertex_Kind
: SkSL::Program::kFragment_Kind,
std::string(sksl.c_str()),
caps,
&glsl);
#ifdef SK_DEBUG
if (!result) {
SkDebugf("SKSL compilation error\n----------------------\n");
SkDebugf("SKSL:\n");
dump_string(sksl);
SkDebugf("\nErrors:\n%s\n", compiler.errorText().c_str());
SkDEBUGFAIL("SKSL compilation failed!\n");
}
#endif
const char* glslChars = glsl.c_str();
GrGLint glslLength = (GrGLint) glsl.length();
GR_GL_CALL(gli, ShaderSource(shaderId, 1, &glslChars, &glslLength));
// If tracing is enabled in chrome then we pretty print
bool traceShader;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), &traceShader);
@ -72,10 +182,14 @@ GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
// buffer param validation.
GrGLsizei length = GR_GL_INIT_ZERO;
GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1, &length, (char*)log.get()));
print_shader_source(strings, lengths, count);
SkDebugf("\n%s", (const char*)log.get());
SkDebugf("GLSL compilation error\n----------------------\n");
SkDebugf("SKSL:\n");
dump_string(sksl);
SkDebugf("GLSL:\n");
dump_string(glsl);
SkDebugf("Errors:\n%s\n", (const char*) log.get());
}
SkDEBUGFAIL("Shader compilation failed!");
SkDEBUGFAIL("GLSL compilation failed!");
GR_GL_CALL(gli, DeleteShader(shaderId));
return 0;
}

View File

@ -291,10 +291,10 @@ void GrGLSLFragmentShaderBuilder::enableCustomOutput() {
if (!fHasCustomColorOutput) {
fHasCustomColorOutput = true;
fCustomColorOutputIndex = fOutputs.count();
fOutputs.push_back().set(kVec4f_GrSLType,
GrGLSLShaderVar::kOut_TypeModifier,
DeclaredColorOutputName());
fProgramBuilder->finalizeFragmentOutputColor(fOutputs.back());
fOutputs.push_back().set(kVec4f_GrSLType,
GrGLSLShaderVar::kOut_TypeModifier,
DeclaredColorOutputName());
fProgramBuilder->finalizeFragmentOutputColor(fOutputs.back());
}
}
@ -318,7 +318,7 @@ void GrGLSLFragmentShaderBuilder::enableSecondaryOutput() {
}
const char* GrGLSLFragmentShaderBuilder::getPrimaryColorOutputName() const {
return fHasCustomColorOutput ? DeclaredColorOutputName() : "gl_FragColor";
return fHasCustomColorOutput ? DeclaredColorOutputName() : "sk_FragColor";
}
void GrGLSLFragmentBuilder::declAppendf(const char* fmt, ...) {

View File

@ -203,7 +203,7 @@ private:
}
#endif
static const char* DeclaredColorOutputName() { return "fsColorOut"; }
static const char* DeclaredColorOutputName() { return "sk_FragColor"; }
static const char* DeclaredSecondaryColorOutputName() { return "fsSecondaryColorOut"; }
GrSurfaceOrigin getSurfaceOrigin() const;

View File

@ -72,23 +72,27 @@ public:
void appendTexelFetch(SamplerHandle, const char* coordExpr);
/**
* Adds a #define directive to the top of the shader.
* Adds a constant declaration to the top of the shader.
*/
void define(const char* macro, const char* replacement) {
this->definitions().appendf("#define %s %s\n", macro, replacement);
void defineConstant(const char* type, const char* name, const char* value) {
this->definitions().appendf("const %s %s = %s;\n", type, name, value);
}
void define(const char* macro, int replacement) {
this->definitions().appendf("#define %s %i\n", macro, replacement);
void defineConstant(const char* name, int value) {
this->definitions().appendf("const int %s = %i;\n", name, value);
}
void definef(const char* macro, const char* replacement, ...) {
this->definitions().appendf("#define %s ", macro);
void defineConstant(const char* name, float value) {
this->definitions().appendf("const float %s = %f;\n", name, value);
}
void defineConstantf(const char* type, const char* name, const char* fmt, ...) {
this->definitions().appendf("const %s %s = ", type, name);
va_list args;
va_start(args, replacement);
this->definitions().appendVAList(replacement, args);
va_start(args, fmt);
this->definitions().appendVAList(fmt, args);
va_end(args);
this->definitions().append("\n");
this->definitions().append(";\n");
}
/**

View File

@ -213,24 +213,20 @@ public:
private:
static const char* TypeModifierString(const GrGLSLCaps* glslCaps, TypeModifier t) {
GrGLSLGeneration gen = glslCaps->generation();
switch (t) {
case kNone_TypeModifier:
return "";
case kAttribute_TypeModifier: // fall through
case kVaryingIn_TypeModifier: // fall through
case kIn_TypeModifier:
return "in";
case kInOut_TypeModifier:
return "inout";
case kVaryingOut_TypeModifier: // fall through
case kOut_TypeModifier:
return "out";
case kUniform_TypeModifier:
return "uniform";
case kAttribute_TypeModifier:
return k110_GrGLSLGeneration == gen ? "attribute" : "in";
case kVaryingIn_TypeModifier:
return k110_GrGLSLGeneration == gen ? "varying" : "in";
case kVaryingOut_TypeModifier:
return k110_GrGLSLGeneration == gen ? "varying" : "out";
default:
SkFAIL("Unknown shader variable type modifier.");
return ""; // suppress warning

View File

@ -107,10 +107,10 @@ public:
void initParams(const SamplerHandle paramsBuffer) {
fParamsBuffer = paramsBuffer;
fVertexBuilder->definef("PARAMS_IDX_MASK", "0x%xu", kParamsIdx_InfoMask);
fVertexBuilder->appendPrecisionModifier(kHigh_GrSLPrecision);
fVertexBuilder->codeAppendf("int paramsIdx = int(%s & PARAMS_IDX_MASK);",
this->attr(Attrib::kInstanceInfo));
fVertexBuilder->codeAppendf("int paramsIdx = int(%s & 0x%x);",
this->attr(Attrib::kInstanceInfo),
kParamsIdx_InfoMask);
}
const char* attr(Attrib attr) const { return fInstProc.getAttrib((int)attr).fName; }
@ -224,10 +224,10 @@ void GLSLInstanceProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
v->codeAppendf("mat2x3 shapeMatrix = mat2x3(%s, %s);",
inputs.attr(Attrib::kShapeMatrixX), inputs.attr(Attrib::kShapeMatrixY));
} else {
v->definef("PERSPECTIVE_FLAG", "0x%xu", kPerspective_InfoFlag);
v->defineConstantf("int", "PERSPECTIVE_FLAG", "0x%x", kPerspective_InfoFlag);
v->codeAppendf("mat3 shapeMatrix = mat3(%s, %s, vec3(0, 0, 1));",
inputs.attr(Attrib::kShapeMatrixX), inputs.attr(Attrib::kShapeMatrixY));
v->codeAppendf("if (0u != (%s & PERSPECTIVE_FLAG)) {",
v->codeAppendf("if (0 != (%s & PERSPECTIVE_FLAG)) {",
inputs.attr(Attrib::kInstanceInfo));
v->codeAppend ( "shapeMatrix[2] = ");
inputs.fetchNextParam(kVec3f_GrSLType);
@ -237,7 +237,7 @@ void GLSLInstanceProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
bool hasSingleShapeType = SkIsPow2(ip.batchInfo().fShapeTypes);
if (!hasSingleShapeType) {
v->define("SHAPE_TYPE_BIT", kShapeType_InfoBit);
v->defineConstant("SHAPE_TYPE_BIT", kShapeType_InfoBit);
v->codeAppendf("uint shapeType = %s >> SHAPE_TYPE_BIT;",
inputs.attr(Attrib::kInstanceInfo));
}
@ -285,8 +285,8 @@ void GLSLInstanceProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
if (ip.batchInfo().fInnerShapeTypes) {
bool hasSingleInnerShapeType = SkIsPow2(ip.batchInfo().fInnerShapeTypes);
if (!hasSingleInnerShapeType) {
v->definef("INNER_SHAPE_TYPE_MASK", "0x%xu", kInnerShapeType_InfoMask);
v->define("INNER_SHAPE_TYPE_BIT", kInnerShapeType_InfoBit);
v->defineConstantf("int", "INNER_SHAPE_TYPE_MASK", "0x%x", kInnerShapeType_InfoMask);
v->defineConstant("INNER_SHAPE_TYPE_BIT", kInnerShapeType_InfoBit);
v->codeAppendf("uint innerShapeType = ((%s & INNER_SHAPE_TYPE_MASK) >> "
"INNER_SHAPE_TYPE_BIT);",
inputs.attr(Attrib::kInstanceInfo));
@ -346,13 +346,13 @@ void GLSLInstanceProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
}
if (usedShapeDefinitions & kOval_ShapeFlag) {
v->definef("OVAL_SHAPE_TYPE", "%du", (int)ShapeType::kOval);
v->defineConstant("OVAL_SHAPE_TYPE", (int)ShapeType::kOval);
}
if (usedShapeDefinitions & kSimpleRRect_ShapeFlag) {
v->definef("SIMPLE_R_RECT_SHAPE_TYPE", "%du", (int)ShapeType::kSimpleRRect);
v->defineConstant("SIMPLE_R_RECT_SHAPE_TYPE", (int)ShapeType::kSimpleRRect);
}
if (usedShapeDefinitions & kNinePatch_ShapeFlag) {
v->definef("NINE_PATCH_SHAPE_TYPE", "%du", (int)ShapeType::kNinePatch);
v->defineConstant("NINE_PATCH_SHAPE_TYPE", (int)ShapeType::kNinePatch);
}
SkASSERT(!(usedShapeDefinitions & (kRect_ShapeFlag | kComplexRRect_ShapeFlag)));
@ -367,8 +367,8 @@ void GLSLInstanceProcessor::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
inputs.attr(Attrib::kLocalRect), inputs.attr(Attrib::kLocalRect));
}
if (ip.batchInfo().fHasLocalMatrix && ip.batchInfo().fHasParams) {
v->definef("LOCAL_MATRIX_FLAG", "0x%xu", kLocalMatrix_InfoFlag);
v->codeAppendf("if (0u != (%s & LOCAL_MATRIX_FLAG)) {",
v->defineConstantf("int", "LOCAL_MATRIX_FLAG", "0x%x", kLocalMatrix_InfoFlag);
v->codeAppendf("if (0 != (%s & LOCAL_MATRIX_FLAG)) {",
inputs.attr(Attrib::kInstanceInfo));
if (!ip.batchInfo().fUsesLocalCoords) {
inputs.skipParams(2);
@ -1179,7 +1179,7 @@ void GLSLInstanceProcessor::BackendMultisample::onInit(GrGLSLVaryingHandler* var
}
}
if (kRect_ShapeFlag != fBatchInfo.fShapeTypes) {
v->definef("SAMPLE_MASK_ALL", "0x%x", (1 << fEffectiveSampleCnt) - 1);
v->defineConstantf("int", "SAMPLE_MASK_ALL", "0x%x", (1 << fEffectiveSampleCnt) - 1);
varyingHandler->addFlatVarying("earlyAccept", &fEarlyAccept, kHigh_GrSLPrecision);
}
}
@ -1360,10 +1360,10 @@ void GLSLInstanceProcessor::BackendMultisample::onSetupInnerSimpleRRect(GrGLSLVe
void GLSLInstanceProcessor::BackendMultisample::onEmitCode(GrGLSLVertexBuilder*,
GrGLSLPPFragmentBuilder* f,
const char*, const char*) {
f->define("SAMPLE_COUNT", fEffectiveSampleCnt);
f->defineConstant("SAMPLE_COUNT", fEffectiveSampleCnt);
if (this->isMixedSampled()) {
f->definef("SAMPLE_MASK_ALL", "0x%x", (1 << fEffectiveSampleCnt) - 1);
f->definef("SAMPLE_MASK_MSB", "0x%x", 1 << (fEffectiveSampleCnt - 1));
f->defineConstantf("int", "SAMPLE_MASK_ALL", "0x%x", (1 << fEffectiveSampleCnt) - 1);
f->defineConstantf("int", "SAMPLE_MASK_MSB", "0x%x", 1 << (fEffectiveSampleCnt - 1));
}
if (kRect_ShapeFlag != (fBatchInfo.fShapeTypes | fBatchInfo.fInnerShapeTypes)) {

35
src/sksl/README Normal file
View File

@ -0,0 +1,35 @@
Overview
========
SkSL ("Skia Shading Language") is a variant of GLSL which is used as Skia's
internal shading language. SkSL is, at its heart, a single standardized version
of GLSL which avoids all of the various version and dialect differences found
in GLSL "in the wild", but it does bring a few of its own changes to the table.
Skia uses the SkSL compiler to convert SkSL code to GLSL, GLSL ES, or SPIR-V
before handing it over to the graphics driver.
Differences from GLSL
=====================
SkSL is based on GLSL 4.5. For the most part, write SkSL exactly as you would
desktop GLSL, and the SkSL compiler will take care of version and dialect
differences (for instance, you always use "in" and "out", and skslc will handle
translating them to "varying" and "attribute" as appropriate). Be aware of the
following differences between SkSL and GLSL:
* no #version or "precision" statement is required, and they will be ignored if
present
* the output color is sk_FragColor (do not declare it)
* lowp, mediump, and highp are always permitted (but will only be respected if
you run on a GLES device)
* you do not need to include ".0" to make a number a float (meaning that
"vec2(x, y) * 4" is perfectly legal in SkSL, unlike GLSL where it would often
have to be expressed "vec2(x, y) * 4.0". There is no performance penalty for
this, as the number is converted to a float at compile time)
* type suffixes on numbers (1.0f, 0xFFu) are both unnecessary and unsupported
* some built-in functions and one or two rarely-used language features are not
yet supported (sorry!)
SkSL is still under development, and is expected to diverge further from GLSL
over time.

View File

@ -15,6 +15,7 @@
#include "SkSLSPIRVCodeGenerator.h"
#include "ir/SkSLExpression.h"
#include "ir/SkSLIntLiteral.h"
#include "ir/SkSLModifiersDeclaration.h"
#include "ir/SkSLSymbolTable.h"
#include "ir/SkSLVarDeclaration.h"
#include "SkMutex.h"
@ -97,6 +98,7 @@ Compiler::Compiler()
ADD_TYPE(Sampler1D);
ADD_TYPE(Sampler2D);
ADD_TYPE(Sampler3D);
ADD_TYPE(SamplerExternalOES);
ADD_TYPE(SamplerCube);
ADD_TYPE(Sampler2DRect);
ADD_TYPE(Sampler1DArray);
@ -130,6 +132,7 @@ Compiler::Compiler()
std::vector<std::unique_ptr<ProgramElement>> ignored;
this->internalConvertProgram(SKSL_INCLUDE, &ignored);
printf("%s", errorText().c_str());
ASSERT(!fErrorCount);
}
@ -164,6 +167,14 @@ void Compiler::internalConvertProgram(std::string text,
}
break;
}
case ASTDeclaration::kModifiers_Kind: {
std::unique_ptr<ModifiersDeclaration> f = fIRGenerator->convertModifiersDeclaration(
(ASTModifiersDeclaration&) decl);
if (f) {
result->push_back(std::move(f));
}
break;
}
case ASTDeclaration::kInterfaceBlock_Kind: {
std::unique_ptr<InterfaceBlock> i = fIRGenerator->convertInterfaceBlock(
(ASTInterfaceBlock&) decl);

View File

@ -15,6 +15,8 @@
#include "SkSLErrorReporter.h"
#include "SkSLGLSLCodeGenerator.h"
#define SK_FRAGCOLOR_BUILTIN 10001
namespace SkSL {
class IRGenerator;
@ -24,6 +26,8 @@ class IRGenerator;
* file into an abstract syntax tree (a tree of ASTNodes), then performs semantic analysis to
* produce a Program (a tree of IRNodes), then feeds the Program into a CodeGenerator to produce
* compiled output.
*
* See the README for information about SkSL.
*/
class Compiler : public ErrorReporter {
public:

View File

@ -60,6 +60,7 @@ public:
, fSampler1D_Type(new Type("sampler1D", SpvDim1D, false, false, false, true))
, fSampler2D_Type(new Type("sampler2D", SpvDim2D, false, false, false, true))
, fSampler3D_Type(new Type("sampler3D", SpvDim3D, false, false, false, true))
, fSamplerExternalOES_Type(new Type("samplerExternalOES", SpvDim2D, false, false, false, true))
, fSamplerCube_Type(new Type("samplerCube"))
, fSampler2DRect_Type(new Type("sampler2DRect"))
, fSampler1DArray_Type(new Type("sampler1DArray"))
@ -169,6 +170,7 @@ public:
const std::unique_ptr<Type> fSampler1D_Type;
const std::unique_ptr<Type> fSampler2D_Type;
const std::unique_ptr<Type> fSampler3D_Type;
const std::unique_ptr<Type> fSamplerExternalOES_Type;
const std::unique_ptr<Type> fSamplerCube_Type;
const std::unique_ptr<Type> fSampler2DRect_Type;
const std::unique_ptr<Type> fSampler1DArray_Type;

View File

@ -14,8 +14,11 @@
#include "ir/SkSLExpressionStatement.h"
#include "ir/SkSLExtension.h"
#include "ir/SkSLIndexExpression.h"
#include "ir/SkSLModifiersDeclaration.h"
#include "ir/SkSLVariableReference.h"
#define SK_FRAGCOLOR_BUILTIN 10001
namespace SkSL {
void GLSLCodeGenerator::write(const char* s) {
@ -66,7 +69,7 @@ void GLSLCodeGenerator::writeType(const Type& type) {
this->writeLine("struct " + type.name() + " {");
fIndentation++;
for (const auto& f : type.fields()) {
this->writeModifiers(f.fModifiers);
this->writeModifiers(f.fModifiers, false);
// sizes (which must be static in structs) are part of the type name here
this->writeType(*f.fType);
this->writeLine(" " + f.fName + ";");
@ -124,7 +127,40 @@ void GLSLCodeGenerator::writeExpression(const Expression& expr, Precedence paren
}
}
static bool is_abs(Expression& expr) {
if (expr.fKind != Expression::kFunctionCall_Kind) {
return false;
}
return ((FunctionCall&) expr).fFunction.fName == "abs";
}
// turns min(abs(x), y) into (abs(x) > (tmpVar = y) ? tmpVar : abs(x)) to avoid a Tegra3 compiler
// bug.
void GLSLCodeGenerator::writeMinAbsHack(Expression& absExpr, Expression& otherExpr) {
ASSERT(!fCaps.fCanUseMinAndAbsTogether);
std::string varName = "minAbsHackVar" + to_string(fVarCount++);
this->fFunctionHeader += " " + otherExpr.fType.name() + " " + varName + ";\n";
this->write("(");
this->writeExpression(absExpr, kTopLevel_Precedence);
this->write(" > (" + varName + " = ");
this->writeExpression(otherExpr, kRelational_Precedence);
this->write(") ? " + varName + " : ");
this->writeExpression(absExpr, kTernary_Precedence);
this->write(")");
}
void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) {
if (!fCaps.fCanUseMinAndAbsTogether && c.fFunction.fName == "min") {
ASSERT(c.fArguments.size() == 2);
if (is_abs(*c.fArguments[0])) {
this->writeMinAbsHack(*c.fArguments[0], *c.fArguments[1]);
return;
}
if (is_abs(*c.fArguments[1])) {
this->writeMinAbsHack(*c.fArguments[1], *c.fArguments[0]);
return;
}
}
this->write(c.fFunction.fName + "(");
const char* separator = "";
for (const auto& arg : c.fArguments) {
@ -147,7 +183,15 @@ void GLSLCodeGenerator::writeConstructor(const Constructor& c) {
}
void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) {
this->write(ref.fVariable.fName);
if (ref.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOLOR_BUILTIN) {
if (fCaps.fMustDeclareFragmentShaderOutput) {
this->write("sk_FragColor");
} else {
this->write("gl_FragColor");
}
} else {
this->write(ref.fVariable.fName);
}
}
void GLSLCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
@ -270,7 +314,11 @@ void GLSLCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
}
void GLSLCodeGenerator::writeIntLiteral(const IntLiteral& i) {
this->write(to_string(i.fValue));
if (i.fType == *fContext.fUInt_Type) {
this->write(to_string(i.fValue & 0xffffffff) + "u");
} else {
this->write(to_string(i.fValue));
}
}
void GLSLCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
@ -284,28 +332,96 @@ void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
for (const auto& param : f.fDeclaration.fParameters) {
this->write(separator);
separator = ", ";
this->writeModifiers(param->fModifiers);
this->writeType(param->fType);
this->writeModifiers(param->fModifiers, false);
std::vector<int> sizes;
const Type* type = &param->fType;
while (type->kind() == Type::kArray_Kind) {
sizes.push_back(type->columns());
type = &type->componentType();
}
this->writeType(*type);
this->write(" " + param->fName);
for (int s : sizes) {
if (s <= 0) {
this->write("[]");
} else {
this->write("[" + to_string(s) + "]");
}
}
}
this->write(") ");
this->writeBlock(*f.fBody);
this->writeLine();
this->writeLine(") {");
fFunctionHeader = "";
std::ostream* oldOut = fOut;
std::stringstream buffer;
fOut = &buffer;
fIndentation++;
for (const auto& s : f.fBody->fStatements) {
this->writeStatement(*s);
this->writeLine();
}
fIndentation--;
this->writeLine("}");
fOut = oldOut;
this->write(fFunctionHeader);
this->write(buffer.str());
}
void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers) {
this->write(modifiers.description());
void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers,
bool globalContext) {
if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) {
this->write("noperspective ");
}
if (modifiers.fFlags & Modifiers::kFlat_Flag) {
this->write("flat ");
}
std::string layout = modifiers.fLayout.description();
if (layout.length()) {
this->write(layout + " ");
}
if ((modifiers.fFlags & Modifiers::kIn_Flag) &&
(modifiers.fFlags & Modifiers::kOut_Flag)) {
this->write("inout ");
} else if (modifiers.fFlags & Modifiers::kIn_Flag) {
if (globalContext && fCaps.fVersion < 130) {
this->write(fProgramKind == Program::kVertex_Kind ? "attribute "
: "varying ");
} else {
this->write("in ");
}
} else if (modifiers.fFlags & Modifiers::kOut_Flag) {
if (globalContext && fCaps.fVersion < 130) {
this->write("varying ");
} else {
this->write("out ");
}
}
if (modifiers.fFlags & Modifiers::kUniform_Flag) {
this->write("uniform ");
}
if (modifiers.fFlags & Modifiers::kConst_Flag) {
this->write("const ");
}
if (fCaps.fUsesPrecisionModifiers) {
if (modifiers.fFlags & Modifiers::kLowp_Flag) {
this->write("lowp ");
}
if (modifiers.fFlags & Modifiers::kHighp_Flag) {
this->write("highp ");
}
}
}
void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
if (intf.fVariable.fName == "gl_PerVertex") {
return;
}
this->writeModifiers(intf.fVariable.fModifiers);
this->writeModifiers(intf.fVariable.fModifiers, true);
this->writeLine(intf.fVariable.fType.name() + " {");
fIndentation++;
for (const auto& f : intf.fVariable.fType.fields()) {
this->writeModifiers(f.fModifiers);
this->writeModifiers(f.fModifiers, false);
this->writeType(*f.fType);
this->writeLine(" " + f.fName + ";");
}
@ -313,9 +429,9 @@ void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
this->writeLine("};");
}
void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl) {
void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
ASSERT(decl.fVars.size() > 0);
this->writeModifiers(decl.fVars[0].fVar->fModifiers);
this->writeModifiers(decl.fVars[0].fVar->fModifiers, global);
this->writeType(decl.fBaseType);
std::string separator = " ";
for (const auto& var : decl.fVars) {
@ -325,7 +441,9 @@ void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl) {
this->write(var.fVar->fName);
for (const auto& size : var.fSizes) {
this->write("[");
this->writeExpression(*size, kTopLevel_Precedence);
if (size) {
this->writeExpression(*size, kTopLevel_Precedence);
}
this->write("]");
}
if (var.fValue) {
@ -349,7 +467,7 @@ void GLSLCodeGenerator::writeStatement(const Statement& s) {
this->writeReturnStatement((ReturnStatement&) s);
break;
case Statement::kVarDeclarations_Kind:
this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration);
this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
break;
case Statement::kIf_Kind:
this->writeIfStatement((IfStatement&) s);
@ -444,22 +562,42 @@ void GLSLCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
void GLSLCodeGenerator::generateCode(const Program& program, std::ostream& out) {
ASSERT(fOut == nullptr);
fOut = &out;
fProgramKind = program.fKind;
this->write("#version " + to_string(fCaps.fVersion));
if (fCaps.fStandard == GLCaps::kGLES_Standard) {
if (fCaps.fStandard == GLCaps::kGLES_Standard && fCaps.fVersion >= 300) {
this->write(" es");
} else if (fCaps.fIsCoreProfile) {
this->write(" core");
}
this->writeLine();
for (const auto& e : program.fElements) {
if (e->fKind == ProgramElement::kExtension_Kind) {
this->writeExtension((Extension&) *e);
}
}
if (fCaps.fStandard == GLCaps::kGLES_Standard) {
this->writeLine("precision mediump float;");
}
for (const auto& e : program.fElements) {
switch (e->fKind) {
case ProgramElement::kExtension_Kind:
this->writeExtension((Extension&) *e);
break;
case ProgramElement::kVar_Kind: {
VarDeclarations& decl = (VarDeclarations&) *e;
if (decl.fVars.size() > 0 &&
decl.fVars[0].fVar->fModifiers.fLayout.fBuiltin == -1) {
this->writeVarDeclarations(decl);
this->writeLine();
if (decl.fVars.size() > 0) {
int builtin = decl.fVars[0].fVar->fModifiers.fLayout.fBuiltin;
if (builtin == -1) {
// normal var
this->writeVarDeclarations(decl, true);
this->writeLine();
} else if (builtin == SK_FRAGCOLOR_BUILTIN &&
fCaps.fMustDeclareFragmentShaderOutput) {
this->write("out ");
if (fCaps.fUsesPrecisionModifiers) {
this->write("mediump ");
}
this->writeLine("vec4 sk_FragColor;");
}
}
break;
}
@ -469,6 +607,10 @@ void GLSLCodeGenerator::generateCode(const Program& program, std::ostream& out)
case ProgramElement::kFunction_Kind:
this->writeFunction((FunctionDefinition&) *e);
break;
case ProgramElement::kModifiers_Kind:
this->writeModifiers(((ModifiersDeclaration&) *e).fModifiers, true);
this->writeLine(";");
break;
default:
printf("%s\n", e->description().c_str());
ABORT("unsupported program element");

View File

@ -50,6 +50,11 @@ struct GLCaps {
kGL_Standard,
kGLES_Standard
} fStandard;
bool fIsCoreProfile;
bool fUsesPrecisionModifiers;
bool fMustDeclareFragmentShaderOutput;
// The Tegra3 compiler will sometimes never return if we have min(abs(x), y)
bool fCanUseMinAndAbsTogether;
};
/**
@ -81,6 +86,8 @@ public:
GLSLCodeGenerator(const Context* context, GLCaps caps)
: fContext(*context)
, fCaps(caps)
, fOut(nullptr)
, fVarCount(0)
, fIndentation(0)
, fAtLineStart(true) {}
@ -111,11 +118,11 @@ private:
void writeLayout(const Layout& layout);
void writeModifiers(const Modifiers& modifiers);
void writeModifiers(const Modifiers& modifiers, bool globalContext);
void writeGlobalVars(const VarDeclaration& vs);
void writeVarDeclarations(const VarDeclarations& decl);
void writeVarDeclarations(const VarDeclarations& decl, bool global);
void writeVariableReference(const VariableReference& ref);
@ -123,6 +130,8 @@ private:
void writeIntrinsicCall(const FunctionCall& c);
void writeMinAbsHack(Expression& absExpr, Expression& otherExpr);
void writeFunctionCall(const FunctionCall& c);
void writeConstructor(const Constructor& c);
@ -164,6 +173,9 @@ private:
const Context& fContext;
const GLCaps fCaps;
std::ostream* fOut;
std::string fFunctionHeader;
Program::Kind fProgramKind;
int fVarCount;
int fIndentation;
bool fAtLineStart;
// Keeps track of which struct types we have written. Given that we are unlikely to ever write

View File

@ -189,10 +189,16 @@ std::unique_ptr<VarDeclarations> IRGenerator::convertVarDeclarations(const ASTVa
}
value = this->coerce(std::move(value), *type);
}
if ("gl_FragCoord" == varDecl.fName && (*fSymbolTable)[varDecl.fName]) {
if ("sk_FragColor" == varDecl.fName && (*fSymbolTable)[varDecl.fName]) {
// already defined, ignore
} else if ((*fSymbolTable)[varDecl.fName] &&
(*fSymbolTable)[varDecl.fName]->fKind == Symbol::kVariable_Kind &&
((Variable*) (*fSymbolTable)[varDecl.fName])->fModifiers.fLayout.fBuiltin >= 0) {
// already defined, just update the modifiers
Variable* old = (Variable*) (*fSymbolTable)[varDecl.fName];
int builtin = old->fModifiers.fLayout.fBuiltin;
old->fModifiers = var->fModifiers;
old->fModifiers.fLayout.fBuiltin = builtin;
} else {
variables.emplace_back(var.get(), std::move(sizes), std::move(value));
fSymbolTable->add(varDecl.fName, std::move(var));
@ -203,6 +209,12 @@ std::unique_ptr<VarDeclarations> IRGenerator::convertVarDeclarations(const ASTVa
std::move(variables)));
}
std::unique_ptr<ModifiersDeclaration> IRGenerator::convertModifiersDeclaration(
const ASTModifiersDeclaration& m) {
Modifiers modifiers = this->convertModifiers(m.fModifiers);
return std::unique_ptr<ModifiersDeclaration>(new ModifiersDeclaration(modifiers));
}
std::unique_ptr<Statement> IRGenerator::convertIf(const ASTIfStatement& s) {
std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*s.fTest),
*fContext.fBool_Type);
@ -419,8 +431,9 @@ std::unique_ptr<FunctionDefinition> IRGenerator::convertFunction(const ASTFuncti
for (size_t i = 0; i < parameters.size(); i++) {
if (parameters[i]->fModifiers != other->fParameters[i]->fModifiers) {
fErrors.error(f.fPosition, "modifiers on parameter " +
to_string(i + 1) + " differ between " +
"declaration and definition");
to_string((uint64_t) i + 1) +
" differ between declaration and "
"definition");
return nullptr;
}
}
@ -616,8 +629,9 @@ std::unique_ptr<Expression> IRGenerator::coerce(std::unique_ptr<Expression> expr
ASSERT(ctor);
return this->call(Position(), std::move(ctor), std::move(args));
}
ABORT("cannot coerce %s to %s", expr->fType.description().c_str(),
type.description().c_str());
std::vector<std::unique_ptr<Expression>> args;
args.push_back(std::move(expr));
return std::unique_ptr<Expression>(new Constructor(Position(), type, std::move(args)));
}
static bool is_matrix_multiply(const Type& left, const Type& right) {
@ -832,12 +846,12 @@ std::unique_ptr<Expression> IRGenerator::call(Position position,
std::vector<std::unique_ptr<Expression>> arguments) {
if (function.fParameters.size() != arguments.size()) {
std::string msg = "call to '" + function.fName + "' expected " +
to_string(function.fParameters.size()) +
to_string((uint64_t) function.fParameters.size()) +
" argument";
if (function.fParameters.size() != 1) {
msg += "s";
}
msg += ", but found " + to_string(arguments.size());
msg += ", but found " + to_string((uint64_t) arguments.size());
fErrors.error(position, msg);
return nullptr;
}
@ -921,7 +935,8 @@ std::unique_ptr<Expression> IRGenerator::convertConstructor(
std::vector<std::unique_ptr<Expression>> args) {
// FIXME: add support for structs and arrays
Type::Kind kind = type.kind();
if (!type.isNumber() && kind != Type::kVector_Kind && kind != Type::kMatrix_Kind) {
if (!type.isNumber() && kind != Type::kVector_Kind && kind != Type::kMatrix_Kind &&
kind != Type::kArray_Kind) {
fErrors.error(position, "cannot construct '" + type.description() + "'");
return nullptr;
}
@ -938,7 +953,7 @@ std::unique_ptr<Expression> IRGenerator::convertConstructor(
if (args.size() != 1) {
fErrors.error(position, "invalid arguments to '" + type.description() +
"' constructor, (expected exactly 1 argument, but found " +
to_string(args.size()) + ")");
to_string((uint64_t) args.size()) + ")");
}
if (args[0]->fType == *fContext.fBool_Type) {
std::unique_ptr<IntLiteral> zero(new IntLiteral(fContext, position, 0));
@ -953,6 +968,18 @@ std::unique_ptr<Expression> IRGenerator::convertConstructor(
"' constructor (expected a number or bool, but found '" +
args[0]->fType.description() + "')");
}
if (args[0]->fKind == Expression::kIntLiteral_Kind && (type == *fContext.fInt_Type ||
type == *fContext.fUInt_Type)) {
return std::unique_ptr<Expression>(new IntLiteral(fContext,
position,
((IntLiteral&) *args[0]).fValue,
&type));
}
} else if (kind == Type::kArray_Kind) {
const Type& base = type.componentType();
for (size_t i = 0; i < args.size(); i++) {
args[i] = this->coerce(std::move(args[i]), base);
}
} else {
ASSERT(kind == Type::kVector_Kind || kind == Type::kMatrix_Kind);
int actual = 0;
@ -1054,7 +1081,8 @@ std::unique_ptr<Expression> IRGenerator::convertPrefixExpression(
std::unique_ptr<Expression> IRGenerator::convertIndex(std::unique_ptr<Expression> base,
const ASTExpression& index) {
if (base->fType.kind() != Type::kArray_Kind && base->fType.kind() != Type::kMatrix_Kind) {
if (base->fType.kind() != Type::kArray_Kind && base->fType.kind() != Type::kMatrix_Kind &&
base->fType.kind() != Type::kVector_Kind) {
fErrors.error(base->fPosition, "expected array, but found '" + base->fType.description() +
"'");
return nullptr;
@ -1063,9 +1091,11 @@ std::unique_ptr<Expression> IRGenerator::convertIndex(std::unique_ptr<Expression
if (!converted) {
return nullptr;
}
converted = this->coerce(std::move(converted), *fContext.fInt_Type);
if (!converted) {
return nullptr;
if (converted->fType != *fContext.fUInt_Type) {
converted = this->coerce(std::move(converted), *fContext.fInt_Type);
if (!converted) {
return nullptr;
}
}
return std::unique_ptr<Expression>(new IndexExpression(fContext, std::move(base),
std::move(converted)));
@ -1143,9 +1173,21 @@ std::unique_ptr<Expression> IRGenerator::convertSuffixExpression(
return nullptr;
}
switch (expression.fSuffix->fKind) {
case ASTSuffix::kIndex_Kind:
return this->convertIndex(std::move(base),
*((ASTIndexSuffix&) *expression.fSuffix).fExpression);
case ASTSuffix::kIndex_Kind: {
const ASTExpression* expr = ((ASTIndexSuffix&) *expression.fSuffix).fExpression.get();
if (expr) {
return this->convertIndex(std::move(base), *expr);
} else if (base->fKind == Expression::kTypeReference_Kind) {
const Type& oldType = ((TypeReference&) *base).fValue;
Type* newType = new Type(oldType.name() + "[]", Type::kArray_Kind, oldType,
-1);
fSymbolTable->takeOwnership(newType);
return std::unique_ptr<Expression>(new TypeReference(fContext, base->fPosition,
*newType));
} else {
fErrors.error(expression.fPosition, "'[]' must follow a type name");
}
}
case ASTSuffix::kCall_Kind: {
auto rawArguments = &((ASTCallSuffix&) *expression.fSuffix).fArguments;
std::vector<std::unique_ptr<Expression>> arguments;

View File

@ -25,6 +25,7 @@
#include "ast/SkSLASTIfStatement.h"
#include "ast/SkSLASTInterfaceBlock.h"
#include "ast/SkSLASTModifiers.h"
#include "ast/SkSLASTModifiersDeclaration.h"
#include "ast/SkSLASTPrefixExpression.h"
#include "ast/SkSLASTReturnStatement.h"
#include "ast/SkSLASTStatement.h"
@ -39,6 +40,7 @@
#include "ir/SkSLFunctionDefinition.h"
#include "ir/SkSLInterfaceBlock.h"
#include "ir/SkSLModifiers.h"
#include "ir/SkSLModifiersDeclaration.h"
#include "ir/SkSLSymbolTable.h"
#include "ir/SkSLStatement.h"
#include "ir/SkSLType.h"
@ -61,6 +63,8 @@ public:
std::unique_ptr<FunctionDefinition> convertFunction(const ASTFunction& f);
std::unique_ptr<Statement> convertStatement(const ASTStatement& statement);
std::unique_ptr<Expression> convertExpression(const ASTExpression& expression);
std::unique_ptr<ModifiersDeclaration> convertModifiersDeclaration(
const ASTModifiersDeclaration& m);
private:
void pushSymbolTable();

View File

@ -9,6 +9,24 @@
#include <fstream>
#include "SkSLCompiler.h"
bool endsWith(const std::string& s, const std::string& ending) {
if (s.length() >= ending.length()) {
return (0 == s.compare(s.length() - ending.length(), ending.length(), ending));
}
return false;
}
static SkSL::GLCaps default_caps() {
return {
400,
SkSL::GLCaps::kGL_Standard,
false, // isCoreProfile
false, // usesPrecisionModifiers;
false, // mustDeclareFragmentShaderOutput
true // canUseMinAndAbsTogether
};
}
/**
* Very simple standalone executable to facilitate testing.
*/
@ -35,14 +53,30 @@ int main(int argc, const char** argv) {
printf("error reading '%s'\n", argv[1]);
exit(2);
}
std::ofstream out(argv[2], std::ofstream::binary);
SkSL::Compiler compiler;
if (!compiler.toSPIRV(kind, text, out)) {
printf("%s", compiler.errorText().c_str());
exit(3);
}
if (out.rdstate()) {
printf("error writing '%s'\n", argv[2]);
exit(4);
std::string name(argv[2]);
if (endsWith(name, ".spirv")) {
std::ofstream out(argv[2], std::ofstream::binary);
SkSL::Compiler compiler;
if (!compiler.toSPIRV(kind, text, out)) {
printf("%s", compiler.errorText().c_str());
exit(3);
}
if (out.rdstate()) {
printf("error writing '%s'\n", argv[2]);
exit(4);
}
} else if (endsWith(name, ".glsl")) {
std::ofstream out(argv[2], std::ofstream::binary);
SkSL::Compiler compiler;
if (!compiler.toGLSL(kind, text, default_caps(), out)) {
printf("%s", compiler.errorText().c_str());
exit(3);
}
if (out.rdstate()) {
printf("error writing '%s'\n", argv[2]);
exit(4);
}
} else {
printf("expected output filename to end with '.spirv' or '.glsl'");
}
}

View File

@ -56,6 +56,7 @@
#include "ast/SkSLASTIndexSuffix.h"
#include "ast/SkSLASTInterfaceBlock.h"
#include "ast/SkSLASTIntLiteral.h"
#include "ast/SkSLASTModifiersDeclaration.h"
#include "ast/SkSLASTParameter.h"
#include "ast/SkSLASTPrefixExpression.h"
#include "ast/SkSLASTReturnStatement.h"
@ -185,7 +186,8 @@ void Parser::precision() {
this->expect(Token::SEMICOLON, "';'");
}
/* DIRECTIVE(#version) INT_LITERAL | DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */
/* DIRECTIVE(#version) INT_LITERAL ("es" | "compatibility")? |
DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */
std::unique_ptr<ASTDeclaration> Parser::directive() {
Token start;
if (!this->expect(Token::DIRECTIVE, "a directive", &start)) {
@ -193,7 +195,12 @@ std::unique_ptr<ASTDeclaration> Parser::directive() {
}
if (start.fText == "#version") {
this->expect(Token::INT_LITERAL, "a version number");
// ignored for now
Token next = this->peek();
if (next.fText == "es" || next.fText == "compatibility") {
this->nextToken();
}
// version is ignored for now; it will eventually become an error when we stop pretending
// to be GLSL
return nullptr;
} else if (start.fText == "#extension") {
Token name;
@ -227,6 +234,10 @@ std::unique_ptr<ASTDeclaration> Parser::declaration() {
if (lookahead.fKind == Token::STRUCT) {
return this->structVarDeclaration(modifiers);
}
if (lookahead.fKind == Token::SEMICOLON) {
this->nextToken();
return std::unique_ptr<ASTDeclaration>(new ASTModifiersDeclaration(modifiers));
}
std::unique_ptr<ASTType> type(this->type());
if (!type) {
return nullptr;
@ -477,10 +488,13 @@ ASTLayout Parser::layout() {
int set = -1;
int builtin = -1;
bool originUpperLeft = false;
bool overrideCoverage = false;
bool blendSupportAllEquations = false;
if (this->peek().fKind == Token::LAYOUT) {
this->nextToken();
if (!this->expect(Token::LPAREN, "'('")) {
return ASTLayout(location, binding, index, set, builtin, originUpperLeft);
return ASTLayout(location, binding, index, set, builtin, originUpperLeft,
overrideCoverage, blendSupportAllEquations);
}
for (;;) {
Token t = this->nextToken();
@ -496,6 +510,10 @@ ASTLayout Parser::layout() {
builtin = this->layoutInt();
} else if (t.fText == "origin_upper_left") {
originUpperLeft = true;
} else if (t.fText == "override_coverage") {
overrideCoverage = true;
} else if (t.fText == "blend_support_all_equations") {
blendSupportAllEquations = true;
} else {
this->error(t.fPosition, ("'" + t.fText +
"' is not a valid layout qualifier").c_str());
@ -509,7 +527,8 @@ ASTLayout Parser::layout() {
}
}
}
return ASTLayout(location, binding, index, set, builtin, originUpperLeft);
return ASTLayout(location, binding, index, set, builtin, originUpperLeft, overrideCoverage,
blendSupportAllEquations);
}
/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE)* */
@ -1254,12 +1273,16 @@ std::unique_ptr<ASTExpression> Parser::postfixExpression() {
}
}
/* LBRACKET expression RBRACKET | DOT IDENTIFIER | LPAREN parameters RPAREN |
/* LBRACKET expression? RBRACKET | DOT IDENTIFIER | LPAREN parameters RPAREN |
PLUSPLUS | MINUSMINUS */
std::unique_ptr<ASTSuffix> Parser::suffix() {
Token next = this->nextToken();
switch (next.fKind) {
case Token::LBRACKET: {
if (this->peek().fKind == Token::RBRACKET) {
this->nextToken();
return std::unique_ptr<ASTSuffix>(new ASTIndexSuffix(next.fPosition));
}
std::unique_ptr<ASTExpression> e = this->expression();
if (!e) {
return nullptr;

View File

@ -15,6 +15,7 @@
#include "ir/SkSLExtension.h"
#include "ir/SkSLIndexExpression.h"
#include "ir/SkSLVariableReference.h"
#include "SkSLCompiler.h"
namespace SkSL {
@ -2321,7 +2322,7 @@ void SPIRVCodeGenerator::writeLayout(const Layout& layout, SpvId target) {
this->writeInstruction(SpvOpDecorate, target, SpvDecorationDescriptorSet, layout.fSet,
fDecorationBuffer);
}
if (layout.fBuiltin >= 0) {
if (layout.fBuiltin >= 0 && layout.fBuiltin != SK_FRAGCOLOR_BUILTIN) {
this->writeInstruction(SpvOpDecorate, target, SpvDecorationBuiltIn, layout.fBuiltin,
fDecorationBuffer);
}
@ -2363,10 +2364,19 @@ SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
return result;
}
void SPIRVCodeGenerator::writeGlobalVars(const VarDeclarations& decl, std::ostream& out) {
#define BUILTIN_IGNORE 9999
void SPIRVCodeGenerator::writeGlobalVars(Program::Kind kind, const VarDeclarations& decl,
std::ostream& out) {
for (size_t i = 0; i < decl.fVars.size(); i++) {
const VarDeclaration& varDecl = decl.fVars[i];
const Variable* var = varDecl.fVar;
if (var->fModifiers.fLayout.fBuiltin == BUILTIN_IGNORE) {
continue;
}
if (var->fModifiers.fLayout.fBuiltin == SK_FRAGCOLOR_BUILTIN &&
kind != Program::kFragment_Kind) {
continue;
}
if (!var->fIsReadFrom && !var->fIsWrittenTo &&
!(var->fModifiers.fFlags & (Modifiers::kIn_Flag |
Modifiers::kOut_Flag |
@ -2562,7 +2572,8 @@ void SPIRVCodeGenerator::writeInstructions(const Program& program, std::ostream&
}
for (size_t i = 0; i < program.fElements.size(); i++) {
if (program.fElements[i]->fKind == ProgramElement::kVar_Kind) {
this->writeGlobalVars(((VarDeclarations&) *program.fElements[i]), body);
this->writeGlobalVars(program.fKind, ((VarDeclarations&) *program.fElements[i]),
body);
}
}
for (size_t i = 0; i < program.fElements.size(); i++) {

View File

@ -115,7 +115,7 @@ private:
SpvId writeFunction(const FunctionDefinition& f, std::ostream& out);
void writeGlobalVars(const VarDeclarations& v, std::ostream& out);
void writeGlobalVars(Program::Kind kind, const VarDeclarations& v, std::ostream& out);
void writeVarDeclarations(const VarDeclarations& decl, std::ostream& out);

View File

@ -9,7 +9,48 @@
namespace SkSL {
std::string to_string(double value) {
std::stringstream buffer;
buffer << std::setprecision(std::numeric_limits<double>::digits10) << value;
std::string result = buffer.str();
if (result.find_last_of(".") == std::string::npos &&
result.find_last_of("e") == std::string::npos) {
result += ".0";
}
return result;
}
std::string to_string(int32_t value) {
std::stringstream buffer;
buffer << value;
return buffer.str();
}
std::string to_string(uint32_t value) {
std::stringstream buffer;
buffer << value;
return buffer.str();
}
std::string to_string(int64_t value) {
std::stringstream buffer;
buffer << value;
return buffer.str();
}
std::string to_string(uint64_t value) {
std::stringstream buffer;
buffer << value;
return buffer.str();
}
int stoi(std::string s) {
if (s.size() > 2 && s[0] == '0' && s[1] == 'x') {
char* p;
int result = strtoul(s.substr(2).c_str(), &p, 16);
ASSERT(*p == 0);
return result;
}
return atoi(s.c_str());
}
@ -18,6 +59,12 @@ double stod(std::string s) {
}
long stol(std::string s) {
if (s.size() > 2 && s[0] == '0' && s[1] == 'x') {
char* p;
int result = strtoul(s.substr(2).c_str(), &p, 16);
ASSERT(*p == 0);
return result;
}
return atol(s.c_str());
}

View File

@ -19,11 +19,15 @@ namespace SkSL {
// our own definitions of certain std:: functions, because they are not always present on Android
template <typename T> std::string to_string(T value) {
std::stringstream buffer;
buffer << std::setprecision(std::numeric_limits<T>::digits10) << value;
return buffer.str();
}
std::string to_string(double value);
std::string to_string(int32_t value);
std::string to_string(uint32_t value);
std::string to_string(int64_t value);
std::string to_string(uint64_t value);
#if _MSC_VER
#define NORETURN __declspec(noreturn)

View File

@ -20,7 +20,8 @@ struct ASTDeclaration : public ASTPositionNode {
kVar_Kind,
kFunction_Kind,
kInterfaceBlock_Kind,
kExtension_Kind
kExtension_Kind,
kModifiers_Kind
};
ASTDeclaration(Position position, Kind kind)

View File

@ -14,17 +14,27 @@
namespace SkSL {
/**
* A bracketed expression, as in '[0]', indicating an array access.
* A bracketed expression, as in '[0]', indicating an array access. Empty brackets (as occur in
* 'float[](5, 6)' are represented with a null fExpression.
*/
struct ASTIndexSuffix : public ASTSuffix {
ASTIndexSuffix(Position position)
: INHERITED(position, ASTSuffix::kIndex_Kind)
, fExpression(nullptr) {}
ASTIndexSuffix(std::unique_ptr<ASTExpression> expression)
: INHERITED(expression->fPosition, ASTSuffix::kIndex_Kind)
: INHERITED(expression ? expression->fPosition : Position(), ASTSuffix::kIndex_Kind)
, fExpression(std::move(expression)) {}
std::string description() const override {
return "[" + fExpression->description() + "]";
if (fExpression) {
return "[" + fExpression->description() + "]";
} else {
return "[]";
}
}
// may be null
std::unique_ptr<ASTExpression> fExpression;
typedef ASTSuffix INHERITED;

View File

@ -20,13 +20,16 @@ namespace SkSL {
*/
struct ASTLayout : public ASTNode {
// For all parameters, a -1 means no value
ASTLayout(int location, int binding, int index, int set, int builtin, bool originUpperLeft)
ASTLayout(int location, int binding, int index, int set, int builtin, bool originUpperLeft,
bool overrideCoverage, bool blendSupportAllEquations)
: fLocation(location)
, fBinding(binding)
, fIndex(index)
, fSet(set)
, fBuiltin(builtin)
, fOriginUpperLeft(originUpperLeft) {}
, fOriginUpperLeft(originUpperLeft)
, fOverrideCoverage(overrideCoverage)
, fBlendSupportAllEquations(blendSupportAllEquations) {}
std::string description() const {
std::string result;
@ -55,6 +58,14 @@ struct ASTLayout : public ASTNode {
result += separator + "origin_upper_left";
separator = ", ";
}
if (fOverrideCoverage) {
result += separator + "override_coverage";
separator = ", ";
}
if (fBlendSupportAllEquations) {
result += separator + "blend_support_all_equations";
separator = ", ";
}
if (result.length() > 0) {
result = "layout (" + result + ")";
}
@ -67,6 +78,8 @@ struct ASTLayout : public ASTNode {
const int fSet;
const int fBuiltin;
const bool fOriginUpperLeft;
const bool fOverrideCoverage;
const bool fBlendSupportAllEquations;
};
} // namespace

View File

@ -0,0 +1,37 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_ASTMODIFIERDECLARATION
#define SKSL_ASTMODIFIERDECLARATION
#include "SkSLASTDeclaration.h"
#include "SkSLASTModifiers.h"
namespace SkSL {
/**
* A declaration that consists only of modifiers, e.g.:
*
* layout(blend_support_all_equations) out;
*/
struct ASTModifiersDeclaration : public ASTDeclaration {
ASTModifiersDeclaration(ASTModifiers modifiers)
: INHERITED(Position(), kModifiers_Kind)
, fModifiers(modifiers) {}
std::string description() const {
return fModifiers.description() + ";";
}
ASTModifiers fModifiers;
typedef ASTDeclaration INHERITED;
};
} // namespace
#endif

View File

@ -19,7 +19,7 @@ namespace SkSL {
static const Type& index_type(const Context& context, const Type& type) {
if (type.kind() == Type::kMatrix_Kind) {
if (type.componentType() == *context.fFloat_Type) {
switch (type.columns()) {
switch (type.rows()) {
case 2: return *context.fVec2_Type;
case 3: return *context.fVec3_Type;
case 4: return *context.fVec4_Type;
@ -47,7 +47,7 @@ struct IndexExpression : public Expression {
: INHERITED(base->fPosition, kIndex_Kind, index_type(context, base->fType))
, fBase(std::move(base))
, fIndex(std::move(index)) {
ASSERT(fIndex->fType == *context.fInt_Type);
ASSERT(fIndex->fType == *context.fInt_Type || fIndex->fType == *context.fUInt_Type);
}
std::string description() const override {

View File

@ -18,8 +18,8 @@ namespace SkSL {
struct IntLiteral : public Expression {
// FIXME: we will need to revisit this if/when we add full support for both signed and unsigned
// 64-bit integers, but for right now an int64_t will hold every value we care about
IntLiteral(const Context& context, Position position, int64_t value)
: INHERITED(position, kIntLiteral_Kind, *context.fInt_Type)
IntLiteral(const Context& context, Position position, int64_t value, const Type* type = nullptr)
: INHERITED(position, kIntLiteral_Kind, type ? *type : *context.fInt_Type)
, fValue(value) {}
virtual std::string description() const override {

View File

@ -22,15 +22,20 @@ struct Layout {
, fIndex(layout.fIndex)
, fSet(layout.fSet)
, fBuiltin(layout.fBuiltin)
, fOriginUpperLeft(layout.fOriginUpperLeft) {}
, fOriginUpperLeft(layout.fOriginUpperLeft)
, fOverrideCoverage(layout.fOverrideCoverage)
, fBlendSupportAllEquations(layout.fBlendSupportAllEquations) {}
Layout(int location, int binding, int index, int set, int builtin, bool originUpperLeft)
Layout(int location, int binding, int index, int set, int builtin, bool originUpperLeft,
bool overrideCoverage, bool blendSupportAllEquations)
: fLocation(location)
, fBinding(binding)
, fIndex(index)
, fSet(set)
, fBuiltin(builtin)
, fOriginUpperLeft(originUpperLeft) {}
, fOriginUpperLeft(originUpperLeft)
, fOverrideCoverage(overrideCoverage)
, fBlendSupportAllEquations(blendSupportAllEquations) {}
std::string description() const {
std::string result;
@ -59,6 +64,14 @@ struct Layout {
result += separator + "origin_upper_left";
separator = ", ";
}
if (fOverrideCoverage) {
result += separator + "override_coverage";
separator = ", ";
}
if (fBlendSupportAllEquations) {
result += separator + "blend_support_all_equations";
separator = ", ";
}
if (result.length() > 0) {
result = "layout (" + result + ")";
}
@ -66,11 +79,14 @@ struct Layout {
}
bool operator==(const Layout& other) const {
return fLocation == other.fLocation &&
fBinding == other.fBinding &&
fIndex == other.fIndex &&
fSet == other.fSet &&
fBuiltin == other.fBuiltin;
return fLocation == other.fLocation &&
fBinding == other.fBinding &&
fIndex == other.fIndex &&
fSet == other.fSet &&
fBuiltin == other.fBuiltin &&
fOriginUpperLeft == other.fOriginUpperLeft &&
fOverrideCoverage == other.fOverrideCoverage &&
fBlendSupportAllEquations == other.fBlendSupportAllEquations;
}
bool operator!=(const Layout& other) const {
@ -85,6 +101,8 @@ struct Layout {
int fSet;
int fBuiltin;
bool fOriginUpperLeft;
bool fOverrideCoverage;
bool fBlendSupportAllEquations;
};
} // namespace

View File

@ -0,0 +1,37 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_MODIFIERDECLARATION
#define SKSL_MODIFIERDECLARATION
#include "SkSLProgramElement.h"
#include "SkSLModifiers.h"
namespace SkSL {
/**
* A declaration that consists only of modifiers, e.g.:
*
* layout(blend_support_all_equations) out;
*/
struct ModifiersDeclaration : public ProgramElement {
ModifiersDeclaration(Modifiers modifiers)
: INHERITED(Position(), kModifiers_Kind)
, fModifiers(modifiers) {}
std::string description() const {
return fModifiers.description() + ";";
}
Modifiers fModifiers;
typedef ProgramElement INHERITED;
};
} // namespace
#endif

View File

@ -20,7 +20,8 @@ struct ProgramElement : public IRNode {
kVar_Kind,
kFunction_Kind,
kInterfaceBlock_Kind,
kExtension_Kind
kExtension_Kind,
kModifiers_Kind
};
ProgramElement(Position position, Kind kind)

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,8 @@ LETTER [a-zA-Z_$]
{DIGIT}+ { return SkSL::Token::INT_LITERAL; }
"0x"[0-9a-zA-Z]+ { return SkSL::Token::INT_LITERAL; }
true { return SkSL::Token::TRUE_LITERAL; }
false { return SkSL::Token::FALSE_LITERAL; }

View File

@ -244,6 +244,8 @@ int textureQueryLevels(samplerCubeArrayShadow sampler);
$gvec4 texture($gsampler1D sampler, float P);
$gvec4 texture($gsampler1D sampler, float P, float bias);
$gvec4 texture($gsampler2D sampler, vec2 P);
vec4 texture(samplerExternalOES sampler, vec2 P, float bias);
vec4 texture(samplerExternalOES sampler, vec2 P);
$gvec4 texture($gsampler2D sampler, vec2 P, float bias);
$gvec4 texture($gsampler3D sampler, vec3 P);
$gvec4 texture($gsampler3D sampler, vec3 P, float bias);
@ -464,6 +466,7 @@ vec4 texture1DLod(sampler1D sampler, float coord, float lod);
vec4 texture1DProjLod(sampler1D sampler, vec2 coord, float lod);
vec4 texture1DProjLod(sampler1D sampler, vec4 coord, float lod);
vec4 texture2D(sampler2D sampler, vec2 coord);
vec4 texture2D(samplerExternalOES sampler, vec2 coord);
vec4 texture2D(sampler2D sampler, vec2 coord, float bias);
vec4 texture2DProj(sampler2D sampler, vec3 coord);
vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias);

View File

@ -4,4 +4,16 @@ STRINGIFY(
layout(builtin=15) in vec4 gl_FragCoord;
// 9999 is a temporary value that causes us to ignore these declarations beyond
// adding them to the symbol table. This works fine in GLSL (where they do not
// require any further handling) but will fail in SPIR-V. We'll have a better
// solution for this soon.
layout(builtin=9999) vec4 gl_LastFragData[1];
layout(builtin=9999) vec4 gl_LastFragColor;
layout(builtin=9999) vec4 gl_LastFragColorARM;
layout(builtin=9999) int gl_SampleMaskIn[];
layout(builtin=9999) out int gl_SampleMask[];
layout(location=0,index=0,builtin=10001) out vec4 sk_FragColor;
)

View File

@ -263,8 +263,8 @@ DEF_TEST(SkSLBadIndex, r) {
"void main() { int x = 2[0]; }",
"error: 1: expected array, but found 'int'\n1 error\n");
test_failure(r,
"void main() { vec2 x = vec2(0); int y = x[0]; }",
"error: 1: expected array, but found 'vec2'\n1 error\n");
"void main() { vec2 x = vec2(0); int y = x[0][0]; }",
"error: 1: expected array, but found 'float'\n1 error\n");
}
DEF_TEST(SkSLTernaryMismatch, r) {

View File

@ -26,46 +26,52 @@ static void test(skiatest::Reporter* r, const char* src, SkSL::GLCaps caps, cons
}
}
static SkSL::GLCaps default_caps() {
return {
400,
SkSL::GLCaps::kGL_Standard,
false, // isCoreProfile
false, // usesPrecisionModifiers;
false, // mustDeclareFragmentShaderOutput
true // canUseMinAndAbsTogether
};
}
DEF_TEST(SkSLHelloWorld, r) {
SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
test(r,
"out vec4 fragColor; void main() { fragColor = vec4(0.75); }",
caps,
"void main() { sk_FragColor = vec4(0.75); }",
default_caps(),
"#version 400\n"
"out vec4 fragColor;\n"
"void main() {\n"
" fragColor = vec4(0.75);\n"
" gl_FragColor = vec4(0.75);\n"
"}\n");
}
DEF_TEST(SkSLControl, r) {
SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
test(r,
"out vec4 fragColor;"
"void main() {"
"if (1 + 2 + 3 > 5) { fragColor = vec4(0.75); } else { discard; }"
"if (1 + 2 + 3 > 5) { sk_FragColor = vec4(0.75); } else { discard; }"
"int i = 0;"
"while (i < 10) fragColor *= 0.5;"
"do { fragColor += 0.01; } while (fragColor.x < 0.7);"
"while (i < 10) sk_FragColor *= 0.5;"
"do { sk_FragColor += 0.01; } while (sk_FragColor.x < 0.7);"
"for (int i = 0; i < 10; i++) {"
"if (i % 0 == 1) break; else continue;"
"}"
"return;"
"}",
caps,
default_caps(),
"#version 400\n"
"out vec4 fragColor;\n"
"void main() {\n"
" if ((1 + 2) + 3 > 5) {\n"
" fragColor = vec4(0.75);\n"
" gl_FragColor = vec4(0.75);\n"
" } else {\n"
" discard;\n"
" }\n"
" int i = 0;\n"
" while (i < 10) fragColor *= 0.5;\n"
" while (i < 10) gl_FragColor *= 0.5;\n"
" do {\n"
" fragColor += 0.01;\n"
" } while (fragColor.x < 0.7);\n"
" gl_FragColor += 0.01;\n"
" } while (gl_FragColor.x < 0.7);\n"
" for (int i = 0;i < 10; i++) {\n"
" if (i % 0 == 1) break; else continue;\n"
" }\n"
@ -74,34 +80,30 @@ DEF_TEST(SkSLControl, r) {
}
DEF_TEST(SkSLFunctions, r) {
SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
test(r,
"out vec4 fragColor;"
"float foo(float v[2]) { return v[0] * v[1]; }"
"void bar(inout float x) { float y[2], z; y[0] = x; y[1] = x * 2; z = foo(y); x = z; }"
"void main() { float x = 10; bar(x); fragColor = vec4(x); }",
caps,
"void main() { float x = 10; bar(x); sk_FragColor = vec4(x); }",
default_caps(),
"#version 400\n"
"out vec4 fragColor;\n"
"float foo(in float[2] v) {\n"
"float foo(in float v[2]) {\n"
" return v[0] * v[1];\n"
"}\n"
"void bar(inout float x) {\n"
" float y[2], z;\n"
" y[0] = x;\n"
" y[1] = x * 2;\n"
" y[1] = x * 2.0;\n"
" z = foo(y);\n"
" x = z;\n"
"}\n"
"void main() {\n"
" float x = 10;\n"
" float x = 10.0;\n"
" bar(x);\n"
" fragColor = vec4(x);\n"
" gl_FragColor = vec4(x);\n"
"}\n");
}
DEF_TEST(SkSLOperators, r) {
SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
test(r,
"void main() {"
"float x = 1, y = 2;"
@ -123,17 +125,17 @@ DEF_TEST(SkSLOperators, r) {
"z <<= 4;"
"z %= 5;"
"}",
caps,
default_caps(),
"#version 400\n"
"void main() {\n"
" float x = 1, y = 2;\n"
" float x = 1.0, y = 2.0;\n"
" int z = 3;\n"
" x = x + ((y * float(z)) * x) * (y - float(z));\n"
" y = (x / y) / float(z);\n"
" z = (((z / 2) % 3 << 4) >> 2) << 1;\n"
" bool b = x > 4 == x < 2 || (2 >= 5 && y <= float(z)) && 12 != 11;\n"
" x += 12;\n"
" x -= 12;\n"
" bool b = x > 4.0 == x < 2.0 || (2 >= 5 && y <= float(z)) && 12 != 11;\n"
" x += 12.0;\n"
" x -= 12.0;\n"
" x *= (y /= float(z = 10));\n"
" b ||= false;\n"
" b &&= true;\n"
@ -148,7 +150,6 @@ DEF_TEST(SkSLOperators, r) {
}
DEF_TEST(SkSLMatrices, r) {
SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
test(r,
"void main() {"
"mat2x4 x = mat2x4(1);"
@ -157,19 +158,18 @@ DEF_TEST(SkSLMatrices, r) {
"vec3 v1 = mat3(1) * vec3(1);"
"vec3 v2 = vec3(1) * mat3(1);"
"}",
caps,
default_caps(),
"#version 400\n"
"void main() {\n"
" mat2x4 x = mat2x4(1);\n"
" mat3x2 y = mat3x2(1, 0, 0, 1, vec2(2, 2));\n"
" mat2x4 x = mat2x4(1.0);\n"
" mat3x2 y = mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n"
" mat3x4 z = x * y;\n"
" vec3 v1 = mat3(1) * vec3(1);\n"
" vec3 v2 = vec3(1) * mat3(1);\n"
" vec3 v1 = mat3(1.0) * vec3(1.0);\n"
" vec3 v2 = vec3(1.0) * mat3(1.0);\n"
"}\n");
}
DEF_TEST(SkSLInterfaceBlock, r) {
SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
test(r,
"uniform testBlock {"
"float x;"
@ -179,12 +179,12 @@ DEF_TEST(SkSLInterfaceBlock, r) {
"};"
"void main() {"
"}",
caps,
default_caps(),
"#version 400\n"
"uniform testBlock {\n"
" float x;\n"
" float[2] y;\n"
" layout (binding = 12)mat3x2 z;\n"
" layout (binding = 12) mat3x2 z;\n"
" bool w;\n"
"};\n"
"void main() {\n"
@ -192,7 +192,6 @@ DEF_TEST(SkSLInterfaceBlock, r) {
}
DEF_TEST(SkSLStructs, r) {
SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
test(r,
"struct A {"
"int x;"
@ -207,7 +206,7 @@ DEF_TEST(SkSLStructs, r) {
"B b1, b2, b3;"
"void main() {"
"}",
caps,
default_caps(),
"#version 400\n"
"struct A {\n"
" int x;\n"
@ -218,10 +217,149 @@ DEF_TEST(SkSLStructs, r) {
"struct B {\n"
" float x;\n"
" float[2] y;\n"
" layout (binding = 1)A z;\n"
" layout (binding = 1) A z;\n"
"}\n"
" b1, b2, b3;\n"
"void main() {\n"
"}\n");
}
DEF_TEST(SkSLVersion, r) {
SkSL::GLCaps caps = default_caps();
caps.fVersion = 450;
caps.fIsCoreProfile = true;
test(r,
"in float test; void main() { sk_FragColor = vec4(0.75); }",
caps,
"#version 450 core\n"
"in float test;\n"
"void main() {\n"
" gl_FragColor = vec4(0.75);\n"
"}\n");
caps.fVersion = 110;
caps.fIsCoreProfile = false;
test(r,
"in float test; void main() { sk_FragColor = vec4(0.75); }",
caps,
"#version 110\n"
"varying float test;\n"
"void main() {\n"
" gl_FragColor = vec4(0.75);\n"
"}\n");
}
DEF_TEST(SkSLDeclareOutput, r) {
SkSL::GLCaps caps = default_caps();
caps.fMustDeclareFragmentShaderOutput = true;
test(r,
"void main() { sk_FragColor = vec4(0.75); }",
caps,
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" sk_FragColor = vec4(0.75);\n"
"}\n");
}
DEF_TEST(SkSLUsesPrecisionModifiers, r) {
SkSL::GLCaps caps = default_caps();
test(r,
"void main() { float x = 0.75; highp float y = 1; }",
caps,
"#version 400\n"
"void main() {\n"
" float x = 0.75;\n"
" float y = 1.0;\n"
"}\n");
caps.fStandard = SkSL::GLCaps::kGLES_Standard;
caps.fUsesPrecisionModifiers = true;
test(r,
"void main() { float x = 0.75; highp float y = 1; }",
caps,
"#version 400 es\n"
"precision mediump float;\n"
"void main() {\n"
" float x = 0.75;\n"
" highp float y = 1.0;\n"
"}\n");
}
DEF_TEST(SkSLMinAbs, r) {
test(r,
"void main() {"
"float x = -5;"
"x = min(abs(x), 6);"
"}",
default_caps(),
"#version 400\n"
"void main() {\n"
" float x = -5.0;\n"
" x = min(abs(x), 6.0);\n"
"}\n");
SkSL::GLCaps caps = default_caps();
caps.fCanUseMinAndAbsTogether = false;
test(r,
"void main() {"
"float x = -5.0;"
"x = min(abs(x), 6.0);"
"}",
caps,
"#version 400\n"
"void main() {\n"
" float minAbsHackVar0;\n"
" float x = -5.0;\n"
" x = (abs(x) > (minAbsHackVar0 = 6.0) ? minAbsHackVar0 : abs(x));\n"
"}\n");
}
DEF_TEST(SkSLModifiersDeclaration, r) {
test(r,
"layout(blend_support_all_equations) out;"
"void main() { }",
default_caps(),
"#version 400\n"
"layout (blend_support_all_equations) out ;\n"
"void main() {\n"
"}\n");
}
DEF_TEST(SkSLHex, r) {
test(r,
"void main() {"
"int i1 = 0x0;"
"int i2 = 0x1234abcd;"
"int i3 = 0x7fffffff;"
"int i4 = 0xffffffff;"
"int i5 = -0xbeef;"
"uint u1 = 0x0;"
"uint u2 = 0x1234abcd;"
"uint u3 = 0x7fffffff;"
"uint u4 = 0xffffffff;"
"}",
default_caps(),
"#version 400\n"
"void main() {\n"
" int i1 = 0;\n"
" int i2 = 305441741;\n"
" int i3 = 2147483647;\n"
" int i4 = -1;\n"
" int i5 = -48879;\n"
" uint u1 = 0u;\n"
" uint u2 = 305441741u;\n"
" uint u3 = 2147483647u;\n"
" uint u4 = 4294967295u;\n"
"}\n");
}
DEF_TEST(SkSLArrayConstructors, r) {
test(r,
"float test1[] = float[](1, 2, 3, 4);"
"vec2 test2[] = vec2[](vec2(1, 2), vec2(3, 4));"
"mat4 test3[] = mat4[]();",
default_caps(),
"#version 400\n"
"float test1[] = float[](1.0, 2.0, 3.0, 4.0);\n"
"vec2 test2[] = vec2[](vec2(1.0, 2.0), vec2(3.0, 4.0));\n"
"mat4 test3[] = mat4[]();\n");
}