123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- 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
- =====================
- * 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).
- * 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
- * "@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.
- * GLSL caps can be referenced via the syntax 'sk_Caps.<name>', e.g.
- sk_Caps.sampleVariablesSupport. The value will be a constant boolean or int,
- 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.
- * no #version statement is required, and it will be ignored if present
- * the output color is sk_FragColor (do not declare it)
- * use sk_Position instead of gl_Position. sk_Position is in device coordinates
- rather than normalized coordinates.
- * use sk_PointSize instead of gl_PointSize
- * use sk_VertexID instead of gl_VertexID
- * use sk_InstanceID instead of gl_InstanceID
- * the fragment coordinate is sk_FragCoord, and is always relative to the upper
- left.
- * use sk_Clockwise instead of gl_FrontFacing. This is always relative to an
- upper left origin.
- * you do not need to include ".0" to make a number a float (meaning that
- "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)
- * type suffixes on numbers (1.0f, 0xFFu) are both unnecessary and unsupported
- * creating a smaller vector from a larger vector (e.g. float2(float3(1))) is
- intentionally disallowed, as it is just a wordier way of performing a swizzle.
- Use swizzles instead.
- * The last swizzle component, in addition to the normal rgba / xyzw components,
- can also be the constants '0' or '1'. foo.rgb1 is equivalent to
- float4(foo.rgb, 1).
- * Use texture() instead of textureProj(), e.g. texture(sampler2D, float3) is
- equivalent to GLSL's textureProj(sampler2D, float3)
- * Render target width and height are available via sk_Width and sk_Height
- * 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.
- SkSL Fragment Processors
- ========================
- ********************************************************************************
- *** 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. ***
- ********************************************************************************
- 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
- fragment processor's portion of the overall fragment shader.
- 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)
- @headerEnd (at the end of the .h file)
- @class (in the .h file, inside the class declaration)
- @cpp (in the .cpp file)
- @cppEnd (at the end of the .cpp file)
- @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)
- @clone (replaces the default clone() function)
- @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)
- @coordTransform(<sampler>)
- (the matrix to attach to the named sampler2D's
- GrCoordTransform)
- @samplerParams(<sampler>)
- (the sampler params to attach to the named sampler2D)
- * global 'in' variables represent data passed to the fragment processor at
- construction time. These variables become constructor parameters and are
- stored in fragment processor fields. By default float2/half2 maps to SkPoints,
- 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
- (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
- 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
- things.
- * '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
- 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.
- * '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.
- * the 'sk_TransformedCoords2D' array provides access to 2D transformed
- coordinates. sk_TransformedCoords2D[0] is equivalent to calling
- fragBuilder->ensureCoords2D(args.fTransformedCoords[0]) (and the result is
- cached, so you need not worry about using the value repeatedly).
- * 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.
- 'layout(key) uniform int x;' means that this uniform should be included in
- 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.
- * child processors can be declared with 'in fragmentProcessor <name>;', and can
- be invoked by calling 'process(<name>)' or 'process(<name>, <inputColor>)'.
- 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.
- * By default, fragment processors must be non-null. The type for a nullable
- fragment processor is 'fragmentProcessor?', as in
- 'in fragmentProcessor? <name>'. You can check for the presence of such a
- fragment processor by comparing it to 'null'.
- 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
- changes to the .fp file.
|