@pexip/media-processor 20.3.5 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/api-docs/README.mdx +33 -6
- package/api-docs/functions/createBenchmark.mdx +17 -5
- package/api-docs/functions/createWindowedStats.mdx +23 -0
- package/api-docs/functions/iou.mdx +19 -0
- package/api-docs/functions/labelComponents.mdx +20 -0
- package/api-docs/functions/nearestPowerOfTwo.mdx +21 -0
- package/api-docs/functions/playVideo.mdx +16 -0
- package/api-docs/functions/scoreComponent.mdx +22 -0
- package/api-docs/functions/setVideoElementSrc.mdx +14 -0
- package/api-docs/interfaces/Benchmark.mdx +12 -0
- package/api-docs/interfaces/RendererOptions.mdx +26 -9
- package/api-docs/interfaces/SegmentationParams.mdx +29 -12
- package/api-docs/interfaces/SelectionOptions.mdx +15 -0
- package/api-docs/interfaces/Stats.mdx +11 -0
- package/api-docs/interfaces/Weights.mdx +12 -0
- package/api-docs/type-aliases/OptionalKeys.mdx +10 -0
- package/api-docs/variables/BACKGROUND_THRESHOLD.mdx +3 -0
- package/api-docs/variables/DOWN_SAMPLE_FACTOR.mdx +3 -0
- package/api-docs/variables/EDGE_BLUR_AMOUNT.mdx +1 -1
- package/api-docs/variables/EXCLUDE_BYSTANDERS.mdx +3 -0
- package/api-docs/variables/FOREGROUND_THRESHOLD.mdx +1 -1
- package/api-docs/variables/LIGHT_WRAP_BLUR_AMOUNT.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_EDGE_BAND.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_INTENSITY.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_TIGHTNESS.mdx +3 -0
- package/api-docs/variables/MASK_COMBINE_RATIO.mdx +1 -1
- package/api-docs/variables/MORPH_DILATE_RADIUS_PX.mdx +3 -0
- package/api-docs/variables/MORPH_ERODE_RADIUS_PX.mdx +3 -0
- package/api-docs/variables/MORPH_PASS.mdx +3 -0
- package/api-docs/variables/PERSON_CENTER.mdx +3 -0
- package/api-docs/variables/SIGMA_RANGE.mdx +3 -0
- package/api-docs/variables/SIGMA_SPACE.mdx +3 -0
- package/api-docs/variables/STRONG_EDGE_BLUR_AMOUNT.mdx +3 -0
- package/api-docs/variables/getCanvasRenderingContext2D.mdx +14 -0
- package/api-docs/variables/getImageSize.mdx +13 -0
- package/dist/common/backends/webgl/binaryMask.d.ts +8 -0
- package/dist/common/backends/webgl/binaryMask.js +95 -0
- package/dist/common/backends/webgl/blender.d.ts +2 -1
- package/dist/common/backends/webgl/blender.js +18 -15
- package/dist/common/backends/webgl/blur.d.ts +1 -1
- package/dist/common/backends/webgl/blur.js +23 -16
- package/dist/common/backends/webgl/canvas.js +9 -4
- package/dist/common/backends/webgl/diffAbsMask.d.ts +10 -0
- package/dist/common/backends/webgl/diffAbsMask.js +103 -0
- package/dist/common/backends/webgl/geodesicReconstruction.d.ts +13 -0
- package/dist/common/backends/webgl/geodesicReconstruction.js +176 -0
- package/dist/common/backends/webgl/jointBilateralFilter.d.ts +12 -0
- package/dist/common/backends/webgl/jointBilateralFilter.js +108 -0
- package/dist/common/backends/webgl/morphErode.d.ts +13 -0
- package/dist/common/backends/webgl/morphErode.js +121 -0
- package/dist/common/backends/webgl/renderer.js +194 -34
- package/dist/common/backends/webgl/replace.d.ts +17 -0
- package/dist/common/backends/webgl/replace.js +126 -0
- package/dist/common/backends/webgl/selectComponent.d.ts +15 -0
- package/dist/common/backends/webgl/selectComponent.js +102 -0
- package/dist/common/backends/webgl/smoothingMask.d.ts +3 -3
- package/dist/common/backends/webgl/smoothingMask.js +22 -21
- package/dist/common/backends/webgl/tentBlur.d.ts +9 -0
- package/dist/common/backends/webgl/tentBlur.js +118 -0
- package/dist/common/backends/webgl/texture.d.ts +1 -1
- package/dist/common/backends/webgl/texture.js +19 -17
- package/dist/common/backends/webgl/textureToTexture.d.ts +1 -1
- package/dist/common/backends/webgl/textureToTexture.js +19 -16
- package/dist/common/backends/webgl/webglUtils.d.ts +7 -1
- package/dist/common/backends/webgl/webglUtils.js +64 -6
- package/dist/common/backends/webgpu/blender.d.ts +6 -1
- package/dist/common/backends/webgpu/blender.js +96 -51
- package/dist/common/backends/webgpu/constants.js +1 -1
- package/dist/common/backends/webgpu/dualFilterBlur.d.ts +2 -3
- package/dist/common/backends/webgpu/dualFilterBlur.js +146 -140
- package/dist/common/backends/webgpu/jointBilateralFilter.d.ts +7 -0
- package/dist/common/backends/webgpu/jointBilateralFilter.js +162 -0
- package/dist/common/backends/webgpu/renderer.js +211 -39
- package/dist/common/backends/webgpu/replace.d.ts +11 -0
- package/dist/common/backends/webgpu/replace.js +175 -0
- package/dist/common/backends/webgpu/resize.d.ts +4 -0
- package/dist/common/backends/webgpu/resize.js +76 -0
- package/dist/common/backends/webgpu/smoothingMask.d.ts +2 -2
- package/dist/common/backends/webgpu/smoothingMask.js +74 -40
- package/dist/common/backends/webgpu/tentBlur.d.ts +5 -0
- package/dist/common/backends/webgpu/tentBlur.js +131 -0
- package/dist/common/backends/webgpu/texture.d.ts +2 -2
- package/dist/common/backends/webgpu/texture.js +24 -19
- package/dist/common/backends/webgpu/textureToTexture.d.ts +1 -1
- package/dist/common/backends/webgpu/textureToTexture.js +28 -15
- package/dist/common/backends/webgpu/types.d.ts +2 -0
- package/dist/common/backends/webgpu/webgpuUtils.d.ts +24 -4
- package/dist/common/backends/webgpu/webgpuUtils.js +60 -11
- package/dist/common/constants.d.ts +19 -5
- package/dist/common/constants.js +19 -5
- package/dist/common/index.d.ts +1 -0
- package/dist/common/index.js +1 -0
- package/dist/common/tsconfig.tsbuildinfo +1 -1
- package/dist/common/types/render.d.ts +142 -13
- package/dist/common/types/utils.d.ts +21 -0
- package/dist/common/utils.d.ts +16 -0
- package/dist/common/utils.js +152 -0
- package/dist/main/benchUtils.d.ts +33 -1
- package/dist/main/benchUtils.js +145 -11
- package/dist/main/tsconfig.tsbuildinfo +1 -1
- package/dist/main/video/canvasTransform.d.ts +1 -1
- package/dist/main/video/canvasTransform.js +135 -47
- package/dist/main/video/index.d.ts +1 -1
- package/dist/main/video/index.js +1 -1
- package/dist/workers/mediaWorker.js +35 -162
- package/dist/workers/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/dist/common/backends/webgl/shaders/blendFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/blendFragmentShader.js +0 -21
- package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.js +0 -45
- package/dist/common/backends/webgl/shaders/smoothingMaskFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/smoothingMaskFragmentShader.js +0 -57
- package/dist/common/backends/webgl/shaders/textureFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/textureFragmentShader.js +0 -16
- package/dist/common/backends/webgl/shaders/vertexShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/vertexShader.js +0 -19
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { makeShaderDataDefinitions, makeStructuredView } from 'webgpu-utils';
|
|
2
|
+
import { assert } from '@pexip/utils';
|
|
3
|
+
import { RENDERING_EVENTS } from '../../constants';
|
|
4
|
+
import jointBilateralFilterHorizontalShader from './shaders/jointBilateralFilterHorizontal.wgsl';
|
|
5
|
+
import jointBilateralFilterVerticalShader from './shaders/jointBilateralFilterVertical.wgsl';
|
|
6
|
+
import { updateTextureStorageFormat } from './webgpuUtils';
|
|
7
|
+
export const createJointBilateralFilterRenderer = (processor, device, isContextLost, width, height, maskFormat) => {
|
|
8
|
+
// Dispatch workgroups based on the shader's workgroup size (64x4)
|
|
9
|
+
const TILE_W = 32;
|
|
10
|
+
const TILE_ROWS = 4;
|
|
11
|
+
const TILE_H = 32;
|
|
12
|
+
const TILE_COLS = 4;
|
|
13
|
+
// Create intermediate texture for the filtered result
|
|
14
|
+
const tempTexture = device.createTexture({
|
|
15
|
+
label: 'Joint Bilateral Filtered temporary mask texture',
|
|
16
|
+
format: maskFormat,
|
|
17
|
+
size: [width, height],
|
|
18
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,
|
|
19
|
+
});
|
|
20
|
+
const outputTexture = device.createTexture({
|
|
21
|
+
label: 'Joint Bilateral Filtered Mask Texture',
|
|
22
|
+
format: maskFormat,
|
|
23
|
+
size: [width, height],
|
|
24
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,
|
|
25
|
+
});
|
|
26
|
+
const defs = makeShaderDataDefinitions(jointBilateralFilterHorizontalShader);
|
|
27
|
+
// @ts-expect-error This is what we get from the API
|
|
28
|
+
const uniforms = makeStructuredView(defs.uniforms.params);
|
|
29
|
+
// Create params buffer
|
|
30
|
+
const paramsBuffer = device.createBuffer({
|
|
31
|
+
size: uniforms.arrayBuffer.byteLength,
|
|
32
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
33
|
+
});
|
|
34
|
+
const scaleFactor = width / processor.width;
|
|
35
|
+
const texelWidth = 1 / width;
|
|
36
|
+
const texelHeight = 1 / height;
|
|
37
|
+
uniforms.set({
|
|
38
|
+
width,
|
|
39
|
+
height,
|
|
40
|
+
texelWidth,
|
|
41
|
+
texelHeight,
|
|
42
|
+
});
|
|
43
|
+
const updateParams = (params) => {
|
|
44
|
+
uniforms.set({
|
|
45
|
+
...params,
|
|
46
|
+
sigmaSpace: params.sigmaSpace * scaleFactor,
|
|
47
|
+
});
|
|
48
|
+
device.queue.writeBuffer(paramsBuffer, 0, uniforms.arrayBuffer);
|
|
49
|
+
};
|
|
50
|
+
// Generate shader code with appropriate format
|
|
51
|
+
const shaderHCode = updateTextureStorageFormat(jointBilateralFilterHorizontalShader, maskFormat);
|
|
52
|
+
const shaderVCode = updateTextureStorageFormat(jointBilateralFilterVerticalShader, maskFormat);
|
|
53
|
+
// Cache shader module and pipeline at init time
|
|
54
|
+
const shaderHModule = device.createShaderModule({
|
|
55
|
+
label: 'joint bilateral filter horizontal compute',
|
|
56
|
+
code: shaderHCode,
|
|
57
|
+
});
|
|
58
|
+
const shaderVModule = device.createShaderModule({
|
|
59
|
+
label: 'joint bilateral filter vertical compute',
|
|
60
|
+
code: shaderVCode,
|
|
61
|
+
});
|
|
62
|
+
const computeHPipeline = device.createComputePipeline({
|
|
63
|
+
label: 'joint bilateral filter horizontal pipeline',
|
|
64
|
+
layout: 'auto',
|
|
65
|
+
compute: { module: shaderHModule },
|
|
66
|
+
});
|
|
67
|
+
const computeVPipeline = device.createComputePipeline({
|
|
68
|
+
label: 'joint bilateral filter vertical pipeline',
|
|
69
|
+
layout: 'auto',
|
|
70
|
+
compute: { module: shaderVModule },
|
|
71
|
+
});
|
|
72
|
+
const uniformsHBindGroups = device.createBindGroup({
|
|
73
|
+
layout: computeHPipeline.getBindGroupLayout(1),
|
|
74
|
+
entries: [
|
|
75
|
+
{ binding: 0, resource: { buffer: paramsBuffer } },
|
|
76
|
+
{ binding: 1, resource: tempTexture.createView() },
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
const uniformsVBindGroups = device.createBindGroup({
|
|
80
|
+
layout: computeVPipeline.getBindGroupLayout(1),
|
|
81
|
+
entries: [
|
|
82
|
+
{ binding: 0, resource: { buffer: paramsBuffer } },
|
|
83
|
+
{ binding: 1, resource: outputTexture.createView() },
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
const compute = (label, encoder, pipeline, bindGroup0, bindGroup1, workgroupCountX, workgroupCountY) => {
|
|
87
|
+
const computePass = encoder.beginComputePass({ label });
|
|
88
|
+
computePass.setPipeline(pipeline);
|
|
89
|
+
computePass.setBindGroup(0, bindGroup0);
|
|
90
|
+
computePass.setBindGroup(1, bindGroup1);
|
|
91
|
+
computePass.dispatchWorkgroups(workgroupCountX, workgroupCountY);
|
|
92
|
+
computePass.end();
|
|
93
|
+
};
|
|
94
|
+
const wgCntXH = Math.ceil(width / TILE_W);
|
|
95
|
+
const wgCntYH = Math.ceil(height / TILE_ROWS);
|
|
96
|
+
const wgCntXV = Math.ceil(width / TILE_COLS);
|
|
97
|
+
const wgCntYV = Math.ceil(height / TILE_H);
|
|
98
|
+
let cacheMaskTexture = null;
|
|
99
|
+
let cacheGuideTexture = null;
|
|
100
|
+
let cacheBindGroupH = null;
|
|
101
|
+
let cacheBindGroupV = null;
|
|
102
|
+
const cache = {
|
|
103
|
+
sigmaSpace: -1,
|
|
104
|
+
sigmaRangeLo: -1,
|
|
105
|
+
sigmaRangeHi: -1,
|
|
106
|
+
backgroundThreshold: -1,
|
|
107
|
+
foregroundThreshold: -1,
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
render: (encoder, maskTexture, guideTexture, params) => {
|
|
111
|
+
assert(!isContextLost(), RENDERING_EVENTS.ContextLost);
|
|
112
|
+
if (!(cache.sigmaSpace === params.sigmaSpace &&
|
|
113
|
+
cache.sigmaRangeLo === params.sigmaRangeLo &&
|
|
114
|
+
cache.sigmaRangeHi === params.sigmaRangeHi &&
|
|
115
|
+
cache.backgroundThreshold === params.backgroundThreshold &&
|
|
116
|
+
cache.foregroundThreshold === params.foregroundThreshold)) {
|
|
117
|
+
updateParams(params);
|
|
118
|
+
cache.sigmaSpace = params.sigmaSpace;
|
|
119
|
+
cache.sigmaRangeLo = params.sigmaRangeLo;
|
|
120
|
+
cache.sigmaRangeHi = params.sigmaRangeHi;
|
|
121
|
+
cache.backgroundThreshold = params.backgroundThreshold;
|
|
122
|
+
cache.foregroundThreshold = params.foregroundThreshold;
|
|
123
|
+
}
|
|
124
|
+
// Horizontal
|
|
125
|
+
if (cacheMaskTexture !== maskTexture ||
|
|
126
|
+
cacheGuideTexture !== guideTexture) {
|
|
127
|
+
cacheBindGroupH = device.createBindGroup({
|
|
128
|
+
layout: computeHPipeline.getBindGroupLayout(0),
|
|
129
|
+
entries: [
|
|
130
|
+
{ binding: 0, resource: maskTexture.createView() },
|
|
131
|
+
{ binding: 1, resource: guideTexture.createView() },
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
cacheMaskTexture = maskTexture;
|
|
135
|
+
}
|
|
136
|
+
// Horizontal pipeline
|
|
137
|
+
compute('Joint Bilateral Filter Horizontal pass', encoder, computeHPipeline, cacheBindGroupH, uniformsHBindGroups, wgCntXH, wgCntYH);
|
|
138
|
+
// Vertical
|
|
139
|
+
if (cacheGuideTexture !== guideTexture) {
|
|
140
|
+
cacheBindGroupV = device.createBindGroup({
|
|
141
|
+
layout: computeVPipeline.getBindGroupLayout(0),
|
|
142
|
+
entries: [
|
|
143
|
+
{ binding: 0, resource: tempTexture.createView() },
|
|
144
|
+
{ binding: 1, resource: guideTexture.createView() },
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
cacheGuideTexture = guideTexture;
|
|
148
|
+
}
|
|
149
|
+
compute('Joint Bilateral Filter Vertical pass', encoder, computeVPipeline, cacheBindGroupV, uniformsVBindGroups, wgCntXV, wgCntYV);
|
|
150
|
+
return outputTexture;
|
|
151
|
+
},
|
|
152
|
+
release: () => {
|
|
153
|
+
tempTexture.destroy();
|
|
154
|
+
outputTexture.destroy();
|
|
155
|
+
paramsBuffer.destroy();
|
|
156
|
+
cacheMaskTexture = null;
|
|
157
|
+
cacheGuideTexture = null;
|
|
158
|
+
cacheBindGroupH = null;
|
|
159
|
+
cacheBindGroupV = null;
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
};
|
|
@@ -1,43 +1,95 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assert } from '@pexip/utils';
|
|
2
|
+
import { PROCESSING_WIDTH, PROCESSING_HEIGHT, STRONG_EDGE_BLUR_AMOUNT, } from '../../constants';
|
|
2
3
|
import { createObjectUpdater, createLazyProps } from '../../utils';
|
|
3
4
|
import { createTextureRenderer } from './texture';
|
|
4
5
|
import { createBlendRenderer } from './blender';
|
|
5
|
-
import { getGPUDevice,
|
|
6
|
+
import { getGPUDevice, hasR16FloatTextureStorage, supportsTextureFormat, hasF16, } from './webgpuUtils';
|
|
6
7
|
import { createBlurRenderer } from './dualFilterBlur';
|
|
7
8
|
import { createSmoothingMaskRenderer } from './smoothingMask';
|
|
8
9
|
import { createTextureToTextureRenderer } from './textureToTexture';
|
|
10
|
+
import { createJointBilateralFilterRenderer } from './jointBilateralFilter';
|
|
11
|
+
import { createTextureResizeRenderer } from './resize';
|
|
12
|
+
import { createTentBlurRenderer } from './tentBlur';
|
|
13
|
+
import { createReplaceRenderer } from './replace';
|
|
9
14
|
export const createRenderer = (adapter, renderEventHandlers, canvas = new OffscreenCanvas(PROCESSING_WIDTH, PROCESSING_HEIGHT)) => {
|
|
15
|
+
const halfWidth = Math.trunc(canvas.width / 2);
|
|
16
|
+
const halfHeight = Math.trunc(canvas.height / 2);
|
|
17
|
+
const quarterWidth = Math.trunc(canvas.width / 4);
|
|
18
|
+
const quarterHeight = Math.trunc(canvas.height / 4);
|
|
19
|
+
const fullChannelTextureFormat = 'rgba8unorm';
|
|
10
20
|
const rendererCreator = {
|
|
21
|
+
edgeBlurRenderer: () => {
|
|
22
|
+
assert(props.device);
|
|
23
|
+
return createTentBlurRenderer(props.device, isContextLost, canvas.width, canvas.height, props.maskFormat, props.supportF16);
|
|
24
|
+
},
|
|
11
25
|
videoFrameRenderer: () => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
26
|
+
assert(props.device);
|
|
27
|
+
return createTextureRenderer(props.device, isContextLost, fullChannelTextureFormat, canvas.width, canvas.height, 'linear');
|
|
28
|
+
},
|
|
29
|
+
maskRenderer: () => {
|
|
30
|
+
assert(props.device);
|
|
31
|
+
return createTextureRenderer(props.device, isContextLost, props.maskFormat, canvas.width, canvas.height, 'nearest');
|
|
32
|
+
},
|
|
33
|
+
maskDownsampler2: () => {
|
|
34
|
+
assert(props.device);
|
|
35
|
+
return createTextureResizeRenderer(props.device, isContextLost, halfWidth, halfHeight, props.maskFormat, 'nearest');
|
|
36
|
+
},
|
|
37
|
+
maskDownsampler4: () => {
|
|
38
|
+
assert(props.device);
|
|
39
|
+
return createTextureResizeRenderer(props.device, isContextLost, quarterWidth, quarterHeight, props.maskFormat, 'nearest');
|
|
40
|
+
},
|
|
41
|
+
maskUpsampler: () => {
|
|
42
|
+
assert(props.device);
|
|
43
|
+
return createTextureResizeRenderer(props.device, isContextLost, canvas.width, canvas.height, props.maskFormat, 'nearest');
|
|
44
|
+
},
|
|
45
|
+
frameDownsampler2: () => {
|
|
46
|
+
assert(props.device);
|
|
47
|
+
return createTextureResizeRenderer(props.device, isContextLost, halfWidth, halfHeight, fullChannelTextureFormat, 'linear');
|
|
48
|
+
},
|
|
49
|
+
frameDownsampler4: () => {
|
|
50
|
+
assert(props.device);
|
|
51
|
+
return createTextureResizeRenderer(props.device, isContextLost, quarterWidth, quarterHeight, fullChannelTextureFormat, 'linear');
|
|
16
52
|
},
|
|
17
53
|
backgroundImageRenderer: () => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
54
|
+
assert(props.device);
|
|
55
|
+
return createTextureRenderer(props.device, isContextLost, fullChannelTextureFormat, canvas.width, canvas.height, 'linear');
|
|
56
|
+
},
|
|
57
|
+
blendRenderer: () => {
|
|
58
|
+
assert(props.device);
|
|
21
59
|
return createBlendRenderer(canvas, props.device, props.presentationFormat, isContextLost);
|
|
22
60
|
},
|
|
23
61
|
blurRenderer: () => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
return createBlurRenderer(canvas, props.device, props.presentationFormat, isContextLost);
|
|
62
|
+
assert(props.device);
|
|
63
|
+
return createBlurRenderer(props.device, canvas.width, canvas.height, fullChannelTextureFormat, isContextLost);
|
|
28
64
|
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
65
|
+
jointBilateralFilterRenderer2: () => {
|
|
66
|
+
assert(props.device);
|
|
67
|
+
return createJointBilateralFilterRenderer(canvas, props.device, isContextLost, halfWidth, halfHeight, props.maskFormat);
|
|
68
|
+
},
|
|
69
|
+
jointBilateralFilterRenderer4: () => {
|
|
70
|
+
assert(props.device);
|
|
71
|
+
return createJointBilateralFilterRenderer(canvas, props.device, isContextLost, quarterWidth, quarterHeight, props.maskFormat);
|
|
72
|
+
},
|
|
73
|
+
maskSmoothingRenderer2: () => {
|
|
74
|
+
assert(props.device);
|
|
75
|
+
return createSmoothingMaskRenderer(props.device, isContextLost, props.maskFormat, halfWidth, halfHeight, props.supportF16);
|
|
76
|
+
},
|
|
77
|
+
maskSmoothingRenderer4: () => {
|
|
78
|
+
assert(props.device);
|
|
79
|
+
return createSmoothingMaskRenderer(props.device, isContextLost, props.maskFormat, quarterWidth, quarterHeight, props.supportF16);
|
|
34
80
|
},
|
|
35
81
|
prevMaskRenderer: () => {
|
|
36
|
-
|
|
37
|
-
throw new Error('No device.');
|
|
38
|
-
}
|
|
82
|
+
assert(props.device);
|
|
39
83
|
return createTextureToTextureRenderer(props.device, isContextLost);
|
|
40
84
|
},
|
|
85
|
+
strongMaskBlurRenderer: () => {
|
|
86
|
+
assert(props.device);
|
|
87
|
+
return createBlurRenderer(props.device, canvas.width, canvas.height, props.maskFormat, isContextLost);
|
|
88
|
+
},
|
|
89
|
+
replaceRenderer: () => {
|
|
90
|
+
assert(props.device);
|
|
91
|
+
return createReplaceRenderer(canvas, props.device, props.presentationFormat, isContextLost);
|
|
92
|
+
},
|
|
41
93
|
release: (states) => () => {
|
|
42
94
|
for (const key of Object.keys(states)) {
|
|
43
95
|
key !== 'release' && states[key]?.release();
|
|
@@ -48,33 +100,80 @@ export const createRenderer = (adapter, renderEventHandlers, canvas = new Offscr
|
|
|
48
100
|
const props = {
|
|
49
101
|
canvas,
|
|
50
102
|
presentationFormat: 'rgba8unorm',
|
|
103
|
+
supportR16FloatTextureStorage: false,
|
|
104
|
+
maskFormat: 'rgba16float',
|
|
105
|
+
supportF16: false,
|
|
51
106
|
backend: 'webgpu',
|
|
52
107
|
deviceLost: false,
|
|
53
108
|
get videoFrameRenderer() {
|
|
54
109
|
return lazyProps.videoFrameRenderer;
|
|
55
110
|
},
|
|
111
|
+
get edgeBlurRenderer() {
|
|
112
|
+
return lazyProps.edgeBlurRenderer;
|
|
113
|
+
},
|
|
114
|
+
get maskRenderer() {
|
|
115
|
+
return lazyProps.maskRenderer;
|
|
116
|
+
},
|
|
117
|
+
get maskDownsampler2() {
|
|
118
|
+
return lazyProps.maskDownsampler2;
|
|
119
|
+
},
|
|
120
|
+
get maskDownsampler4() {
|
|
121
|
+
return lazyProps.maskDownsampler4;
|
|
122
|
+
},
|
|
123
|
+
get maskUpsampler() {
|
|
124
|
+
return lazyProps.maskUpsampler;
|
|
125
|
+
},
|
|
126
|
+
get frameDownsampler2() {
|
|
127
|
+
return lazyProps.frameDownsampler2;
|
|
128
|
+
},
|
|
129
|
+
get frameDownsampler4() {
|
|
130
|
+
return lazyProps.frameDownsampler4;
|
|
131
|
+
},
|
|
56
132
|
get blendRenderer() {
|
|
57
|
-
return lazyProps.
|
|
133
|
+
return lazyProps.blendRenderer;
|
|
58
134
|
},
|
|
59
135
|
get blurRenderer() {
|
|
60
136
|
return lazyProps.blurRenderer;
|
|
61
137
|
},
|
|
62
|
-
get
|
|
63
|
-
return lazyProps.
|
|
138
|
+
get backgroundImageRenderer() {
|
|
139
|
+
return lazyProps.backgroundImageRenderer;
|
|
140
|
+
},
|
|
141
|
+
get maskSmoothingRenderer2() {
|
|
142
|
+
return lazyProps.maskSmoothingRenderer2;
|
|
143
|
+
},
|
|
144
|
+
get maskSmoothingRenderer4() {
|
|
145
|
+
return lazyProps.maskSmoothingRenderer4;
|
|
64
146
|
},
|
|
65
147
|
get prevMaskRenderer() {
|
|
66
148
|
return lazyProps.prevMaskRenderer;
|
|
67
149
|
},
|
|
150
|
+
get jointBilateralFilterRenderer2() {
|
|
151
|
+
return lazyProps.jointBilateralFilterRenderer2;
|
|
152
|
+
},
|
|
153
|
+
get jointBilateralFilterRenderer4() {
|
|
154
|
+
return lazyProps.jointBilateralFilterRenderer4;
|
|
155
|
+
},
|
|
156
|
+
get strongMaskBlurRenderer() {
|
|
157
|
+
return lazyProps.strongMaskBlurRenderer;
|
|
158
|
+
},
|
|
159
|
+
get replaceRenderer() {
|
|
160
|
+
return lazyProps.replaceRenderer;
|
|
161
|
+
},
|
|
68
162
|
};
|
|
69
163
|
const updateProps = createObjectUpdater(props);
|
|
70
164
|
const init = async () => {
|
|
71
165
|
if (!navigator.gpu) {
|
|
72
166
|
throw Error('WebGPU not supported.');
|
|
73
167
|
}
|
|
74
|
-
props.presentationFormat =
|
|
75
|
-
? navigator.gpu.getPreferredCanvasFormat()
|
|
76
|
-
: 'rgba8unorm';
|
|
168
|
+
props.presentationFormat = navigator.gpu.getPreferredCanvasFormat();
|
|
77
169
|
props.device = await getGPUDevice(adapter);
|
|
170
|
+
props.supportR16FloatTextureStorage =
|
|
171
|
+
hasR16FloatTextureStorage(adapter) &&
|
|
172
|
+
supportsTextureFormat(props.device, 'r16float');
|
|
173
|
+
if (props.supportR16FloatTextureStorage) {
|
|
174
|
+
props.maskFormat = 'r16float';
|
|
175
|
+
}
|
|
176
|
+
props.supportF16 = hasF16(adapter);
|
|
78
177
|
props.device.lost.then(info => {
|
|
79
178
|
if (info.reason === 'destroyed') {
|
|
80
179
|
return;
|
|
@@ -88,34 +187,107 @@ export const createRenderer = (adapter, renderEventHandlers, canvas = new Offscr
|
|
|
88
187
|
if (options) {
|
|
89
188
|
updateProps(options);
|
|
90
189
|
}
|
|
91
|
-
|
|
92
|
-
const
|
|
190
|
+
assert(props.device);
|
|
191
|
+
const encoder = props.device.createCommandEncoder({
|
|
192
|
+
label: 'Renderer Encoder',
|
|
193
|
+
});
|
|
194
|
+
const frameUploadPass = encoder.beginComputePass({
|
|
195
|
+
label: 'frame upload pass',
|
|
196
|
+
});
|
|
197
|
+
const frameTexture = props.videoFrameRenderer.render(frame, frameUploadPass);
|
|
198
|
+
const maskTexture = props.maskRenderer.render({
|
|
93
199
|
frame: mask,
|
|
94
|
-
width:
|
|
95
|
-
height:
|
|
200
|
+
width: mask.width,
|
|
201
|
+
height: mask.height,
|
|
96
202
|
timestamp: frame.timestamp,
|
|
97
|
-
});
|
|
98
|
-
|
|
203
|
+
}, frameUploadPass);
|
|
204
|
+
frameUploadPass.end();
|
|
205
|
+
let smoothedMask;
|
|
206
|
+
let compositeInputMask;
|
|
207
|
+
switch (options.downSampleFactor) {
|
|
208
|
+
case 4: {
|
|
209
|
+
const downsamplePass = encoder.beginComputePass({
|
|
210
|
+
label: 'downsample pass',
|
|
211
|
+
});
|
|
212
|
+
const maskLowRes = props.maskDownsampler4.render(downsamplePass, maskTexture);
|
|
213
|
+
const frameLowRes = props.frameDownsampler4.render(downsamplePass, frameTexture);
|
|
214
|
+
downsamplePass.end();
|
|
215
|
+
const filteredMask = props.jointBilateralFilterRenderer4.render(encoder, maskLowRes, frameLowRes, options);
|
|
216
|
+
smoothedMask = props.maskSmoothingRenderer4.render(encoder, filteredMask, props.prevMask4 ?? filteredMask, options);
|
|
217
|
+
const upsamplePass = encoder.beginComputePass({
|
|
218
|
+
label: 'upsample pass',
|
|
219
|
+
});
|
|
220
|
+
const upsampledMask = props.maskUpsampler.render(upsamplePass, smoothedMask);
|
|
221
|
+
upsamplePass.end();
|
|
222
|
+
compositeInputMask = props.strongMaskBlurRenderer.render(encoder, upsampledMask, STRONG_EDGE_BLUR_AMOUNT);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
default: {
|
|
226
|
+
const downsamplePass = encoder.beginComputePass({
|
|
227
|
+
label: 'downsample pass',
|
|
228
|
+
});
|
|
229
|
+
const maskLowRes = props.maskDownsampler2.render(downsamplePass, maskTexture);
|
|
230
|
+
const frameLowRes = props.frameDownsampler2.render(downsamplePass, frameTexture);
|
|
231
|
+
downsamplePass.end();
|
|
232
|
+
const filteredMask = props.jointBilateralFilterRenderer2.render(encoder, maskLowRes, frameLowRes, options);
|
|
233
|
+
smoothedMask = props.maskSmoothingRenderer2.render(encoder, filteredMask, props.prevMask2 ?? filteredMask, options);
|
|
234
|
+
const upsamplePass = encoder.beginComputePass({
|
|
235
|
+
label: 'upsample pass',
|
|
236
|
+
});
|
|
237
|
+
const upsampledMask = props.maskUpsampler.render(upsamplePass, smoothedMask);
|
|
238
|
+
upsamplePass.end();
|
|
239
|
+
compositeInputMask = props.edgeBlurRenderer.render(encoder, upsampledMask, options);
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
99
243
|
if (options.effects === 'blur') {
|
|
100
|
-
const blurredTexture = props.blurRenderer.render(
|
|
101
|
-
props.blendRenderer.render(
|
|
244
|
+
const blurredTexture = props.blurRenderer.render(encoder, frameTexture, options.backgroundBlurAmount);
|
|
245
|
+
props.blendRenderer.render(encoder, {
|
|
246
|
+
foreground: frameTexture,
|
|
247
|
+
background: blurredTexture,
|
|
248
|
+
mask: compositeInputMask,
|
|
249
|
+
}, options);
|
|
102
250
|
}
|
|
103
251
|
else if (options.effects === 'overlay') {
|
|
104
252
|
if (options.backgroundImage) {
|
|
105
|
-
const
|
|
253
|
+
const bgImgUploadPass = encoder.beginComputePass({
|
|
254
|
+
label: 'background image upload pass',
|
|
255
|
+
});
|
|
256
|
+
const bgTexture = props.backgroundImageRenderer.render({
|
|
106
257
|
frame: options.backgroundImage,
|
|
107
258
|
width: options.backgroundImage.width,
|
|
108
259
|
height: options.backgroundImage.height,
|
|
109
260
|
timestamp: frame.timestamp,
|
|
110
|
-
});
|
|
111
|
-
|
|
261
|
+
}, bgImgUploadPass);
|
|
262
|
+
bgImgUploadPass.end();
|
|
263
|
+
const blurredBackgroundImageTexture = props.blurRenderer.render(encoder, bgTexture, options.backgroundBlurAmount);
|
|
264
|
+
props.replaceRenderer.render(encoder, {
|
|
265
|
+
foreground: frameTexture,
|
|
266
|
+
background: bgTexture,
|
|
267
|
+
mask: compositeInputMask,
|
|
268
|
+
blurBackground: blurredBackgroundImageTexture,
|
|
269
|
+
timestamp: frame.timestamp,
|
|
270
|
+
}, options);
|
|
112
271
|
}
|
|
113
272
|
}
|
|
114
|
-
|
|
273
|
+
switch (options.downSampleFactor) {
|
|
274
|
+
case 4:
|
|
275
|
+
props.prevMask4 = props.prevMaskRenderer.render(encoder, smoothedMask);
|
|
276
|
+
break;
|
|
277
|
+
default:
|
|
278
|
+
props.prevMask2 = props.prevMaskRenderer.render(encoder, smoothedMask);
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
props.device.queue.submit([encoder.finish()]);
|
|
115
282
|
};
|
|
116
283
|
const release = () => {
|
|
117
284
|
lazyProps.release();
|
|
118
285
|
props.deviceLost = false;
|
|
286
|
+
props.prevMask2 = undefined;
|
|
287
|
+
props.prevMask4 = undefined;
|
|
288
|
+
props.presentationFormat = 'rgba8unorm';
|
|
289
|
+
props.supportR16FloatTextureStorage = false;
|
|
290
|
+
props.maskFormat = 'rgba16float';
|
|
119
291
|
lazyProps = createLazyProps(rendererCreator);
|
|
120
292
|
};
|
|
121
293
|
const isContextLost = () => props.deviceLost;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RendererOptions } from '../../types/render';
|
|
2
|
+
export declare const createReplaceRenderer: (processor: OffscreenCanvas, device: GPUDevice, presentationFormat: GPUTextureFormat, isContextLost: () => boolean) => {
|
|
3
|
+
render: (encoder: GPUCommandEncoder, { foreground, background, blurBackground, mask, timestamp, }: {
|
|
4
|
+
foreground: GPUTexture;
|
|
5
|
+
background: GPUTexture;
|
|
6
|
+
blurBackground: GPUTexture;
|
|
7
|
+
mask: GPUTexture;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}, options: RendererOptions) => void;
|
|
10
|
+
release: () => void;
|
|
11
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { makeShaderDataDefinitions, makeStructuredView } from 'webgpu-utils';
|
|
2
|
+
import { assert } from '@pexip/utils';
|
|
3
|
+
import { RENDERING_EVENTS } from '../../constants';
|
|
4
|
+
import shaderCode from './shaders/replace.wgsl';
|
|
5
|
+
export const createReplaceRenderer = (processor, device, presentationFormat, isContextLost) => {
|
|
6
|
+
const context = processor.getContext('webgpu');
|
|
7
|
+
if (!context) {
|
|
8
|
+
throw Error('WebGPU not supported on the canvas.');
|
|
9
|
+
}
|
|
10
|
+
context.configure({
|
|
11
|
+
device,
|
|
12
|
+
format: presentationFormat,
|
|
13
|
+
alphaMode: 'premultiplied',
|
|
14
|
+
});
|
|
15
|
+
const defs = makeShaderDataDefinitions(shaderCode);
|
|
16
|
+
// @ts-expect-error This is what we get from the API
|
|
17
|
+
const uniformsSparams = makeStructuredView(defs.uniforms.sparams);
|
|
18
|
+
// @ts-expect-error This is what we get from the API
|
|
19
|
+
const uniformsDparams = makeStructuredView(defs.uniforms.dparams);
|
|
20
|
+
// Cache shader module and pipeline at init time
|
|
21
|
+
const shaderModule = device.createShaderModule({
|
|
22
|
+
label: 'replace shader',
|
|
23
|
+
code: shaderCode,
|
|
24
|
+
});
|
|
25
|
+
const pipeline = device.createRenderPipeline({
|
|
26
|
+
label: 'replace pipeline',
|
|
27
|
+
layout: 'auto',
|
|
28
|
+
vertex: {
|
|
29
|
+
module: shaderModule,
|
|
30
|
+
},
|
|
31
|
+
fragment: {
|
|
32
|
+
module: shaderModule,
|
|
33
|
+
targets: [{ format: presentationFormat }],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
// Cache sampler at init time
|
|
37
|
+
const linearSampler = device.createSampler({
|
|
38
|
+
addressModeU: 'clamp-to-edge',
|
|
39
|
+
addressModeV: 'clamp-to-edge',
|
|
40
|
+
magFilter: 'linear',
|
|
41
|
+
minFilter: 'linear',
|
|
42
|
+
});
|
|
43
|
+
const nearestSampler = device.createSampler({
|
|
44
|
+
addressModeU: 'clamp-to-edge',
|
|
45
|
+
addressModeV: 'clamp-to-edge',
|
|
46
|
+
magFilter: 'nearest',
|
|
47
|
+
minFilter: 'nearest',
|
|
48
|
+
});
|
|
49
|
+
const sparamsBuffer = device.createBuffer({
|
|
50
|
+
size: uniformsSparams.arrayBuffer.byteLength,
|
|
51
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
52
|
+
});
|
|
53
|
+
const dparamsBuffer = device.createBuffer({
|
|
54
|
+
size: uniformsDparams.arrayBuffer.byteLength,
|
|
55
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
56
|
+
});
|
|
57
|
+
const updateSparams = (options) => {
|
|
58
|
+
uniformsSparams.set({
|
|
59
|
+
background: options.backgroundThreshold,
|
|
60
|
+
foreground: options.foregroundThreshold,
|
|
61
|
+
lightWrap: options.lightWrapIntensity,
|
|
62
|
+
lightWrapTightness: options.lightWrapTightness,
|
|
63
|
+
lightWrapEdgeBand: options.lightWrapEdgeBand,
|
|
64
|
+
width: options.width,
|
|
65
|
+
height: options.height,
|
|
66
|
+
});
|
|
67
|
+
device.queue.writeBuffer(sparamsBuffer, 0, uniformsSparams.arrayBuffer);
|
|
68
|
+
};
|
|
69
|
+
const updateDparams = ({ time, parity }) => {
|
|
70
|
+
uniformsDparams.set({ time, parity });
|
|
71
|
+
device.queue.writeBuffer(dparamsBuffer, 0, uniformsDparams.arrayBuffer);
|
|
72
|
+
};
|
|
73
|
+
const colorAttachment0 = {
|
|
74
|
+
loadOp: 'load',
|
|
75
|
+
storeOp: 'store',
|
|
76
|
+
};
|
|
77
|
+
const renderPassDescriptor = {
|
|
78
|
+
label: 'replace renderPass',
|
|
79
|
+
colorAttachments: [colorAttachment0],
|
|
80
|
+
};
|
|
81
|
+
const bindGroup0 = device.createBindGroup({
|
|
82
|
+
layout: pipeline.getBindGroupLayout(0),
|
|
83
|
+
entries: [
|
|
84
|
+
{ binding: 0, resource: linearSampler },
|
|
85
|
+
{ binding: 1, resource: nearestSampler },
|
|
86
|
+
{ binding: 2, resource: { buffer: sparamsBuffer } },
|
|
87
|
+
{ binding: 3, resource: { buffer: dparamsBuffer } },
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
let cachedBindGroup1 = null;
|
|
91
|
+
let cachedMask = null;
|
|
92
|
+
let cachedBackground = null;
|
|
93
|
+
let cachedForeground = null;
|
|
94
|
+
let cachedBlurBackground = null;
|
|
95
|
+
let cachedBackgroundThreshold = -1;
|
|
96
|
+
let cachedForegroundThreshold = -1;
|
|
97
|
+
let cachedLightWrapIntensity = -1;
|
|
98
|
+
let cachedLightWrapTightness = -1;
|
|
99
|
+
let cachedLightWrapEdgeBand = -1;
|
|
100
|
+
let cachedWidth = -1;
|
|
101
|
+
let cachedHeight = -1;
|
|
102
|
+
let parity = -1;
|
|
103
|
+
return {
|
|
104
|
+
render: (encoder, { foreground, background, blurBackground, mask, timestamp, }, options) => {
|
|
105
|
+
assert(!isContextLost(), RENDERING_EVENTS.ContextLost);
|
|
106
|
+
colorAttachment0.view = context.getCurrentTexture().createView();
|
|
107
|
+
if (!(cachedBackground === background &&
|
|
108
|
+
cachedForeground === foreground &&
|
|
109
|
+
cachedMask === mask &&
|
|
110
|
+
cachedBlurBackground === blurBackground)) {
|
|
111
|
+
cachedBindGroup1 = device.createBindGroup({
|
|
112
|
+
layout: pipeline.getBindGroupLayout(1),
|
|
113
|
+
entries: [
|
|
114
|
+
{ binding: 0, resource: foreground.createView() },
|
|
115
|
+
{ binding: 1, resource: background.createView() },
|
|
116
|
+
{ binding: 2, resource: mask.createView() },
|
|
117
|
+
{ binding: 3, resource: blurBackground.createView() },
|
|
118
|
+
],
|
|
119
|
+
});
|
|
120
|
+
cachedForeground = foreground;
|
|
121
|
+
cachedBackground = background;
|
|
122
|
+
cachedMask = mask;
|
|
123
|
+
cachedBlurBackground = blurBackground;
|
|
124
|
+
}
|
|
125
|
+
if (!(cachedBackgroundThreshold === options.backgroundThreshold &&
|
|
126
|
+
cachedForegroundThreshold === options.foregroundThreshold &&
|
|
127
|
+
cachedLightWrapIntensity === options.lightWrapIntensity &&
|
|
128
|
+
cachedLightWrapTightness === options.lightWrapTightness &&
|
|
129
|
+
cachedLightWrapEdgeBand === options.lightWrapEdgeBand &&
|
|
130
|
+
cachedWidth === background.width &&
|
|
131
|
+
cachedHeight === background.height)) {
|
|
132
|
+
updateSparams({
|
|
133
|
+
backgroundThreshold: options.backgroundThreshold,
|
|
134
|
+
foregroundThreshold: options.foregroundThreshold,
|
|
135
|
+
lightWrapIntensity: options.lightWrapIntensity,
|
|
136
|
+
lightWrapTightness: options.lightWrapTightness,
|
|
137
|
+
lightWrapEdgeBand: options.lightWrapEdgeBand,
|
|
138
|
+
width: background.width,
|
|
139
|
+
height: background.height,
|
|
140
|
+
});
|
|
141
|
+
cachedBackgroundThreshold = options.backgroundThreshold;
|
|
142
|
+
cachedForegroundThreshold = options.foregroundThreshold;
|
|
143
|
+
cachedLightWrapIntensity = options.lightWrapIntensity;
|
|
144
|
+
cachedLightWrapTightness = options.lightWrapTightness;
|
|
145
|
+
cachedLightWrapEdgeBand = options.lightWrapEdgeBand;
|
|
146
|
+
cachedWidth = background.width;
|
|
147
|
+
cachedHeight = background.height;
|
|
148
|
+
}
|
|
149
|
+
parity = -parity;
|
|
150
|
+
updateDparams({ time: (timestamp * 1e-6) % 1024.0, parity });
|
|
151
|
+
const pass = encoder.beginRenderPass(renderPassDescriptor);
|
|
152
|
+
pass.setPipeline(pipeline);
|
|
153
|
+
pass.setBindGroup(0, bindGroup0);
|
|
154
|
+
pass.setBindGroup(1, cachedBindGroup1);
|
|
155
|
+
pass.draw(6); // call our vertex shader 6 times
|
|
156
|
+
pass.end();
|
|
157
|
+
},
|
|
158
|
+
release: () => {
|
|
159
|
+
sparamsBuffer.destroy();
|
|
160
|
+
dparamsBuffer.destroy();
|
|
161
|
+
cachedBackgroundThreshold = -1;
|
|
162
|
+
cachedForegroundThreshold = -1;
|
|
163
|
+
cachedLightWrapIntensity = -1;
|
|
164
|
+
cachedLightWrapTightness = -1;
|
|
165
|
+
cachedLightWrapEdgeBand = -1;
|
|
166
|
+
cachedWidth = -1;
|
|
167
|
+
cachedHeight = -1;
|
|
168
|
+
cachedBlurBackground = null;
|
|
169
|
+
cachedBackground = null;
|
|
170
|
+
cachedForeground = null;
|
|
171
|
+
cachedMask = null;
|
|
172
|
+
cachedBindGroup1 = null;
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const createTextureResizeRenderer: (device: GPUDevice, isContextLost: () => boolean, width: number, height: number, textureFormat: GPUTextureFormat, filterMode: GPUFilterMode) => {
|
|
2
|
+
render(pass: GPUComputePassEncoder, texture: GPUTexture): GPUTexture;
|
|
3
|
+
release(): void;
|
|
4
|
+
};
|