RuntimeShader Class

Definition

A RuntimeShader calculates a per-pixel color based on the output of a user defined Android Graphics Shading Language (AGSL) function.

[Android.Runtime.Register("android/graphics/RuntimeShader", ApiSince=33, DoNotGenerateAcw=true)]
public class RuntimeShader : Android.Graphics.Shader
[<Android.Runtime.Register("android/graphics/RuntimeShader", ApiSince=33, DoNotGenerateAcw=true)>]
type RuntimeShader = class
    inherit Shader
Inheritance
RuntimeShader
Attributes

Remarks

A RuntimeShader calculates a per-pixel color based on the output of a user defined Android Graphics Shading Language (AGSL) function.

<h3>Android Graphics Shading Language</h3>

The AGSL syntax is very similar to OpenGL ES Shading Language, but there are some important differences that are highlighted here. Most of these differences are summed up in one basic fact: <b>With GPU shading languages, you are programming a stage of the GPU pipeline. With AGSL, you are programming a stage of the Canvas or RenderNode drawing pipeline.</b>

In particular, a GLSL fragment shader controls the entire behavior of the GPU between the rasterizer and the blending hardware. That shader does all of the work to compute a color, and the color it generates is exactly what is fed to the blending stage of the pipeline.

In contrast, AGSL functions exist as part of a larger pipeline. When you issue a Canvas drawing operation, Android (generally) assembles a single GPU fragment shader to do all of the required work. This shader typically includes several pieces. For example, it might include:

<ul> <li>Evaluating whether a pixel falls inside or outside of the shape being drawn (or on the border, where it might apply antialiasing).</li> <li>Evaluating whether a pixel falls inside or outside of the clipping region (again, with possible antialiasing logic for border pixels).</li> <li>Logic for the Shader, ColorFilter, and BlendMode on the Paint.</li> <li>Color space conversion code, as part of Android’s color management.</li> </ul>

A RuntimeShader, like other Shader types, effectively contributes a function to the GPU’s fragment shader.

<h3>AGSL Shader Execution</h3>

Just like a GLSL shader, an AGSL shader begins execution in a main function. Unlike GLSL, the function receives as an input parameter the position of the pixel within the Canvas or RenderNode coordinate space (similar to gl_fragCoord) and returns the color to be shaded as a vec4 (similar to out vec4 color or gl_FragColor in GLSL).

vec4 main(vec2 canvas_coordinates);

AGSL and GLSL use different coordinate spaces by default. In GLSL, the fragment coordinate (fragCoord) is relative to the lower left. AGSL matches the screen coordinate system of the Android Canvas which has its origin as the upper left corner. This means that the coordinates provided as a parameter in the main function are local to the canvas with the exception of any Shader#getLocalMatrix(Matrix) transformations applied to this shader. Additionally, if the shader is invoked by another using #setInputShader(String, Shader), then that parent shader may modify the input coordinates arbitrarily.

<h3>AGSL and Color Spaces</h3>

Android Graphics and by extension RuntimeShader are color managed. The working ColorSpace for an AGSL shader is defined to be the color space of the destination, which in most cases is determined by Window#setColorMode(int).

When authoring an AGSL shader, you won’t know what the working color space is. For many effects, this is fine because by default color inputs are automatically converted into the working color space. For certain effects, it may be important to do some math in a fixed, known color space. A common example is lighting – to get physically accurate lighting, math should be done in a linear color space. To help with this, AGSL provides two intrinsic functions that convert colors between the working color space and the ColorSpace.Named#LINEAR_EXTENDED_SRGB color space:

vec3 toLinearSrgb(vec3 color);
            vec3 fromLinearSrgb(vec3 color);

<h3>AGSL and Premultiplied Alpha</h3>

When dealing with transparent colors, there are two (common) possible representations: straight (unassociated) alpha and premultiplied (associated) alpha. In ASGL the color returned by the main function is expected to be premultiplied. AGSL’s use of premultiplied alpha implies:

<ul> <li>If your AGSL shader will return transparent colors, be sure to multiply the RGB by A. The resulting color should be [R*A, G*A, B*A, A], not [R, G, B, A].</li> <li>For more complex shaders, you must understand which of your colors are premultiplied vs. straight. Many operations don’t make sense if you mix both kinds of color together.</li> </ul>

<h3>Uniforms</h3>

AGSL, like GLSL, exposes the concept of uniforms. An AGSL uniform is defined as a read-only, global variable that is accessible by the AGSL code and is initialized by a number of setter methods on RuntimeShader. AGSL exposes two primitive uniform data types (float, int) and two specialized types (colors, shaders) that are outlined below.

<h4>Primitive Uniforms</h4>

