ethannicholas | 5961bc9 | 2016-10-12 06:39:56 -0700 | [diff] [blame] | 1 | Overview |
| 2 | ======== |
| 3 | |
Ethan Nicholas | 0df1b04 | 2017-03-31 13:56:23 -0400 | [diff] [blame] | 4 | SkSL ("Skia Shading Language") is a variant of GLSL which is used as Skia's |
ethannicholas | 5961bc9 | 2016-10-12 06:39:56 -0700 | [diff] [blame] | 5 | internal shading language. SkSL is, at its heart, a single standardized version |
| 6 | of GLSL which avoids all of the various version and dialect differences found |
| 7 | in GLSL "in the wild", but it does bring a few of its own changes to the table. |
| 8 | |
| 9 | Skia uses the SkSL compiler to convert SkSL code to GLSL, GLSL ES, or SPIR-V |
| 10 | before handing it over to the graphics driver. |
| 11 | |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 12 | |
ethannicholas | 5961bc9 | 2016-10-12 06:39:56 -0700 | [diff] [blame] | 13 | Differences from GLSL |
| 14 | ===================== |
| 15 | |
Ethan Nicholas | 8aa4569 | 2017-09-20 11:24:15 -0400 | [diff] [blame] | 16 | * Precision modifiers are not used. 'float', 'int', and 'uint' are always high |
| 17 | precision. New types 'half', 'short', and 'ushort' are medium precision (we |
| 18 | do not use low precision). |
Ethan Nicholas | 5af9ea3 | 2017-07-28 15:19:46 -0400 | [diff] [blame] | 19 | * Vector types are named <base type><columns>, so float2 instead of vec2 and |
| 20 | bool4 instead of bvec4 |
| 21 | * Matrix types are named <base type><columns>x<rows>, so float2x3 instead of |
| 22 | mat2x3 and double4x4 instead of dmat4 |
Ethan Nicholas | 5ac13c2 | 2017-05-10 15:06:17 -0400 | [diff] [blame] | 23 | * "@if" and "@switch" are static versions of if and switch. They behave exactly |
| 24 | the same as if and switch in all respects other than it being a compile-time |
| 25 | error to use a non-constant expression as a test. |
Ethan Nicholas | 3605ace | 2016-11-21 15:59:48 -0500 | [diff] [blame] | 26 | * GLSL caps can be referenced via the syntax 'sk_Caps.<name>', e.g. |
| 27 | sk_Caps.sampleVariablesSupport. The value will be a constant boolean or int, |
| 28 | as appropriate. As SkSL supports constant folding and branch elimination, this |
| 29 | means that an 'if' statement which statically queries a cap will collapse down |
| 30 | to the chosen branch, meaning that: |
| 31 | |
| 32 | if (sk_Caps.externalTextureSupport) |
| 33 | do_something(); |
| 34 | else |
| 35 | do_something_else(); |
| 36 | |
| 37 | will compile as if you had written either 'do_something();' or |
| 38 | 'do_something_else();', depending on whether that cap is enabled or not. |
Ethan Nicholas | 5af9ea3 | 2017-07-28 15:19:46 -0400 | [diff] [blame] | 39 | * no #version statement is required, and it will be ignored if present |
ethannicholas | 5961bc9 | 2016-10-12 06:39:56 -0700 | [diff] [blame] | 40 | * the output color is sk_FragColor (do not declare it) |
Robert Phillips | fe8da17 | 2018-01-24 14:52:02 +0000 | [diff] [blame] | 41 | * use sk_Position instead of gl_Position. sk_Position is in device coordinates |
| 42 | rather than normalized coordinates. |
Ethan Nicholas | bed683a | 2017-09-26 14:23:59 -0400 | [diff] [blame] | 43 | * use sk_PointSize instead of gl_PointSize |
Ethan Nicholas | a51740c | 2017-02-07 14:53:32 -0500 | [diff] [blame] | 44 | * use sk_VertexID instead of gl_VertexID |
Chris Dalton | 8580d51 | 2017-10-14 22:12:33 -0600 | [diff] [blame] | 45 | * use sk_InstanceID instead of gl_InstanceID |
Ethan Nicholas | 3865711 | 2017-02-09 17:01:22 -0500 | [diff] [blame] | 46 | * the fragment coordinate is sk_FragCoord, and is always relative to the upper |
| 47 | left. |
Chris Dalton | 49d14e9 | 2018-07-27 12:38:35 -0600 | [diff] [blame] | 48 | * use sk_Clockwise instead of gl_FrontFacing. This is always relative to an |
| 49 | upper left origin. |
ethannicholas | 5961bc9 | 2016-10-12 06:39:56 -0700 | [diff] [blame] | 50 | * you do not need to include ".0" to make a number a float (meaning that |
Ethan Nicholas | 40d4420 | 2018-02-01 10:02:37 -0500 | [diff] [blame] | 51 | "float2(x, y) * 4" is perfectly legal in SkSL, unlike GLSL where it would |
| 52 | often have to be expressed "float2(x, y) * 4.0". There is no performance |
| 53 | penalty for this, as the number is converted to a float at compile time) |
ethannicholas | 5961bc9 | 2016-10-12 06:39:56 -0700 | [diff] [blame] | 54 | * type suffixes on numbers (1.0f, 0xFFu) are both unnecessary and unsupported |
Ethan Nicholas | 40d4420 | 2018-02-01 10:02:37 -0500 | [diff] [blame] | 55 | * creating a smaller vector from a larger vector (e.g. float2(float3(1))) is |
Ethan Nicholas | 84645e3 | 2017-02-09 13:57:14 -0500 | [diff] [blame] | 56 | intentionally disallowed, as it is just a wordier way of performing a swizzle. |
| 57 | Use swizzles instead. |
Ethan Nicholas | e455f65 | 2019-09-13 12:52:55 -0400 | [diff] [blame] | 58 | * Swizzle components, in addition to the normal rgba / xyzw components, can also |
| 59 | be LTRB (meaning "left/top/right/bottom", for when we store rectangles in |
| 60 | vectors), and may also be the constants '0' or '1' to produce a constant 0 or |
| 61 | 1 in that channel instead of selecting anything from the source vector. |
| 62 | foo.rgb1 is equivalent to float4(foo.rgb, 1). |
Ethan Nicholas | 1386366 | 2019-07-29 13:05:15 -0400 | [diff] [blame] | 63 | * All texture functions are named "sample", e.g. sample(sampler2D, float3) is |
| 64 | equivalent to GLSL's textureProj(sampler2D, float3). |
Ethan Nicholas | cd700e9 | 2018-08-24 16:43:57 -0400 | [diff] [blame] | 65 | * Render target width and height are available via sk_Width and sk_Height |
ethannicholas | 5961bc9 | 2016-10-12 06:39:56 -0700 | [diff] [blame] | 66 | * some built-in functions and one or two rarely-used language features are not |
| 67 | yet supported (sorry!) |
| 68 | |
| 69 | SkSL is still under development, and is expected to diverge further from GLSL |
| 70 | over time. |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 71 | |
| 72 | |
| 73 | SkSL Fragment Processors |
| 74 | ======================== |
| 75 | |
Ethan Nicholas | 40d4420 | 2018-02-01 10:02:37 -0500 | [diff] [blame] | 76 | ******************************************************************************** |
| 77 | *** IMPORTANT: You must set gn arg "skia_compile_processors = true" to cause *** |
| 78 | *** .fp files to be recompiled! In order for compilation to succeed, you *** |
| 79 | *** must run bin/fetch-clang-format (once) to install our blessed version. *** |
| 80 | ******************************************************************************** |
| 81 | |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 82 | An extension of SkSL allows for the creation of fragment processors in pure |
| 83 | SkSL. The program defines its inputs similarly to a normal SkSL program (with |
| 84 | 'in' and 'uniform' variables), but the 'main()' function represents only this |
| 85 | fragment processor's portion of the overall fragment shader. |
| 86 | |
| 87 | Within an '.fp' fragment processor file: |
| 88 | |
| 89 | * C++ code can be embedded in sections of the form: |
| 90 | |
| 91 | @section_name { <arbitrary C++ code> } |
| 92 | |
| 93 | Supported section are: |
| 94 | @header (in the .h file, outside the class declaration) |
Ethan Nicholas | 9fb036f | 2017-07-05 16:19:09 -0400 | [diff] [blame] | 95 | @headerEnd (at the end of the .h file) |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 96 | @class (in the .h file, inside the class declaration) |
| 97 | @cpp (in the .cpp file) |
Ethan Nicholas | 9fb036f | 2017-07-05 16:19:09 -0400 | [diff] [blame] | 98 | @cppEnd (at the end of the .cpp file) |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 99 | @constructorParams (extra parameters to the constructor, comma-separated) |
| 100 | @constructor (replaces the default constructor) |
| 101 | @initializers (constructor initializer list, comma-separated) |
| 102 | @emitCode (extra code for the emitCode function) |
| 103 | @fields (extra private fields, each terminated with a semicolon) |
| 104 | @make (replaces the default Make function) |
Ethan Nicholas | f57c0d6 | 2017-07-31 11:18:22 -0400 | [diff] [blame] | 105 | @clone (replaces the default clone() function) |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 106 | @setData(<pdman>) (extra code for the setData function, where <pdman> is |
| 107 | the name of the GrGLSLProgramDataManager) |
| 108 | @test(<testData>) (the body of the TestCreate function, where <testData> is |
| 109 | the name of the GrProcessorTestData* parameter) |
Ethan Nicholas | 68990be | 2017-07-13 09:36:52 -0400 | [diff] [blame] | 110 | @coordTransform(<sampler>) |
| 111 | (the matrix to attach to the named sampler2D's |
| 112 | GrCoordTransform) |
| 113 | @samplerParams(<sampler>) |
| 114 | (the sampler params to attach to the named sampler2D) |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 115 | * global 'in' variables represent data passed to the fragment processor at |
| 116 | construction time. These variables become constructor parameters and are |
Ethan Nicholas | 40d4420 | 2018-02-01 10:02:37 -0500 | [diff] [blame] | 117 | stored in fragment processor fields. By default float2/half2 maps to SkPoints, |
Michael Ludwig | a427559 | 2018-08-31 10:52:47 -0400 | [diff] [blame] | 118 | and float4/half4 maps to SkRects (in x, y, width, height) order. Similarly, |
| 119 | int2/short2 maps to SkIPoint and int4/half4 maps to SkIRect. Use ctype |
Ethan Nicholas | 40d4420 | 2018-02-01 10:02:37 -0500 | [diff] [blame] | 120 | (below) to override this default mapping. |
| 121 | * global variables support an additional 'ctype' layout key, providing the type |
| 122 | they should be represented as from within the C++ code. For instance, you can |
Brian Osman | 495993a | 2018-10-16 15:45:55 -0400 | [diff] [blame] | 123 | use 'layout(ctype=SkPMColor4f) in half4 color;' to create a variable that looks |
| 124 | like a half4 on the SkSL side of things, and a SkPMColor4f on the C++ side of |
Ethan Nicholas | 40d4420 | 2018-02-01 10:02:37 -0500 | [diff] [blame] | 125 | things. |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 126 | * 'uniform' variables become, as one would expect, top-level uniforms. By |
| 127 | default they do not have any data provided to them; you will need to provide |
| 128 | them with data via the @setData section. |
| 129 | * 'in uniform' variables are uniforms that are automatically wired up to |
Ethan Nicholas | 40d4420 | 2018-02-01 10:02:37 -0500 | [diff] [blame] | 130 | fragment processor constructor parameters. The fragment processor will accept |
| 131 | a parameter representing the uniform's value, and automatically plumb it |
| 132 | through to the uniform's value in its generated setData() function. |
Michael Ludwig | a427559 | 2018-08-31 10:52:47 -0400 | [diff] [blame] | 133 | * 'in uniform' variables support a 'tracked' flag in the layout that will |
| 134 | have the generated code automatically implement state tracking on the uniform |
| 135 | value to minimize GPU calls. |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 136 | * the 'sk_TransformedCoords2D' array provides access to 2D transformed |
| 137 | coordinates. sk_TransformedCoords2D[0] is equivalent to calling |
| 138 | fragBuilder->ensureCoords2D(args.fTransformedCoords[0]) (and the result is |
| 139 | cached, so you need not worry about using the value repeatedly). |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 140 | * Uniform variables support an additional 'when' layout key. |
| 141 | 'layout(when=foo) uniform int x;' means that this uniform will only be |
| 142 | emitted when the 'foo' expression is true. |
| 143 | * 'in' variables support an additional 'key' layout key. |
Brian Osman | 801dd1b | 2019-09-23 13:53:31 -0400 | [diff] [blame] | 144 | 'layout(key) in uniform int x;' means that this uniform should be included in |
Ethan Nicholas | 762466e | 2017-06-29 10:03:38 -0400 | [diff] [blame] | 145 | the program's key. Matrix variables additionally support 'key=identity', |
| 146 | which causes the key to consider only whether or not the matrix is an |
| 147 | identity matrix. |
Michael Ludwig | 92e4c7f | 2018-08-30 16:08:18 -0400 | [diff] [blame] | 148 | * child processors can be declared with 'in fragmentProcessor <name>;', and can |
Ethan Nicholas | 1386366 | 2019-07-29 13:05:15 -0400 | [diff] [blame] | 149 | be invoked by calling 'sample(<name>)' or 'sample(<name>, <inputColor>)'. |
Michael Ludwig | 92e4c7f | 2018-08-30 16:08:18 -0400 | [diff] [blame] | 150 | The first variant emits the child with a solid white input color. The second |
| 151 | variant emits the child with the result of the 2nd argument's expression, |
| 152 | which must evaluate to a half4. The process function returns a half4. |
Ethan Nicholas | ee1c8a7 | 2019-02-22 10:50:47 -0500 | [diff] [blame] | 153 | * By default, fragment processors must be non-null. The type for a nullable |
| 154 | fragment processor is 'fragmentProcessor?', as in |
| 155 | 'in fragmentProcessor? <name>'. You can check for the presence of such a |
| 156 | fragment processor by comparing it to 'null'. |
Ethan Nicholas | 40d4420 | 2018-02-01 10:02:37 -0500 | [diff] [blame] | 157 | |
| 158 | |
| 159 | Creating a new .fp file |
| 160 | ======================= |
| 161 | |
| 162 | 1. Ensure that you have set gn arg "skia_compile_processors = true" |
| 163 | 2. Create your new .fp file, generally under src/gpu/effects. |
| 164 | 3. Add the .fp file to sksl.gni. |
| 165 | 4. Build Skia. This will cause the .fp file to be compiled, resulting in a new |
| 166 | .cpp and .h file for the fragment processor. |
| 167 | 5. Add the .cpp and .h files to gpu.gni. |
| 168 | 6. Add the new processor's ClassID (k<ProcessorName>_ClassID) to |
| 169 | GrProcessor::ClassID. |
| 170 | 7. At this point you can reference the new fragment processor from within Skia. |
| 171 | |
| 172 | Once you have done this initial setup, simply re-build Skia to pick up any |
Chris Dalton | 49d14e9 | 2018-07-27 12:38:35 -0600 | [diff] [blame] | 173 | changes to the .fp file. |