2016-10-12 13:39:56 +00:00
|
|
|
Overview
|
|
|
|
========
|
|
|
|
|
2017-03-31 17:56:23 +00:00
|
|
|
SkSL ("Skia Shading Language") is a variant of GLSL which is used as Skia's
|
2016-10-12 13:39:56 +00:00
|
|
|
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.
|
|
|
|
|
2017-06-29 14:03:38 +00:00
|
|
|
|
2016-10-12 13:39:56 +00:00
|
|
|
Differences from GLSL
|
|
|
|
=====================
|
|
|
|
|
2017-09-20 15:24:15 +00:00
|
|
|
* Precision modifiers are not used. 'float', 'int', and 'uint' are always high
|
|
|
|
precision. New types 'half', 'short', and 'ushort' are medium precision (we
|
|
|
|
do not use low precision).
|
2017-07-28 19:19:46 +00:00
|
|
|
* Vector types are named <base type><columns>, so float2 instead of vec2 and
|
|
|
|
bool4 instead of bvec4
|
|
|
|
* Matrix types are named <base type><columns>x<rows>, so float2x3 instead of
|
|
|
|
mat2x3 and double4x4 instead of dmat4
|
2017-05-10 19:06:17 +00:00
|
|
|
* "@if" and "@switch" are static versions of if and switch. They behave exactly
|
|
|
|
the same as if and switch in all respects other than it being a compile-time
|
|
|
|
error to use a non-constant expression as a test.
|
2016-11-21 20:59:48 +00:00
|
|
|
* GLSL caps can be referenced via the syntax 'sk_Caps.<name>', e.g.
|
2019-10-29 22:28:01 +00:00
|
|
|
sk_Caps.canUseAnyFunctionInShader. The value will be a constant boolean or int,
|
2016-11-21 20:59:48 +00:00
|
|
|
as appropriate. As SkSL supports constant folding and branch elimination, this
|
|
|
|
means that an 'if' statement which statically queries a cap will collapse down
|
|
|
|
to the chosen branch, meaning that:
|
|
|
|
|
|
|
|
if (sk_Caps.externalTextureSupport)
|
|
|
|
do_something();
|
|
|
|
else
|
|
|
|
do_something_else();
|
|
|
|
|
|
|
|
will compile as if you had written either 'do_something();' or
|
|
|
|
'do_something_else();', depending on whether that cap is enabled or not.
|
2017-07-28 19:19:46 +00:00
|
|
|
* no #version statement is required, and it will be ignored if present
|
2016-10-12 13:39:56 +00:00
|
|
|
* the output color is sk_FragColor (do not declare it)
|
2018-01-24 14:52:02 +00:00
|
|
|
* use sk_Position instead of gl_Position. sk_Position is in device coordinates
|
|
|
|
rather than normalized coordinates.
|
2017-09-26 18:23:59 +00:00
|
|
|
* use sk_PointSize instead of gl_PointSize
|
2017-02-07 19:53:32 +00:00
|
|
|
* use sk_VertexID instead of gl_VertexID
|
2017-10-15 04:12:33 +00:00
|
|
|
* use sk_InstanceID instead of gl_InstanceID
|
2017-02-09 22:01:22 +00:00
|
|
|
* the fragment coordinate is sk_FragCoord, and is always relative to the upper
|
|
|
|
left.
|
2018-07-27 18:38:35 +00:00
|
|
|
* use sk_Clockwise instead of gl_FrontFacing. This is always relative to an
|
|
|
|
upper left origin.
|
2016-10-12 13:39:56 +00:00
|
|
|
* you do not need to include ".0" to make a number a float (meaning that
|
2018-02-01 15:02:37 +00:00
|
|
|
"float2(x, y) * 4" is perfectly legal in SkSL, unlike GLSL where it would
|
|
|
|
often have to be expressed "float2(x, y) * 4.0". There is no performance
|
|
|
|
penalty for this, as the number is converted to a float at compile time)
|
2016-10-12 13:39:56 +00:00
|
|
|
* type suffixes on numbers (1.0f, 0xFFu) are both unnecessary and unsupported
|
2018-02-01 15:02:37 +00:00
|
|
|
* creating a smaller vector from a larger vector (e.g. float2(float3(1))) is
|
2017-02-09 18:57:14 +00:00
|
|
|
intentionally disallowed, as it is just a wordier way of performing a swizzle.
|
|
|
|
Use swizzles instead.
|
2019-09-13 16:52:55 +00:00
|
|
|
* Swizzle components, in addition to the normal rgba / xyzw components, can also
|
|
|
|
be LTRB (meaning "left/top/right/bottom", for when we store rectangles in
|
|
|
|
vectors), and may also be the constants '0' or '1' to produce a constant 0 or
|
|
|
|
1 in that channel instead of selecting anything from the source vector.
|
|
|
|
foo.rgb1 is equivalent to float4(foo.rgb, 1).
|
2019-07-29 17:05:15 +00:00
|
|
|
* All texture functions are named "sample", e.g. sample(sampler2D, float3) is
|
|
|
|
equivalent to GLSL's textureProj(sampler2D, float3).
|
2018-08-24 20:43:57 +00:00
|
|
|
* Render target width and height are available via sk_Width and sk_Height
|
2020-08-20 13:09:14 +00:00
|
|
|
* Functions support the 'inline' modifier, which causes the compiler to ignore
|
|
|
|
its normal inlining heuristics and inline the function if at all possible
|
2016-10-12 13:39:56 +00:00
|
|
|
* 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.
|
2017-06-29 14:03:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
SkSL Fragment Processors
|
|
|
|
========================
|
|
|
|
|
2018-02-01 15:02:37 +00:00
|
|
|
********************************************************************************
|
|
|
|
*** IMPORTANT: You must set gn arg "skia_compile_processors = true" to cause ***
|
|
|
|
*** .fp files to be recompiled! In order for compilation to succeed, you ***
|
|
|
|
*** must run bin/fetch-clang-format (once) to install our blessed version. ***
|
|
|
|
********************************************************************************
|
|
|
|
|
2017-06-29 14:03:38 +00:00
|
|
|
An extension of SkSL allows for the creation of fragment processors in pure
|
|
|
|
SkSL. The program defines its inputs similarly to a normal SkSL program (with
|
|
|
|
'in' and 'uniform' variables), but the 'main()' function represents only this
|
2020-06-29 21:20:13 +00:00
|
|
|
fragment processor's portion of the overall fragment shader. The 'main' function
|
|
|
|
can optionally have a 'float2' parameter that holds the sample coordinates the
|
|
|
|
processor is evaluated at.
|
|
|
|
|
|
|
|
Processors do not need to declare this parameter if they do not rely on local
|
|
|
|
coordinates or only invoke child processors using the no-arg and matrix-based
|
|
|
|
sample() functions.
|
2017-06-29 14:03:38 +00:00
|
|
|
|
|
|
|
Within an '.fp' fragment processor file:
|
|
|
|
|
|
|
|
* C++ code can be embedded in sections of the form:
|
|
|
|
|
|
|
|
@section_name { <arbitrary C++ code> }
|
|
|
|
|
|
|
|
Supported section are:
|
|
|
|
@header (in the .h file, outside the class declaration)
|
2017-07-05 20:19:09 +00:00
|
|
|
@headerEnd (at the end of the .h file)
|
2017-06-29 14:03:38 +00:00
|
|
|
@class (in the .h file, inside the class declaration)
|
|
|
|
@cpp (in the .cpp file)
|
2017-07-05 20:19:09 +00:00
|
|
|
@cppEnd (at the end of the .cpp file)
|
2017-06-29 14:03:38 +00:00
|
|
|
@constructorParams (extra parameters to the constructor, comma-separated)
|
|
|
|
@constructor (replaces the default constructor)
|
|
|
|
@initializers (constructor initializer list, comma-separated)
|
|
|
|
@emitCode (extra code for the emitCode function)
|
|
|
|
@fields (extra private fields, each terminated with a semicolon)
|
|
|
|
@make (replaces the default Make function)
|
2017-07-31 15:18:22 +00:00
|
|
|
@clone (replaces the default clone() function)
|
2020-08-12 13:56:50 +00:00
|
|
|
@dumpInfo (replaces the default dumpInfo() function)
|
2017-06-29 14:03:38 +00:00
|
|
|
@setData(<pdman>) (extra code for the setData function, where <pdman> is
|
|
|
|
the name of the GrGLSLProgramDataManager)
|
|
|
|
@test(<testData>) (the body of the TestCreate function, where <testData> is
|
|
|
|
the name of the GrProcessorTestData* parameter)
|
2017-07-13 13:36:52 +00:00
|
|
|
@samplerParams(<sampler>)
|
|
|
|
(the sampler params to attach to the named sampler2D)
|
2017-06-29 14:03:38 +00:00
|
|
|
* global 'in' variables represent data passed to the fragment processor at
|
|
|
|
construction time. These variables become constructor parameters and are
|
2018-02-01 15:02:37 +00:00
|
|
|
stored in fragment processor fields. By default float2/half2 maps to SkPoints,
|
2018-08-31 14:52:47 +00:00
|
|
|
and float4/half4 maps to SkRects (in x, y, width, height) order. Similarly,
|
|
|
|
int2/short2 maps to SkIPoint and int4/half4 maps to SkIRect. Use ctype
|
2018-02-01 15:02:37 +00:00
|
|
|
(below) to override this default mapping.
|
|
|
|
* global variables support an additional 'ctype' layout key, providing the type
|
|
|
|
they should be represented as from within the C++ code. For instance, you can
|
2018-10-16 19:45:55 +00:00
|
|
|
use 'layout(ctype=SkPMColor4f) in half4 color;' to create a variable that looks
|
|
|
|
like a half4 on the SkSL side of things, and a SkPMColor4f on the C++ side of
|
2018-02-01 15:02:37 +00:00
|
|
|
things.
|
2017-06-29 14:03:38 +00:00
|
|
|
* 'uniform' variables become, as one would expect, top-level uniforms. By
|
|
|
|
default they do not have any data provided to them; you will need to provide
|
|
|
|
them with data via the @setData section.
|
|
|
|
* 'in uniform' variables are uniforms that are automatically wired up to
|
2018-02-01 15:02:37 +00:00
|
|
|
fragment processor constructor parameters. The fragment processor will accept
|
|
|
|
a parameter representing the uniform's value, and automatically plumb it
|
|
|
|
through to the uniform's value in its generated setData() function.
|
2018-08-31 14:52:47 +00:00
|
|
|
* 'in uniform' variables support a 'tracked' flag in the layout that will
|
|
|
|
have the generated code automatically implement state tracking on the uniform
|
|
|
|
value to minimize GPU calls.
|
2017-06-29 14:03:38 +00:00
|
|
|
* Uniform variables support an additional 'when' layout key.
|
|
|
|
'layout(when=foo) uniform int x;' means that this uniform will only be
|
|
|
|
emitted when the 'foo' expression is true.
|
|
|
|
* 'in' variables support an additional 'key' layout key.
|
2019-09-23 17:53:31 +00:00
|
|
|
'layout(key) in uniform int x;' means that this uniform should be included in
|
2017-06-29 14:03:38 +00:00
|
|
|
the program's key. Matrix variables additionally support 'key=identity',
|
|
|
|
which causes the key to consider only whether or not the matrix is an
|
|
|
|
identity matrix.
|
2018-08-30 20:08:18 +00:00
|
|
|
* child processors can be declared with 'in fragmentProcessor <name>;', and can
|
2019-07-29 17:05:15 +00:00
|
|
|
be invoked by calling 'sample(<name>)' or 'sample(<name>, <inputColor>)'.
|
2018-08-30 20:08:18 +00:00
|
|
|
The first variant emits the child with a solid white input color. The second
|
|
|
|
variant emits the child with the result of the 2nd argument's expression,
|
|
|
|
which must evaluate to a half4. The process function returns a half4.
|
2020-06-08 15:44:25 +00:00
|
|
|
* The 'fragmentProcessor' type cannot hold a null. Nullable fragment processors
|
|
|
|
should use the 'fragmentProcessor?' type: 'in fragmentProcessor? <name>'. You
|
|
|
|
can check for null fragment processors by comparing them against 'null', as
|
|
|
|
in: 'if (inputFP != null) { ... }'. Invoking 'sample()' on a null fragment
|
|
|
|
processor will return the inputColor unchanged (this defaults to solid white
|
|
|
|
if not explicitly specified).
|
2018-02-01 15:02:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
Creating a new .fp file
|
|
|
|
=======================
|
|
|
|
|
|
|
|
1. Ensure that you have set gn arg "skia_compile_processors = true"
|
|
|
|
2. Create your new .fp file, generally under src/gpu/effects.
|
|
|
|
3. Add the .fp file to sksl.gni.
|
|
|
|
4. Build Skia. This will cause the .fp file to be compiled, resulting in a new
|
|
|
|
.cpp and .h file for the fragment processor.
|
|
|
|
5. Add the .cpp and .h files to gpu.gni.
|
|
|
|
6. Add the new processor's ClassID (k<ProcessorName>_ClassID) to
|
|
|
|
GrProcessor::ClassID.
|
|
|
|
7. At this point you can reference the new fragment processor from within Skia.
|
|
|
|
|
|
|
|
Once you have done this initial setup, simply re-build Skia to pick up any
|
2018-07-27 18:38:35 +00:00
|
|
|
changes to the .fp file.
|