Skip to content

Commit f859d14

Browse files
authored
Merge pull request #57 from Shopify/fix/49
Update documentation on uniform types + Minor shader improvements
2 parents f913a5c + b8b6b56 commit f859d14

File tree

8 files changed

+62
-29
lines changed

8 files changed

+62
-29
lines changed
-1.06 KB
Loading

docs/docs/shaders/language.md

+16-11
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ if (!source) {
3030
Creates a shader from source.
3131
Shaders can be nested with one another.
3232

33-
| Name | Type | Description |
34-
|:---------|:----------------|:------------------------------|
35-
| source | `RuntimeEffect` | Compiled shaders |
36-
| uniforms | `number` | uniform values |
37-
| children | `Shader` | Shaders to be used as uniform |
33+
| Name | Type | Description |
34+
|:---------|:---------------------------------------------------------------------|:------------------------------|
35+
| source | `RuntimeEffect` | Compiled shaders |
36+
| uniforms | <code>{ [name: string]: number &#124; Vector &#124; number[]}</code> | uniform values |
37+
| children | `Shader` | Shaders to be used as uniform |
3838

3939
### Simple Shader
4040

@@ -63,25 +63,30 @@ const SimpleShader = () => {
6363

6464
### Using Uniforms
6565

66+
Uniforms are variables used to parametrize shaders.
67+
The following uniform types are supported: `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, `float4x4`, `int`, `int2`, `int3` and, `int4`.
68+
6669
```tsx twoslash
67-
import {Canvas, Skia, Paint, Shader, Fill} from "@shopify/react-native-skia";
70+
import {Canvas, Skia, Paint, Shader, Fill, vec} from "@shopify/react-native-skia";
6871

6972
const source = Skia.RuntimeEffect.Make(`
70-
uniform float blue;
73+
uniform vec2 c;
7174
uniform float r;
75+
uniform float blue;
7276
7377
vec4 main(vec2 pos) {
7478
vec2 normalized = pos/vec2(2 * r);
75-
return distance(pos, vec2(r)) > r ? vec4(1) : vec4(normalized.x, normalized.y, blue, 1);
79+
return distance(pos, c) > r ? vec4(1) : vec4(normalized, blue, 1);
7680
}`)!;
7781

7882
const UniformShader = () => {
79-
const blue = 1.0;
8083
const r = 128;
84+
const c = vec(2 * r, r);
85+
const blue = 1.0;
8186
return (
8287
<Canvas style={{ flex: 1 }}>
8388
<Paint>
84-
<Shader source={source} uniforms={[blue, r]} />
89+
<Shader source={source} uniforms={{ c, r, blue }} />
8590
</Paint>
8691
<Fill />
8792
</Canvas>
@@ -122,4 +127,4 @@ const NestedShader = () => {
122127
};
123128
```
124129

125-
![Simple Shader](assets/nested.png)
130+
![Simple Shader](assets/nested.png)

example/src/Examples/Filters/Filters.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ export const Filters = () => {
2727
return (
2828
<Canvas style={{ width, height }}>
2929
<Paint>
30-
<Shader source={source} uniforms={() => [mix(progress.value, 1, 100)]}>
30+
<Shader
31+
source={source}
32+
uniforms={() => ({ r: mix(progress.value, 1, 100) })}
33+
>
3134
<ImageShader
3235
source={require("../../assets/oslo.jpg")}
3336
fit="cover"

example/src/Examples/Hue/Hue.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ const { width, height } = Dimensions.get("window");
1919
const c = vec(width / 2, height / 2);
2020

2121
const source = Skia.RuntimeEffect.Make(`
22-
uniform float cx;
23-
uniform float cy;
22+
uniform float2 c;
2423
uniform float r;
2524
2625
${ShaderLib.Math}
@@ -31,7 +30,6 @@ float quadraticIn(float t) {
3130
}
3231
3332
half4 main(vec2 uv) {
34-
float2 c = vec2(cx, cy);
3533
float mag = distance(uv, c);
3634
float theta = normalizeRad(canvas2Polar(uv, c).x);
3735
return hsv2rgb(vec3(theta/TAU, quadraticIn(mag/r), 1.0));
@@ -52,7 +50,7 @@ export const Hue = () => {
5250
<Fill color="#1f1f1f" />
5351
<Paint>
5452
<BlurMask sigma={40} style="solid" />
55-
<Shader source={source} uniforms={[c.x, c.y, r]} />
53+
<Shader source={source} uniforms={{ c, r }} />
5654
</Paint>
5755
<Circle c={c} r={r} />
5856
<Circle

package/cpp/api/JsiSKRuntimeEffect.h

+7-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace RNSkia
3232
{
3333
auto uniforms = castUniforms(runtime, arguments[0]);
3434
auto isOpaque = count >= 2 && arguments[1].isBool() ? arguments[1].getBool() : false;
35-
auto matrix = count == 3 ? JsiSkMatrix::fromValue(runtime, arguments[2]).get() : nullptr;
35+
auto matrix = count >= 3 && !arguments[2].isUndefined() && !arguments[2].isNull() ? JsiSkMatrix::fromValue(runtime, arguments[2]).get() : nullptr;
3636

3737
// Create and return shader as host object
3838
auto shader = getObject()->makeShader(std::move(uniforms), nullptr,
@@ -60,7 +60,7 @@ namespace RNSkia
6060
children.push_back(shader);
6161
}
6262

63-
auto matrix = count == 4 ? JsiSkMatrix::fromValue(runtime, arguments[3]).get() : nullptr;
63+
auto matrix = count >= 4 && !arguments[3].isUndefined() && !arguments[3].isNull() ? JsiSkMatrix::fromValue(runtime, arguments[3]).get() : nullptr;
6464

6565
// Create and return shader as host object
6666
auto shader = getObject()->makeShader(std::move(uniforms), children.data(),
@@ -131,8 +131,11 @@ namespace RNSkia
131131
// Convert to skia uniforms
132132
for (int i = 0; i < jsiUniformsSize; i++)
133133
{
134-
// We only support floats for now
135-
float value = jsiUniforms.getValueAtIndex(runtime, i).asNumber();
134+
auto it = getObject()->uniforms().begin() + i;
135+
RuntimeEffectUniform u = fromUniform(*it);
136+
float fValue = jsiUniforms.getValueAtIndex(runtime, i).asNumber();
137+
int iValue = static_cast<int>(fValue);
138+
auto value = u.isInteger ? iValue : fValue;
136139
memcpy(SkTAddOffset<void>(uniforms->writable_data(), i * sizeof(value)),
137140
&value, sizeof(value));
138141
}

package/src/renderer/components/shaders/Shader.tsx

+29-6
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,47 @@ import type { ReactNode } from "react";
33

44
import { isShader } from "../../../skia";
55
import type { IRuntimeEffect } from "../../../skia";
6+
import type { Vector, AnimatedProps, TransformProps } from "../../processors";
67
import { useDeclaration } from "../../nodes/Declaration";
7-
import type { AnimatedProps } from "../../processors/Animations/Animations";
8+
import { localMatrix } from "../../processors";
9+
import { hasProperty } from "../../typeddash";
810

9-
export interface ShaderProps {
11+
const isVector = (obj: unknown): obj is Vector =>
12+
hasProperty(obj, "x") && hasProperty(obj, "y");
13+
14+
type Uniform = number | number[] | Vector;
15+
16+
interface Uniforms {
17+
[name: string]: Uniform;
18+
}
19+
20+
export interface ShaderProps extends TransformProps {
1021
source: IRuntimeEffect;
11-
uniforms: number[];
22+
uniforms: Uniforms;
1223
isOpaque?: boolean;
1324
children?: ReactNode | ReactNode[];
1425
}
1526

1627
export const Shader = (props: AnimatedProps<ShaderProps>) => {
1728
const declaration = useDeclaration<ShaderProps>(
1829
props,
19-
({ uniforms, source, isOpaque }, children) => {
30+
({ uniforms, source, isOpaque, ...transform }, children) => {
31+
const processedUniforms = new Array(source.getUniformCount())
32+
.fill(0)
33+
.map((_, i) => {
34+
const name = source.getUniformName(i);
35+
const value = uniforms[name];
36+
if (isVector(value)) {
37+
return [value.x, value.y];
38+
}
39+
return value;
40+
})
41+
.flat(4);
2042
return source.makeShaderWithChildren(
21-
uniforms,
43+
processedUniforms,
2244
isOpaque,
23-
children.filter(isShader)
45+
children.filter(isShader),
46+
localMatrix(transform)
2447
);
2548
}
2649
);

package/src/renderer/processors/Shapes.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ReactNode } from "react";
22

33
import type { IRect, IRRect } from "../../skia";
4+
import { hasProperty } from "../typeddash";
45

56
import type { Vector as Point } from "./math/Vector";
67
import { vec } from "./math/Vector";
@@ -24,9 +25,6 @@ interface ScalarCircleDef {
2425

2526
export type CircleDef = PointCircleDef | ScalarCircleDef;
2627

27-
const hasProperty = (obj: unknown, key: string) =>
28-
!!(typeof obj === "object" && obj !== null && key in obj);
29-
3028
const isCircleScalarDef = (def: CircleDef): def is ScalarCircleDef =>
3129
hasProperty(def, "cx");
3230
export const processCircle = (def: CircleDef) => {

package/src/renderer/typeddash.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
export const mapKeys = <T>(obj: T) => Object.keys(obj) as (keyof T)[];
22

3+
export const hasProperty = (obj: unknown, key: string) =>
4+
!!(typeof obj === "object" && obj !== null && key in obj);
5+
36
export const exhaustiveCheck = (a: never): never => {
47
throw new Error(`Unexhaustive handling for ${a}`);
58
};

0 commit comments

Comments
 (0)