There are two primitive uniform types supported by AGSL, float and int. For these types and uniforms representing a grouping of these types, like arrays and matrices, there are corresponding RuntimeShader methods to initialize them. <table border="2" width="85%" align="center" cellpadding="5"> <thead> <tr><th>Java Type</th> <th>AGSL Type</th> <th>Method</th> </tr> </thead>

<tbody> <tr> <td rowspan="4">Floats</td> <td>float</td> <td>RuntimeShader#setFloatUniform(String, float)</td> </tr> <tr> <td>vec2</td> <td>RuntimeShader#setFloatUniform(String, float, float)</td> </tr> <tr> <td>vec3</td> <td>RuntimeShader#setFloatUniform(String, float, float, float)</td> </tr> <tr> <td>vec4</td> <td>RuntimeShader#setFloatUniform(String, float, float, float, float)</td> </tr> <tr> <td rowspan="4">Integers</td> <td>int</td> <td>RuntimeShader#setIntUniform(String, int)</td> </tr> <tr> <td>ivec2</td> <td>RuntimeShader#setIntUniform(String, int, int)</td> </tr> <tr> <td>ivec3</td> <td>RuntimeShader#setIntUniform(String, int, int, int)</td> </tr> <tr> <td>ivec4</td> <td>RuntimeShader#setIntUniform(String, int, int, int, int)</td> </tr> <tr> <td rowspan="2">Matrices and Arrays</td> <td>mat2, mat3, and mat4, and float[]</td> <td>RuntimeShader#setFloatUniform(String, float[])</td> </tr> <tr> <td>int[]</td> <td>RuntimeShader#setIntUniform(String, int[])</td> </tr> </tbody> </table>

For example, a simple AGSL shader making use of a float uniform to modulate the transparency of the output color would look like:

uniform float alpha;
            vec4 main(vec2 canvas_coordinates) {
                vec3 red = vec3(1.0, 0.0, 0.0);
                return vec4(red * alpha, alpha);
            }

After creating a RuntimeShader with that program the uniform can then be initialized and updated per frame by calling RuntimeShader#setFloatUniform(String, float) with the value of alpha. The value of a primitive uniform defaults to 0 if it is declared in the AGSL shader but not initialized.

<h4>Color Uniforms</h4>

AGSL doesn't know if uniform variables contain colors, it won't automatically convert them to the working colorspace of the shader at runtime. However, you can label your vec4 uniform with the "layout(color)" qualifier which lets Android know that the uniform will be used as a color. Doing so allows AGSL to transform the uniform value to the working color space. In AGSL, declare the uniform like this:

layout(color) uniform vec4 inputColorA;
            layout(color) uniform vec4 inputColorB;
            vec4 main(vec2 canvas_coordinates) {
                // blend the two colors together and return the resulting color
                return mix(inputColorA, inputColorB, 0.5);
            }

After creating a RuntimeShader with that program the uniforms can then be initialized and updated per frame by calling RuntimeShader#setColorUniform(String, int), RuntimeShader#setColorUniform(String, long), or RuntimeShader#setColorUniform(String, Color) with the desired colors. The value of a color uniform is undefined if it is declared in the AGSL shader but not initialized.

<h4>Shader Uniforms</h4> In GLSL, a fragment shader can sample a texture. For AGSL instead of sampling textures you can sample from any Shader, which includes but is not limited to BitmapShader. To make it clear that you are operating on an Shader object there is no "sample" method. Instead, the shader uniform has an "eval()" method. This distinction enables AGSL shaders to sample from existing bitmap and gradient shaders as well as other RuntimeShader objects. In AGSL, declare the uniform like this:

uniform shader myShader;
            vec4 main(vec2 canvas_coordinates) {
                // swap the red and blue color channels when sampling from myShader
                return myShader.eval(canvas_coordinates).bgra;
            }

After creating a RuntimeShader with that program the shader uniform can then be initialized and updated per frame by calling RuntimeShader#setInputShader(String, Shader) with the desired shader. The value of a shader uniform is undefined if it is declared in the AGSL shader but not initialized.

Although most BitmapShaders contain colors that should be color managed, some contain data that isn’t actually colors. This includes bitmaps storing normals, material properties (e.g. roughness), heightmaps, or any other purely mathematical data that happens to be stored in a bitmap. When using these kinds of shaders in AGSL, you probably want to initialize them with #setInputBuffer(String, BitmapShader). Shaders initialized this way work much like a regular BitmapShader (including filtering and tiling), with a few major differences: <ul> <li>No color space transformation is applied (the color space of the bitmap is ignored).</li> <li>Bitmaps that return false for Bitmap#isPremultiplied() are not automatically premultiplied.</li> </ul>

