Skip to content

Commit 995a7c4

Browse files
authored
fix: Rotate icon (#19)
* get correct rounded triange * remove commented code, calculate real coords of pixel in vertex shader * small refactor * bigger refactor of draw triangle shader * fix issue with radius * shader is working correctly * fix drawing lines * optimizations of interpolation, adding support for opacity,refactor slices in zig * remove unnecessary wrapping around modules * render rotate icon * fix typo and remove console lof
1 parent 5be0639 commit 995a7c4

File tree

19 files changed

+493
-263
lines changed

19 files changed

+493
-263
lines changed

src/WebGPU/programs/drawMSDF/getProgram.ts

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import shaderCode from './shader.wgsl'
22

3-
const STRIDE = 4 + 2 // + 1 + 1 + 4
3+
const INSTANCE_STRIDE =
4+
3 /*3 verticies*/ * (4 /*destinatio position*/ + 2) /*source position*/ + 4 /*color*/
45

56
export default function getProgram(device: GPUDevice, presentationFormat: GPUTextureFormat) {
67
const module = device.createShaderModule({
@@ -21,13 +22,16 @@ export default function getProgram(device: GPUDevice, presentationFormat: GPUTex
2122
entryPoint: 'vs',
2223
buffers: [
2324
{
24-
arrayStride: STRIDE * 4,
25+
arrayStride: INSTANCE_STRIDE * 4,
26+
stepMode: 'instance',
2527
attributes: [
26-
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // destination position
27-
{ shaderLocation: 1, offset: 16, format: 'float32x2' }, // source position
28-
// {shaderLocation: 2, offset: 16 + 8, format: 'float32'}, // source texture layer
29-
// {shaderLocation: 3, offset: 16 + 8 + 4, format: 'float32'}, // index of color matrix
30-
// {shaderLocation: 4, offset: 16 + 8 + 4 + 4, format: 'float32x3'}, // index of color matrix
28+
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // p0 destination position
29+
{ shaderLocation: 1, offset: 16, format: 'float32x2' }, // p0 source position
30+
{ shaderLocation: 2, offset: 16 + 8, format: 'float32x4' }, // p1 destination position
31+
{ shaderLocation: 3, offset: 16 + 8 + 16, format: 'float32x2' }, // p1 source position
32+
{ shaderLocation: 4, offset: 16 + 8 + 16 + 8, format: 'float32x4' }, // p2 destination position
33+
{ shaderLocation: 5, offset: 16 + 8 + 16 + 8 + 16, format: 'float32x2' }, // p2 source position
34+
{ shaderLocation: 6, offset: 16 + 8 + 16 + 8 + 16 + 8, format: 'float32x4' }, // color
3135
] as const,
3236
},
3337
],
@@ -38,16 +42,16 @@ export default function getProgram(device: GPUDevice, presentationFormat: GPUTex
3842
targets: [
3943
{
4044
format: presentationFormat,
41-
// blend: {
42-
// color: {
43-
// srcFactor: 'one',
44-
// dstFactor: 'one-minus-src-alpha'
45-
// },
46-
// alpha: {
47-
// srcFactor: 'one',
48-
// dstFactor: 'one-minus-src-alpha'
49-
// },
50-
// },
45+
blend: {
46+
color: {
47+
srcFactor: 'one',
48+
dstFactor: 'one-minus-src-alpha',
49+
},
50+
alpha: {
51+
srcFactor: 'one',
52+
dstFactor: 'one-minus-src-alpha',
53+
},
54+
},
5155
},
5256
],
5357
},
@@ -76,15 +80,16 @@ export default function getProgram(device: GPUDevice, presentationFormat: GPUTex
7680
kScreenPixelDistanceOffset + 1
7781
)
7882

79-
return function drawTexture(
83+
return function drawMSDF(
8084
pass: GPURenderPassEncoder,
8185
worldProjectionMatrix: Float32Array,
82-
vertexData: Float32Array<ArrayBufferLike>,
86+
vertexData: Float32Array,
8387
texture: GPUTexture
8488
) {
85-
const numVertices = (vertexData.length / STRIDE) | 0
89+
const numInstances = vertexData.length / INSTANCE_STRIDE
90+
8691
const vertexBuffer = device.createBuffer({
87-
label: 'vertex buffer vertices',
92+
label: 'draw msdf - vertex buffer',
8893
size: vertexData.byteLength,
8994
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
9095
})
@@ -109,6 +114,6 @@ export default function getProgram(device: GPUDevice, presentationFormat: GPUTex
109114
device.queue.writeBuffer(uniformBuffer, 0, uniformValues)
110115

111116
pass.setBindGroup(0, bindGroup)
112-
pass.draw(numVertices)
117+
pass.draw(3, numInstances)
113118
}
114119
}

src/WebGPU/programs/drawMSDF/shader.wgsl

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
struct Vertex {
2-
@location(0) position: vec4f,
3-
@location(1) uv: vec2f,
2+
@location(0) p0_position: vec4f,
3+
@location(1) p0_uv: vec2f,
4+
@location(2) p1_position: vec4f,
5+
@location(3) p1_uv: vec2f,
6+
@location(4) p2_position: vec4f,
7+
@location(5) p2_uv: vec2f,
8+
@location(6) color: vec4f,
49
};
510

611
struct Uniforms {
@@ -11,16 +16,29 @@ struct Uniforms {
1116
struct VertexOutput {
1217
@builtin(position) position: vec4f,
1318
@location(0) texCoord: vec2f,
19+
@location(1) @interpolate(flat) color: vec4f,
1420
};
1521

1622
@group(0) @binding(0) var<uniform> u: Uniforms;
1723
@group(0) @binding(1) var ourSampler: sampler;
1824
@group(0) @binding(2) var ourTexture: texture_2d<f32>;
1925

20-
@vertex fn vs(vert: Vertex) -> VertexOutput {
26+
@vertex fn vs(vert: Vertex, @builtin(vertex_index) vertexIndex : u32) -> VertexOutput {
27+
let normVertexIndex = vertexIndex % 3;
28+
2129
var out: VertexOutput;
22-
out.position = u.worldViewProjection * vert.position;
23-
out.texCoord = vert.uv;
30+
if (normVertexIndex == 0) {
31+
out.position = u.worldViewProjection * vert.p0_position;
32+
out.texCoord = vert.p0_uv;
33+
} else if (normVertexIndex == 1) {
34+
out.position = u.worldViewProjection * vert.p1_position;
35+
out.texCoord = vert.p1_uv;
36+
} else {
37+
out.position = u.worldViewProjection * vert.p2_position;
38+
out.texCoord = vert.p2_uv;
39+
}
40+
41+
out.color = vert.color;
2442

2543
return out;
2644
}
@@ -35,5 +53,5 @@ fn median(r: f32, g: f32, b: f32) -> f32 {
3553

3654
let screenPxDistance = u.screenPxDistance * (sd - 0.5);
3755
let opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
38-
return vec4f(opacity, opacity, opacity, opacity);
56+
return in.color * opacity;
3957
}
Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
import shaderCode from "./shader.wgsl"
2-
1+
import shaderCode from './shader.wgsl'
32

4-
const STRIDE = 4 + 4// + 1 + 1 + 4
3+
const INSTANCE_STRIDE =
4+
4 * 3 /* positon */ + 4 /* color */ + 3 /* value of roudned corner for each of three positions */
55

6-
export default function getProgram(
7-
device: GPUDevice,
8-
presentationFormat: GPUTextureFormat
9-
) {
6+
export default function getProgram(device: GPUDevice, presentationFormat: GPUTextureFormat) {
107
const module = device.createShaderModule({
118
label: 'draw triangle module',
12-
code: shaderCode
9+
code: shaderCode,
1310
})
1411

1512
const pipeline = device.createRenderPipeline({
@@ -20,41 +17,40 @@ export default function getProgram(
2017
entryPoint: 'vs',
2118
buffers: [
2219
{
23-
arrayStride: STRIDE * 4,
20+
arrayStride: INSTANCE_STRIDE * 4, // The size in bytes for one instance's data
21+
stepMode: 'instance',
2422
attributes: [
25-
{shaderLocation: 0, offset: 0, format: 'float32x4'}, // destination position
26-
{shaderLocation: 1, offset: 16, format: 'float32x4'}, // color
23+
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // position 0
24+
{ shaderLocation: 1, offset: 16, format: 'float32x4' }, // position 1
25+
{ shaderLocation: 2, offset: 16 + 16, format: 'float32x4' }, // position 2
26+
{ shaderLocation: 3, offset: 16 + 16 + 16, format: 'float32x4' }, // color
27+
{ shaderLocation: 4, offset: 16 + 16 + 16 + 16, format: 'float32x3' }, // rounded corner values
2728
] as const,
2829
},
2930
],
3031
},
3132
fragment: {
3233
module,
3334
entryPoint: 'fs',
34-
targets: [{
35-
format: presentationFormat,
36-
// blend: {
37-
// color: {
38-
// srcFactor: 'one',
39-
// dstFactor: 'one-minus-src-alpha'
40-
// },
41-
// alpha: {
42-
// srcFactor: 'one',
43-
// dstFactor: 'one-minus-src-alpha'
44-
// },
45-
// },
46-
}],
35+
targets: [
36+
{
37+
format: presentationFormat,
38+
blend: {
39+
color: {
40+
srcFactor: 'one',
41+
dstFactor: 'one-minus-src-alpha',
42+
},
43+
alpha: {
44+
srcFactor: 'one',
45+
dstFactor: 'one-minus-src-alpha',
46+
},
47+
},
48+
},
49+
],
4750
},
48-
// depthStencil: {
49-
// depthWriteEnabled: true,
50-
// depthCompare: 'less',
51-
// format: 'depth24plus',
52-
// },
5351
})
5452

55-
const uniformBufferSize = (
56-
16/*projection matrix*/
57-
) * 4
53+
const uniformBufferSize = 16 /*projection matrix*/ * 4
5854
const uniformBuffer = device.createBuffer({
5955
label: 'uniforms',
6056
size: uniformBufferSize,
@@ -68,26 +64,24 @@ export default function getProgram(
6864
return function drawTriangle(
6965
pass: GPURenderPassEncoder,
7066
worldProjectionMatrix: Float32Array,
71-
vertexData: Float32Array<ArrayBufferLike>,
67+
vertexData: Float32Array<ArrayBufferLike>
7268
) {
73-
const numVertices = vertexData.length / STRIDE | 0
69+
// console.log('worldProjectionMatrix', worldProjectionMatrix)
70+
const numInstances = vertexData.length / INSTANCE_STRIDE
71+
7472
const vertexBuffer = device.createBuffer({
7573
label: 'vertex buffer vertices',
7674
size: vertexData.byteLength,
7775
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
7876
})
7977
device.queue.writeBuffer(vertexBuffer, 0, vertexData)
8078

81-
8279
// bind group should be pre-created and reuse instead of constantly initialized
8380
const bindGroup = device.createBindGroup({
8481
layout: pipeline.getBindGroupLayout(0),
85-
entries: [
86-
{ binding: 0, resource: { buffer: uniformBuffer }},
87-
],
82+
entries: [{ binding: 0, resource: { buffer: uniformBuffer } }],
8883
})
8984

90-
9185
pass.setPipeline(pipeline)
9286
pass.setVertexBuffer(0, vertexBuffer)
9387

@@ -96,6 +90,6 @@ export default function getProgram(
9690
device.queue.writeBuffer(uniformBuffer, 0, uniformValues)
9791

9892
pass.setBindGroup(0, bindGroup)
99-
pass.draw(numVertices)
93+
pass.draw(3, numInstances)
10094
}
10195
}
Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
const PI = 3.14159265358979323846;
2+
const EPSILON = 1.1920929e-7;
3+
14
struct Vertex {
2-
@location(0) position: vec4f,
3-
@location(1) color: vec4f,
5+
@location(0) p0: vec4f,
6+
@location(1) p1: vec4f,
7+
@location(2) p2: vec4f,
8+
@location(3) color: vec4f,
9+
@location(4) radius_list: vec3f,
410
};
511

612
struct Uniforms {
@@ -9,19 +15,93 @@ struct Uniforms {
915

1016
struct VertexOutput {
1117
@builtin(position) position: vec4f,
12-
@location(0) color: vec4f,
18+
@location(0) @interpolate(flat) p0: vec4f,
19+
@location(1) @interpolate(flat) p1: vec4f,
20+
@location(2) @interpolate(flat) p2: vec4f,
21+
@location(3) @interpolate(flat) color: vec4f,
22+
@location(4) pixel: vec2f,
23+
@location(5) @interpolate(flat) radius_list: vec3f,
24+
@location(6) @interpolate(flat) threshold_list: vec3f,
1325
};
1426

1527
@group(0) @binding(0) var<uniform> u: Uniforms;
1628

17-
@vertex fn vs(vert: Vertex) -> VertexOutput {
29+
@vertex fn vs(
30+
vert: Vertex,
31+
@builtin(vertex_index) vertexIndex : u32
32+
) -> VertexOutput {
33+
let normVertexIndex = vertexIndex % 3;
34+
1835
var out: VertexOutput;
19-
out.position = u.worldViewProjection * vert.position;
36+
if (normVertexIndex == 0) {
37+
out.position = vec4f(vert.p0.xy, 0, 1);
38+
} else if (normVertexIndex == 1) {
39+
out.position = vec4f(vert.p1.xy, 0, 1);
40+
} else {
41+
out.position = vec4f(vert.p2.xy, 0, 1);
42+
}
43+
44+
out.pixel = vec2f(out.position.x, out.position.y);
45+
46+
out.position = u.worldViewProjection * out.position;
47+
48+
out.p0 = vert.p0;
49+
out.p1 = vert.p1;
50+
out.p2 = vert.p2;
2051
out.color = vert.color;
21-
52+
out.radius_list = vert.radius_list;
53+
54+
let p0_circle_dist = distance(vert.p0.xy, vert.p0.zw);
55+
let p1_circle_dist = distance(vert.p1.xy, vert.p1.zw);
56+
let p2_circle_dist = distance(vert.p2.xy, vert.p2.zw);
57+
58+
out.threshold_list = vec3f(
59+
sqrt(pow(p0_circle_dist, 2) - pow(vert.radius_list.x, 2)),
60+
sqrt(pow(p1_circle_dist, 2) - pow(vert.radius_list.y, 2)),
61+
sqrt(pow(p2_circle_dist, 2) - pow(vert.radius_list.z, 2)),
62+
); // behind this value is roudned corner
63+
2264
return out;
2365
}
2466

2567
@fragment fn fs(in: VertexOutput) -> @location(0) vec4f {
26-
return in.color;
68+
let p0_circle_dist = distance(in.p0.xy, in.pixel) - in.threshold_list.x;
69+
let p1_circle_dist = distance(in.p1.xy, in.pixel) - in.threshold_list.y;
70+
let p2_circle_dist = distance(in.p2.xy, in.pixel) - in.threshold_list.z;
71+
72+
let min_circle_dist = min(
73+
p0_circle_dist,
74+
min(p1_circle_dist, p2_circle_dist)
75+
);
76+
77+
var p: vec2f; // closest corner
78+
var p_circle: vec2f; // closest corner's circle position
79+
var radius: f32;
80+
var threshold: f32;
81+
82+
if (abs(min_circle_dist - p0_circle_dist) <= EPSILON) {
83+
p = in.p0.xy;
84+
p_circle = in.p0.zw;
85+
radius = in.radius_list.x;
86+
threshold = in.threshold_list.x;
87+
} else if (abs(min_circle_dist - p1_circle_dist) <= EPSILON) {
88+
p = in.p1.xy;
89+
p_circle = in.p1.zw;
90+
radius = in.radius_list.y;
91+
threshold = in.threshold_list.y;
92+
} else {
93+
p = in.p2.xy;
94+
p_circle = in.p2.zw;
95+
radius = in.radius_list.z;
96+
threshold = in.threshold_list.z;
97+
}
98+
99+
let dist = distance(p, in.pixel);
100+
101+
if (dist < threshold) {
102+
let circle_distance = distance(p_circle, in.pixel);
103+
return in.color * (1.0 - step(radius, circle_distance));
104+
} else {
105+
return in.color;
106+
}
27107
}

0 commit comments

Comments
 (0)