• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1---
2title: 'SkSL & Runtime Effects'
3linkTitle: 'SkSL'
4---
5
6## Overview
7
8**SkSL** is Skia's
9[shading language](https://en.wikipedia.org/wiki/Shading_language).
10**`SkRuntimeEffect`** is a Skia C++ object that can be used to create
11`SkShader`, `SkColorFilter`, and `SkBlender` objects with behavior controlled by
12SkSL code.
13
14You can experiment with SkSL at https://shaders.skia.org/. The syntax is very
15similar to GLSL. When using SkSL effects in your Skia application, there are
16important differences (from GLSL) to remember. Most of these differences are
17because of one basic fact: **With GPU shading languages, you are programming a
18stage of the
19[GPU pipeline](https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview).
20With SkSL, you are programming a stage of the Skia pipeline.**
21
22In particular, a GLSL fragment shader controls the entire behavior of the GPU
23between the rasterizer and the blending hardware. That shader does all of the
24work to compute a color, and the color it generates is exactly what is fed to
25the fixed-function blending stage of the pipeline.
26
27SkSL effects exist as part of the larger Skia pipeline. When you issue a canvas
28drawing operation, Skia (generally) assembles a single GPU fragment shader to do
29all of the required work. This shader typically includes several pieces. For
30example, it might include:
31
32- Evaluating whether a pixel falls inside or outside of the shape being drawn
33  (or on the border, where it might apply antialiasing).
34- Evaluating whether a pixel falls inside or outside of the clipping region
35  (again, with possible antialiasing logic for border pixels).
36- Logic for the `SkShader` on the `SkPaint`. The `SkShader` can actually be a
37  tree of objects (due to `SkShaders::Blend` and other features described
38  below).
39- Similar logic for the `SkColorFilter` (which can also be a tree, due to
40  `SkColorFilters::Compose`, `SkColorFilters::Blend`, and features described
41  below).
42- Blending code (for certain `SkBlendMode`s, or for custom blending specified
43  with `SkPaint::setBlender`).
44
45Even if the `SkPaint` has a complex tree of objects in the `SkShader`,
46`SkColorFilter`, or `SkBlender` fields, there is still only a _single_ GPU
47fragment shader. Each node in that tree creates a single function. The clipping
48code and geometry code each create a function. The blending code might create a
49function. The overall fragment shader then calls all of these functions (which
50may call other functions, e.g. in the case of an `SkShader` tree).
51
52**Your SkSL effect contributes a function to the GPU's fragment shader.**
53
54---
55
56## Evaluating (sampling) other SkShaders
57
58In GLSL, a fragment shader can sample a texture. With runtime effects, the
59object that you bind (in C++) is an `SkShader`, represented by a `shader` in
60SkSL. To make it clear that you are operating on an object that will emit its
61own shader code, you don't use `sample`. Instead, the `shader` object has a
62`.eval()` method. Regardless, Skia has simple methods for creating an `SkShader`
63from an `SkImage`, so it's easy to use images in your runtime effects:
64
65<fiddle-embed name='3654053c76b5c23f18eb9a1c82abbde4'></fiddle-embed>
66
67Because the object you bind and evaluate is an `SkShader`, you can directly use
68any Skia shader, without necessarily turning it into an image (texture) first.
69For example, you can evaluate a linear gradient. In this example, there is no
70texture created to hold the gradient. Skia generates a single fragment shader
71that computes the gradient color, samples from the image's texture, and then
72multiplies the two together:
73
74<fiddle-embed name='f282a4411782ed92057350e339586502'></fiddle-embed>
75
76Of course, you can even invoke another runtime effect, allowing you to combine
77shader snippets dynamically:
78
79<fiddle-embed name='2151b061428f47844a2500b57c887ddf'></fiddle-embed>
80
81---
82
83## Premultiplied Alpha
84
85When dealing with transparent colors, there are two (common)
86[possible representations](https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied).
87Skia calls these _unpremultiplied_ (what Wikipedia calls _straight_), and
88_premultiplied_. In the Skia pipeline, every `SkShader` returns premultiplied
89colors.
90
91If you're familiar with OpenGL blending, you can think of it in terms of the
92blend equation. For common alpha blending (called
93[source-over](https://developer.android.com/reference/android/graphics/PorterDuff.Mode#SRC_OVER)),
94you would normally configure your blend function as
95`(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)`. Skia defines source-over blending as
96if the blend function were `(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)`.
97
98Skia's use of premultiplied alpha implies:
99
100- If you start with an unpremultiplied `SkImage` (like a PNG), turn that into an
101  `SkImageShader`, and evaluate that shader... the resulting colors will be
102  `[R*A, G*A, B*A, A]`, **not** `[R, G, B, A]`.
103- If your SkSL will return transparent colors, it must be sure to multiply the
104  `RGB` by `A`.
105- For more complex shaders, you must understand which of your colors are
106  premultiplied vs. unpremultiplied. Many operations don't make sense if you mix
107  both kinds of color together.
108
109The image below demonstrates this: properly premultiplied colors produce a smooth
110gradient as alpha decreases. Unpremultipled colors cause the gradient to display
111incorrectly, becoming too bright and shifting hue as the alpha changes.
112
113<fiddle-embed name='4aa28e27a9682fec18d8c0ca265151ad'></fiddle-embed>
114
115---
116
117## Coordinate Spaces
118
119To understand how coordinates work in SkSL, you first need to understand
120[how they work in Skia](/docs/user/coordinates). If you're comfortable with
121Skia's coordinate spaces, then just remember that the coordinates supplied to
122your `main()` are **local** coordinates. They will be relative to the coordinate
123space of the `SkShader`. This will match the local space of the canvas and any
124`localMatrix` transformations. Additionally, if the shader is invoked by
125another, that parent shader may modify them arbitrarily.
126
127In addition, the `SkShader` produced from an `SkImage` does not use normalized
128coordinates (like a texture in GLSL). It uses `(0, 0)` in the upper-left corner,
129and `(w, h)` in the bottom-right corner. Normally, this is exactly what you
130want. If you're evaluating an `SkImageShader` with coordinates based on the ones
131passed to you, the scale is correct. However, if you want to adjust those
132coordinates (to do some kind of re-mapping of the image), remember that the
133coordinates are scaled up to the dimensions of the image:
134
135<fiddle-embed name='ddbd4142c1c88232ae131d27266e72b3'></fiddle-embed>
136