In addition, when sampling from a BitmapShader be aware that the shader does not use normalized coordinates (like a texture in GLSL). It uses (0, 0) in the upper-left corner, and (width, height) in the bottom-right corner. Normally, this is exactly what you want. If you’re evaluating the shader with coordinates based on the ones passed to your AGSL program, the scale is correct. However, if you want to adjust those coordinates (to do some kind of re-mapping of the bitmap), remember that the coordinates are local to the canvas.

Java documentation for android.graphics.RuntimeShader.

Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

Constructors

RuntimeShader(IntPtr, JniHandleOwnership)
RuntimeShader(String)

Creates a new RuntimeShader.

Properties

Class

Returns the runtime class of this Object.

(Inherited from Object)
Handle

The handle to the underlying Android instance.

(Inherited from Object)
JniIdentityHashCode (Inherited from Object)
JniPeerMembers
PeerReference (Inherited from Object)
ThresholdClass
ThresholdType

Methods

Clone()

Creates and returns a copy of this object.

(Inherited from Object)
Dispose() (Inherited from Object)
Dispose(Boolean) (Inherited from Object)
Equals(Object)

Indicates whether some other object is "equal to" this one.

(Inherited from Object)
GetHashCode()

Returns a hash code value for the object.

(Inherited from Object)
GetLocalMatrix(Matrix)

Return true if the shader has a non-identity local matrix.

(Inherited from Shader)
JavaFinalize()

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.

(Inherited from Object)
Notify()

Wakes up a single thread that is waiting on this object's monitor.

(Inherited from Object)
NotifyAll()

Wakes up all threads that are waiting on this object's monitor.

(Inherited from Object)
SetColorUniform(String, Color)

Sets the uniform color value corresponding to this shader.

SetColorUniform(String, ColorObject)

Sets the uniform color value corresponding to this shader.

SetColorUniform(String, Int64)

Sets the uniform color value corresponding to this shader.

SetFloatUniform(String, Single)

Sets the uniform value corresponding to this shader.

SetFloatUniform(String, Single, Single)

Sets the uniform value corresponding to this shader.

SetFloatUniform(String, Single, Single, Single)

Sets the uniform value corresponding to this shader.

SetFloatUniform(String, Single, Single, Single, Single)

Sets the uniform value corresponding to this shader.

SetFloatUniform(String, Single[])

Sets the uniform value corresponding to this shader.

SetHandle(IntPtr, JniHandleOwnership)

Sets the Handle property.

(Inherited from Object)
SetInputBuffer(String, BitmapShader)

Assigns the uniform shader to the provided shader parameter.

SetInputShader(String, Shader)

Assigns the uniform shader to the provided shader parameter.

SetIntUniform(String, Int32)

Sets the uniform value corresponding to this shader.

SetIntUniform(String, Int32, Int32)

Sets the uniform value corresponding to this shader.

SetIntUniform(String, Int32, Int32, Int32)

Sets the uniform value corresponding to this shader.

SetIntUniform(String, Int32, Int32, Int32, Int32)

Sets the uniform value corresponding to this shader.

SetIntUniform(String, Int32[])

Sets the uniform value corresponding to this shader.

SetLocalMatrix(Matrix)

Set the shader's local matrix.

(Inherited from Shader)
ToArray<T>() (Inherited from Object)
ToString()

Returns a string representation of the object.

(Inherited from Object)
UnregisterFromRuntime() (Inherited from Object)
Wait()

Causes the current thread to wait until it is awakened, typically by being <em>notified</em> or <em>interrupted</em>.

(Inherited from Object)
Wait(Int64)

Causes the current thread to wait until it is awakened, typically by being <em>notified</em> or <em>interrupted</em>, or until a certain amount of real time has elapsed.

(Inherited from Object)
Wait(Int64, Int32)

Causes the current thread to wait until it is awakened, typically by being <em>notified</em> or <em>interrupted</em>, or until a certain amount of real time has elapsed.

(Inherited from Object)

Explicit Interface Implementations

IJavaPeerable.Disposed() (Inherited from Object)
IJavaPeerable.DisposeUnlessReferenced() (Inherited from Object)
IJavaPeerable.Finalized() (Inherited from Object)
IJavaPeerable.JniManagedPeerState (Inherited from Object)
IJavaPeerable.SetJniIdentityHashCode(Int32) (Inherited from Object)
IJavaPeerable.SetJniManagedPeerState(JniManagedPeerStates) (Inherited from Object)
IJavaPeerable.SetPeerReference(JniObjectReference) (Inherited from Object)

Extension Methods

JavaCast<TResult>(IJavaObject)

Performs an Android runtime-checked type conversion.

JavaCast<TResult>(IJavaObject)
GetJniTypeName(IJavaPeerable)

Applies to