Skip to content

Commit 9e6cf13

Browse files
authored
fix: Switch to r32float texture (#342)
1 parent 62bf2c3 commit 9e6cf13

33 files changed

Lines changed: 3284 additions & 180 deletions

ADRs/SDF rendering

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
rgba32float texture is way too big, it crashes Webkit on iphone.
2+
3+
What is the plan is to store just curve index + t value in a r16float.
4+
5+
TO know if you are inside or outside we can store red channe lwit hnegative or positive value... OR
6+
7+
Switch from using raycasting/widning number and dissallow hoels in a shape UNLESS we do it intentionally by boolean oepration (so inner curve direciton is reversed) or in fonts (so their inner cuvre direciton is reversE).
8+
9+
This way we can use jsut tangent fast oepraiton in FRAGMENT SHADER to determine where pixels lives.

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ https://webgpufundamentals.org/webgpu/lessons/resources/wgsl-offset-computer.htm
1010

1111
# Releasing versions
1212

13-
Each Pull Request has to be merged with squash, the following naming convention needs to be respected:
13+
Each Pull Request has to be merged with squash (currnetly to `main`, we have dropped `next` branch),, the following naming convention needs to be respected:
1414

1515
| Commit Message | Release Type |
1616
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
@@ -35,14 +35,14 @@ npm link @mateuszjs/magic-render
3535
In the repo where you want to put local version of the package.
3636
Remember to build package to see an update!
3737

38-
# Glossary
38+
## Glossary
3939

40-
## Units:
40+
### Units:
4141

42-
viewport - value expressed in pixels which will be rendered on the screen. This is exavtly how many pixels will be rendered, already includes retina. It's device physical pixel, not CSS pixel.
42+
- `Viewport` - value expressed in pixels which will be rendered onto the screen. Represents exactly how many pixels will be rendered, already includes retina/non-retina. It's physical device pixel, not CSS pixel.
4343

44-
world - values expressed in abstract units used in project. This unit is absolute, never changes, does not depend on anything. This is how all sizes of all assets and assets' properties are kept expressed.
44+
- `World` - values expressed in abstract units used in projects. This unit is absolute, never changes, does not depend on anything. This is how all sizes of all assets and assets' properties are kept expressed in zig state and snapshots.
4545

46-
texel - value expressed in texels, ususally in SDF texture texels. It corresponds to how many texels will be used in the SDF texture.
46+
- `Texel` - value expressed in texels (pixel but for texture), usually in SDF texture texels. It corresponds to how many texels will be used in the SDF texture.
4747

48-
Often when no unit is used, value is expressed in world coordinates OR the unit is obvious from the function context.
48+
_Often when no unit is used, a value is expressed in the world coordinates OR the unit is obvious from the context._

src/WebGPU/programs/clearSdf/getProgram.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default function getClearSdf(device: GPUDevice) {
1717

1818
return function clearSdf(passEncoder: GPUComputePassEncoder, texture: GPUTexture) {
1919
const bindGroup = device.createBindGroup({
20+
label: 'clearSdf bind group',
2021
layout: pipeline.getBindGroupLayout(0),
2122
entries: [
2223
{
Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,78 @@
1+
import clamp from 'utils/clamp'
12
import { delayedDestroy } from '../initPrograms'
23
import shaderCode from './shader.wgsl'
34

4-
const WORKING_GROUP_SIZE = [16, 4, 1]
5-
65
export default function getCombineSdf(device: GPUDevice) {
76
const shaderModule = device.createShaderModule({
87
label: 'combineSdf shader',
9-
code: shaderCode.replace('$WORKING_GROUP_SIZE', WORKING_GROUP_SIZE.join(', ')),
8+
code: shaderCode,
109
})
1110

12-
const pipeline = device.createComputePipeline({
13-
label: 'combineSdf pipeline',
11+
const pipeline = device.createRenderPipeline({
12+
label: 'combineSDF pipeline',
1413
layout: 'auto',
15-
compute: {
14+
vertex: {
15+
module: shaderModule,
16+
entryPoint: 'vs',
17+
},
18+
fragment: {
1619
module: shaderModule,
20+
entryPoint: 'fs',
21+
targets: [{ format: 'r32float' }],
22+
},
23+
depthStencil: {
24+
depthWriteEnabled: true,
25+
depthCompare: 'greater',
26+
format: 'depth24plus',
1727
},
1828
})
1929

2030
return function combineSdf(
21-
passEncoder: GPUComputePassEncoder,
22-
destinationTex: GPUTexture,
31+
passEncoder: GPURenderPassEncoder,
32+
destTex: GPUTexture,
2333
sourceTex: GPUTexture,
24-
computeDepthTex: GPUTexture,
25-
placementData: DataView<ArrayBuffer> // [placement_start_x, placement_start_y, placement_size_x, placement_size_y]
34+
uniformData: DataView<ArrayBuffer>, // placement + initial_t
35+
curvesDataView: DataView<ArrayBuffer>
2636
) {
37+
const curvesBuffer = device.createBuffer({
38+
label: 'renderShapeSdf curves buffer',
39+
size: curvesDataView.byteLength,
40+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
41+
})
42+
device.queue.writeBuffer(curvesBuffer, 0, curvesDataView)
43+
delayedDestroy(curvesBuffer)
44+
2745
const uniformBuffer = device.createBuffer({
2846
label: 'combine sdf uniform buffer',
29-
size: placementData.byteLength,
47+
size: uniformData.byteLength,
3048
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
3149
})
32-
device.queue.writeBuffer(uniformBuffer, 0, placementData)
50+
device.queue.writeBuffer(uniformBuffer, 0, uniformData)
3351
delayedDestroy(uniformBuffer)
3452

53+
const startX = uniformData.getFloat32(0 * 4, true)
54+
const startY = uniformData.getFloat32(1 * 4, true)
55+
const sizeX = uniformData.getFloat32(2 * 4, true)
56+
const sizeY = uniformData.getFloat32(3 * 4, true)
57+
const scissorX = Math.max(0, Math.floor(startX))
58+
const scissorY = Math.max(0, Math.floor(startY))
59+
const scissorW = clamp(Math.ceil(startX + sizeX) - scissorX, 1, destTex.width - scissorX)
60+
const scissorH = clamp(Math.ceil(startY + sizeY) - scissorY, 1, destTex.height - scissorY)
61+
3562
passEncoder.setPipeline(pipeline)
63+
passEncoder.setScissorRect(scissorX, scissorY, scissorW, scissorH)
3664

3765
const bindGroup = device.createBindGroup({
66+
label: 'combineSdf bind group',
3867
layout: pipeline.getBindGroupLayout(0),
3968
entries: [
40-
{ binding: 0, resource: destinationTex.createView() },
41-
{ binding: 1, resource: sourceTex.createView() },
42-
{ binding: 2, resource: computeDepthTex.createView() },
43-
{ binding: 3, resource: { buffer: uniformBuffer } },
69+
{ binding: 0, resource: sourceTex.createView() },
70+
{ binding: 1, resource: { buffer: uniformBuffer } },
71+
{ binding: 2, resource: { buffer: curvesBuffer } },
4472
],
4573
})
4674

4775
passEncoder.setBindGroup(0, bindGroup)
48-
49-
const startX = placementData.getFloat32(0 * 4, true)
50-
const startY = placementData.getFloat32(1 * 4, true)
51-
const sizeX = placementData.getFloat32(2 * 4, true)
52-
const sizeY = placementData.getFloat32(3 * 4, true)
53-
54-
const width = Math.max(0, Math.ceil(startX + sizeX) - Math.floor(startX))
55-
const height = Math.max(0, Math.ceil(startY + sizeY) - Math.floor(startY))
56-
57-
passEncoder.dispatchWorkgroups(
58-
Math.ceil(width / WORKING_GROUP_SIZE[0]),
59-
Math.ceil(height / WORKING_GROUP_SIZE[1])
60-
)
76+
passEncoder.draw(6)
6177
}
6278
}
Lines changed: 65 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,87 @@
1+
const STRAIGHT_LINE_THRESHOLD = 1e10;
2+
13
struct Uniform {
24
placement_start: vec2f,
35
placement_size: vec2f,
6+
initial_t: f32,
47
};
58

6-
@group(0) @binding(0) var destination_tex: texture_storage_2d<rgba32float, write>;
7-
@group(0) @binding(1) var source_tex: texture_storage_2d<rgba32float, read>;
8-
@group(0) @binding(2) var depth_tex: texture_storage_2d<r32float, read_write>;
9-
@group(0) @binding(3) var<uniform> u: Uniform;
10-
11-
@compute @workgroup_size($WORKING_GROUP_SIZE) fn cs(
12-
@builtin(global_invocation_id) id : vec3u
13-
) {
14-
if (any(u.placement_size <= vec2f(0.0))) {
15-
return;
16-
}
9+
@group(0) @binding(0) var source_tex: texture_2d<f32>;
10+
@group(0) @binding(1) var<uniform> u: Uniform;
11+
@group(0) @binding(2) var<storage, read> curves: array<vec2f>;
1712

18-
let placement_min = floor(u.placement_start);
19-
let placement_max = ceil(u.placement_start + u.placement_size);
13+
struct VSOutput {
14+
@builtin(position) position: vec4f,
15+
};
2016

21-
let dest_pos = vec2i(placement_min) + vec2i(id.xy);
17+
const vertex_list = array<vec2f, 6>(
18+
vec2f(-1.0, -1.0), vec2f( 1.0, -1.0), vec2f(-1.0, 1.0),
19+
vec2f(-1.0, 1.0), vec2f( 1.0, -1.0), vec2f( 1.0, 1.0),
20+
);
2221

23-
if (any(vec2f(dest_pos) >= placement_max)) {
24-
return;
25-
}
22+
@vertex fn vs(@builtin(vertex_index) idx: u32) -> VSOutput {
23+
return VSOutput(vec4f(vertex_list[idx], 0.0, 1.0));
24+
}
2625

27-
let dest_dims = vec2i(textureDimensions(destination_tex));
28-
if (any(dest_pos < vec2i(0)) || any(dest_pos >= dest_dims)) {
29-
return;
30-
}
26+
struct FSOutput {
27+
@location(0) color: f32,
28+
@builtin(frag_depth) depth: f32,
29+
};
3130

32-
let dest_center = vec2f(dest_pos) + vec2f(0.5);
31+
@fragment fn fs(vsOut: VSOutput) -> FSOutput {
32+
let dest_center = vsOut.position.xy;
3333
let local = (dest_center - u.placement_start) / u.placement_size;
34-
35-
if (any(local < vec2f(0.0)) || any(local >= vec2f(1.0))) {
36-
return;
37-
}
38-
3934
let source_size = vec2f(textureDimensions(source_tex));
4035
let source_sample_pos = local * source_size;
41-
let source_texel = getSampleSource(source_sample_pos);
42-
let scale = source_size / u.placement_size;
43-
let scaled_dist = source_texel.r / scale.x; // we assume all sizes keeps their ratio width / height, so we can use .x or .y here
44-
let depth = textureLoad(depth_tex, dest_pos).r;
45-
46-
if (scaled_dist > depth) {
47-
textureStore(destination_tex, dest_pos, vec4f(scaled_dist, source_texel.g, source_texel.b, source_texel.a));
48-
textureStore(depth_tex, dest_pos, vec4f(scaled_dist));
49-
}
50-
}
36+
let source_texel = textureLoad(source_tex, vec2u(source_sample_pos), 0);
5137

52-
fn getSampleSource(pos: vec2f) -> vec4f {
53-
let min = vec2i(0);
54-
let source_dims_u = textureDimensions(source_tex);
55-
let max = vec2i(textureDimensions(source_tex)) - vec2i(1);
38+
let sanitized_t = abs(source_texel.r) - 1.0;
39+
let closest_curve_point = g_to_bezier_pos(sanitized_t);
40+
let distance = length(dest_center - closest_curve_point);
41+
let scaled_dist = 0.5 + sign(source_texel.r) * distance / 1000;//max(source_size.x, source_size.y);
5642

57-
// We do not clamp pos on purpose. Textures always have empty 1 texel paddign around.
43+
return FSOutput(
44+
(1.0 + u.initial_t + sanitized_t) * sign(source_texel.r),
45+
scaled_dist,
46+
);
47+
}
48+
49+
fn bezier_point(curve: CubicBezier, t: f32) -> vec2f {
50+
let t2 = t * t;
51+
let t3 = t2 * t;
52+
let one_minus_t = 1.0 - t;
53+
let one_minus_t2 = one_minus_t * one_minus_t;
54+
let one_minus_t3 = one_minus_t2 * one_minus_t;
55+
56+
return curve.p0 * one_minus_t3 +
57+
3.0 * curve.p1 * t * one_minus_t2 +
58+
3.0 * curve.p2 * t2 * one_minus_t +
59+
curve.p3 * t3;
60+
}
5861

5962

60-
let base_pos = pos - vec2f(0.5);
61-
let floor_pos = vec2i(floor(base_pos));
62-
let fract_pos = base_pos - vec2f(floor_pos);
63+
struct CubicBezier {
64+
p0: vec2f,
65+
p1: vec2f,
66+
p2: vec2f,
67+
p3: vec2f,
68+
};
6369

64-
let p00 = vec2u(clamp(floor_pos, min, max));
65-
let p10 = vec2u(clamp(floor_pos + vec2i(1, 0), min, max));
66-
let p01 = vec2u(clamp(floor_pos + vec2i(0, 1), min, max));
67-
let p11 = vec2u(clamp(floor_pos + vec2i(1, 1), min, max));
6870

69-
let c00 = textureLoad(source_tex, p00);
70-
let c10 = textureLoad(source_tex, p10);
71-
let c01 = textureLoad(source_tex, p01);
72-
let c11 = textureLoad(source_tex, p11);
71+
fn g_to_bezier_pos(global_t: f32) -> vec2f {
72+
let idx = u32(global_t);
73+
let local_t = fract(global_t);
74+
let curve = CubicBezier(
75+
curves[idx * 4 + 0],
76+
curves[idx * 4 + 1],
77+
curves[idx * 4 + 2],
78+
curves[idx * 4 + 3]
79+
);
7380

74-
let top = mix(c00, c10, fract_pos.x);
75-
let bottom = mix(c01, c11, fract_pos.x);
81+
let is_straight_line = curve.p1.x > STRAIGHT_LINE_THRESHOLD;
82+
if (is_straight_line) {
83+
return mix(curve.p0, curve.p3, local_t);
84+
}
7685

77-
return mix(top, bottom, fract_pos.y);
86+
return bezier_point(curve, local_t);
7887
}

src/WebGPU/programs/computeShape/shader.wgsl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,5 +350,10 @@ fn evaluate_shape(point: vec2f) -> ShapeInfo {
350350

351351
let signed_dist = select(-min_distance, min_distance, is_inside);
352352

353-
return ShapeInfo(signed_dist, f32(closest_curve_idx) + closest_t, angle);
353+
let t = f32(closest_curve_idx) + closest_t + 1.0; // plus one to avoid 0
354+
// 0 cannot indicate if its negative or positive
355+
let signed_t = select(-t, t, is_inside);
356+
// let signed_t = t;
357+
358+
return ShapeInfo(signed_dist, signed_t, angle);
354359
}

0 commit comments

Comments
 (0)