@luma.gl/webgpu 9.2.5 → 9.3.0-alpha.10
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/dist/adapter/helpers/cpu-hotspot-profiler.d.ts +54 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts.map +1 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.js +26 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.js.map +1 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts +7 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts.map +1 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.js +490 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.js.map +1 -0
- package/dist/adapter/helpers/get-bind-group.d.ts +4 -6
- package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
- package/dist/adapter/helpers/get-bind-group.js +39 -32
- package/dist/adapter/helpers/get-bind-group.js.map +1 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +3 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.js +17 -12
- package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -1
- package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
- package/dist/adapter/helpers/webgpu-parameters.js +1 -0
- package/dist/adapter/helpers/webgpu-parameters.js.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.d.ts +7 -0
- package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.js +58 -15
- package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
- package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
- package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
- package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
- package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
- package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
- package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
- package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
- package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
- package/dist/adapter/resources/webgpu-fence.js +33 -0
- package/dist/adapter/resources/webgpu-fence.js.map +1 -0
- package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
- package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
- package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.js +11 -18
- package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
- package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
- package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-query-set.js +145 -4
- package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
- package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.js +78 -34
- package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.js +56 -35
- package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-sampler.js +4 -0
- package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
- package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-shader.js +17 -1
- package/dist/adapter/resources/webgpu-shader.js.map +1 -1
- package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
- package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture-view.js +47 -11
- package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
- package/dist/adapter/resources/webgpu-texture.d.ts +25 -3
- package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture.js +211 -43
- package/dist/adapter/resources/webgpu-texture.js.map +1 -1
- package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
- package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
- package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
- package/dist/adapter/webgpu-adapter.js +34 -34
- package/dist/adapter/webgpu-adapter.js.map +1 -1
- package/dist/adapter/webgpu-canvas-context.d.ts +6 -3
- package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
- package/dist/adapter/webgpu-canvas-context.js +90 -30
- package/dist/adapter/webgpu-canvas-context.js.map +1 -1
- package/dist/adapter/webgpu-device.d.ts +12 -2
- package/dist/adapter/webgpu-device.d.ts.map +1 -1
- package/dist/adapter/webgpu-device.js +173 -16
- package/dist/adapter/webgpu-device.js.map +1 -1
- package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
- package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
- package/dist/adapter/webgpu-presentation-context.js +144 -0
- package/dist/adapter/webgpu-presentation-context.js.map +1 -0
- package/dist/dist.dev.js +8070 -547
- package/dist/dist.min.js +169 -6
- package/dist/index.cjs +1929 -410
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
- package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
- package/dist/wgsl/get-shader-layout-wgsl.js +144 -0
- package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
- package/package.json +6 -5
- package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
- package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
- package/src/adapter/helpers/get-bind-group.ts +52 -46
- package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
- package/src/adapter/helpers/webgpu-parameters.ts +2 -0
- package/src/adapter/resources/webgpu-buffer.ts +61 -15
- package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
- package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
- package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
- package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
- package/src/adapter/resources/webgpu-fence.ts +38 -0
- package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
- package/src/adapter/resources/webgpu-pipeline-layout.ts +18 -17
- package/src/adapter/resources/webgpu-query-set.ts +185 -9
- package/src/adapter/resources/webgpu-render-pass.ts +92 -40
- package/src/adapter/resources/webgpu-render-pipeline.ts +83 -44
- package/src/adapter/resources/webgpu-sampler.ts +5 -0
- package/src/adapter/resources/webgpu-shader.ts +16 -1
- package/src/adapter/resources/webgpu-texture-view.ts +51 -11
- package/src/adapter/resources/webgpu-texture.ts +281 -101
- package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
- package/src/adapter/webgpu-adapter.ts +40 -42
- package/src/adapter/webgpu-canvas-context.ts +107 -40
- package/src/adapter/webgpu-device.ts +231 -21
- package/src/adapter/webgpu-presentation-context.ts +180 -0
- package/src/index.ts +3 -0
- package/src/wgsl/get-shader-layout-wgsl.ts +165 -0
- package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
- package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
- package/dist/adapter/helpers/accessor-to-format.js +0 -105
- package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
- package/src/adapter/helpers/accessor-to-format.ts +0 -104
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
// Forked from https://github.com/greggman/webgpu-utils under MIT license
|
|
6
|
+
// Copyright (c) 2022 Gregg Tavares
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Texture,
|
|
10
|
+
TextureView,
|
|
11
|
+
TextureFormat,
|
|
12
|
+
TextureFormatColor,
|
|
13
|
+
TextureBindingLayout,
|
|
14
|
+
StorageTextureBindingLayout,
|
|
15
|
+
UniformBufferBindingLayout,
|
|
16
|
+
SamplerBindingLayout
|
|
17
|
+
} from '@luma.gl/core';
|
|
18
|
+
import {Buffer, textureFormatDecoder} from '@luma.gl/core';
|
|
19
|
+
import type {WebGPUDevice} from '../webgpu-device';
|
|
20
|
+
import type {WebGPUComputePass} from '../resources/webgpu-compute-pass';
|
|
21
|
+
import type {WebGPURenderPass} from '../resources/webgpu-render-pass';
|
|
22
|
+
|
|
23
|
+
type RenderTextureViewDimension = '2d' | '2d-array' | 'cube' | 'cube-array';
|
|
24
|
+
type TextureCapability = 'render' | 'filter' | 'store';
|
|
25
|
+
type MipmapPath = 'render' | 'compute';
|
|
26
|
+
|
|
27
|
+
const RENDER_DIMENSIONS: ReadonlyArray<RenderTextureViewDimension> = [
|
|
28
|
+
'2d',
|
|
29
|
+
'2d-array',
|
|
30
|
+
'cube',
|
|
31
|
+
'cube-array'
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const WORKGROUP_SIZE = {
|
|
35
|
+
x: 4,
|
|
36
|
+
y: 4,
|
|
37
|
+
z: 4
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
const RENDER_SOURCE_SAMPLER_LAYOUT: SamplerBindingLayout = {
|
|
41
|
+
type: 'sampler',
|
|
42
|
+
name: 'sourceSampler',
|
|
43
|
+
group: 0,
|
|
44
|
+
location: 0
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const COMPUTE_SOURCE_TEXTURE_LAYOUT: TextureBindingLayout = {
|
|
48
|
+
type: 'texture',
|
|
49
|
+
name: 'sourceTexture',
|
|
50
|
+
group: 0,
|
|
51
|
+
location: 0,
|
|
52
|
+
viewDimension: '3d',
|
|
53
|
+
sampleType: 'float'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const COMPUTE_UNIFORMS_LAYOUT: UniformBufferBindingLayout = {
|
|
57
|
+
type: 'uniform',
|
|
58
|
+
name: 'uniforms',
|
|
59
|
+
group: 0,
|
|
60
|
+
location: 2
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Generates mip levels from level 0 to the last mip for an existing WebGPU texture.
|
|
65
|
+
*/
|
|
66
|
+
export function generateMipmapsWebGPU(device: WebGPUDevice, texture: Texture): void {
|
|
67
|
+
if (texture.mipLevels <= 1) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (texture.dimension === '3d') {
|
|
72
|
+
generateMipmaps3D(device, texture);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (RENDER_DIMENSIONS.includes(texture.dimension as RenderTextureViewDimension)) {
|
|
77
|
+
generateMipmapsRender(device, texture);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Cannot generate mipmaps for texture dimension "${texture.dimension}" with WebGPU.`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function generateMipmapsRender(device: WebGPUDevice, texture: Texture): void {
|
|
87
|
+
validateFormatCapabilities(device, texture, ['render', 'filter'], 'render');
|
|
88
|
+
const colorAttachmentFormat = getColorAttachmentFormat(
|
|
89
|
+
texture.format,
|
|
90
|
+
'render',
|
|
91
|
+
texture.dimension
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const viewDimension = texture.dimension as RenderTextureViewDimension;
|
|
95
|
+
const shaderSource = getRenderMipmapWGSL(viewDimension);
|
|
96
|
+
const sampler = device.createSampler({minFilter: 'linear', magFilter: 'linear'});
|
|
97
|
+
const uniformsBuffer = device.createBuffer({
|
|
98
|
+
byteLength: 16,
|
|
99
|
+
usage: Buffer.UNIFORM | Buffer.COPY_DST
|
|
100
|
+
});
|
|
101
|
+
const uniformValues = new Uint32Array(1);
|
|
102
|
+
const sourceTextureLayout: TextureBindingLayout = {
|
|
103
|
+
type: 'texture',
|
|
104
|
+
name: 'sourceTexture',
|
|
105
|
+
group: 0,
|
|
106
|
+
location: 1,
|
|
107
|
+
viewDimension,
|
|
108
|
+
sampleType: 'float'
|
|
109
|
+
};
|
|
110
|
+
const uniformsLayout: UniformBufferBindingLayout = {
|
|
111
|
+
type: 'uniform',
|
|
112
|
+
name: 'uniforms',
|
|
113
|
+
group: 0,
|
|
114
|
+
location: 2
|
|
115
|
+
};
|
|
116
|
+
const renderShaderLayout = {
|
|
117
|
+
attributes: [],
|
|
118
|
+
bindings: [RENDER_SOURCE_SAMPLER_LAYOUT, sourceTextureLayout, uniformsLayout]
|
|
119
|
+
};
|
|
120
|
+
const vertexShader = device.createShader({
|
|
121
|
+
id: 'mipmap-generation-render-vs',
|
|
122
|
+
source: shaderSource,
|
|
123
|
+
language: 'wgsl',
|
|
124
|
+
stage: 'vertex'
|
|
125
|
+
});
|
|
126
|
+
const fragmentShader = device.createShader({
|
|
127
|
+
id: 'mipmap-generation-render-fs',
|
|
128
|
+
source: shaderSource,
|
|
129
|
+
language: 'wgsl',
|
|
130
|
+
stage: 'fragment'
|
|
131
|
+
});
|
|
132
|
+
const renderPipeline = device.createRenderPipeline({
|
|
133
|
+
id: `mipmap-generation-render:${texture.dimension}:${texture.format}`,
|
|
134
|
+
vs: vertexShader,
|
|
135
|
+
fs: fragmentShader,
|
|
136
|
+
shaderLayout: renderShaderLayout,
|
|
137
|
+
colorAttachmentFormats: [colorAttachmentFormat],
|
|
138
|
+
topology: 'triangle-list'
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
let sourceWidth = texture.width;
|
|
142
|
+
let sourceHeight = texture.height;
|
|
143
|
+
const layerCount = texture.dimension === '2d' ? 1 : texture.depth;
|
|
144
|
+
|
|
145
|
+
function renderMipmapLayer(
|
|
146
|
+
sourceView: TextureView,
|
|
147
|
+
baseMipLevel: number,
|
|
148
|
+
baseArrayLayer: number,
|
|
149
|
+
destinationWidth: number,
|
|
150
|
+
destinationHeight: number
|
|
151
|
+
): void {
|
|
152
|
+
uniformValues[0] = baseArrayLayer;
|
|
153
|
+
uniformsBuffer.write(uniformValues);
|
|
154
|
+
|
|
155
|
+
const destinationView = texture.createView({
|
|
156
|
+
dimension: '2d',
|
|
157
|
+
baseMipLevel,
|
|
158
|
+
mipLevelCount: 1,
|
|
159
|
+
baseArrayLayer,
|
|
160
|
+
arrayLayerCount: 1
|
|
161
|
+
});
|
|
162
|
+
const framebuffer = device.createFramebuffer({
|
|
163
|
+
colorAttachments: [destinationView]
|
|
164
|
+
});
|
|
165
|
+
const renderPass = device.beginRenderPass({
|
|
166
|
+
id: `mipmap-generation:${texture.format}:${baseMipLevel}:${baseArrayLayer}`,
|
|
167
|
+
framebuffer
|
|
168
|
+
}) as WebGPURenderPass;
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
renderPass.setPipeline(renderPipeline);
|
|
172
|
+
renderPass.setBindings({
|
|
173
|
+
sourceSampler: sampler,
|
|
174
|
+
sourceTexture: sourceView,
|
|
175
|
+
uniforms: uniformsBuffer
|
|
176
|
+
});
|
|
177
|
+
renderPass.setParameters({
|
|
178
|
+
viewport: [0, 0, destinationWidth, destinationHeight, 0, 1],
|
|
179
|
+
scissorRect: [0, 0, destinationWidth, destinationHeight]
|
|
180
|
+
});
|
|
181
|
+
renderPass.draw({vertexCount: 3});
|
|
182
|
+
renderPass.end();
|
|
183
|
+
device.submit();
|
|
184
|
+
} finally {
|
|
185
|
+
destinationView.destroy();
|
|
186
|
+
framebuffer.destroy();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
for (let baseMipLevel = 1; baseMipLevel < texture.mipLevels; ++baseMipLevel) {
|
|
192
|
+
validateFormatCapabilities(device, texture, ['render', 'filter'], 'render');
|
|
193
|
+
const sourceMipLevel = baseMipLevel - 1;
|
|
194
|
+
const destinationWidth = Math.max(1, sourceWidth >> 1);
|
|
195
|
+
const destinationHeight = Math.max(1, sourceHeight >> 1);
|
|
196
|
+
|
|
197
|
+
const sourceView = texture.createView({
|
|
198
|
+
dimension: viewDimension,
|
|
199
|
+
baseMipLevel: sourceMipLevel,
|
|
200
|
+
mipLevelCount: 1,
|
|
201
|
+
baseArrayLayer: 0,
|
|
202
|
+
arrayLayerCount: texture.depth
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
for (let baseArrayLayer = 0; baseArrayLayer < layerCount; ++baseArrayLayer) {
|
|
207
|
+
renderMipmapLayer(
|
|
208
|
+
sourceView,
|
|
209
|
+
baseMipLevel,
|
|
210
|
+
baseArrayLayer,
|
|
211
|
+
destinationWidth,
|
|
212
|
+
destinationHeight
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
} finally {
|
|
216
|
+
sourceView.destroy();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
sourceWidth = destinationWidth;
|
|
220
|
+
sourceHeight = destinationHeight;
|
|
221
|
+
}
|
|
222
|
+
} finally {
|
|
223
|
+
renderPipeline.destroy();
|
|
224
|
+
vertexShader.destroy();
|
|
225
|
+
fragmentShader.destroy();
|
|
226
|
+
sampler.destroy();
|
|
227
|
+
uniformsBuffer.destroy();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function getColorAttachmentFormat(
|
|
232
|
+
format: TextureFormat,
|
|
233
|
+
path: MipmapPath,
|
|
234
|
+
dimension: string
|
|
235
|
+
): TextureFormatColor {
|
|
236
|
+
if (textureFormatDecoder.isColor(format)) {
|
|
237
|
+
return format;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
throw new Error(
|
|
241
|
+
`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". ` +
|
|
242
|
+
`Only color textures can be used for this operation. ` +
|
|
243
|
+
`Required capabilities: color. ` +
|
|
244
|
+
`Actual capabilities: color=false.`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function generateMipmaps3D(device: WebGPUDevice, texture: Texture): void {
|
|
249
|
+
validateFormatCapabilities(device, texture, ['filter', 'store'], 'compute');
|
|
250
|
+
const format = getColorAttachmentFormat(texture.format, 'compute', texture.dimension);
|
|
251
|
+
const shaderSource = get3DComputeMipmapWGSL(format);
|
|
252
|
+
const destinationTextureLayout: StorageTextureBindingLayout = {
|
|
253
|
+
type: 'storage',
|
|
254
|
+
name: 'destinationTexture',
|
|
255
|
+
group: 0,
|
|
256
|
+
location: 1,
|
|
257
|
+
format,
|
|
258
|
+
viewDimension: '3d',
|
|
259
|
+
access: 'write-only'
|
|
260
|
+
};
|
|
261
|
+
const computeShaderLayout = {
|
|
262
|
+
bindings: [COMPUTE_SOURCE_TEXTURE_LAYOUT, destinationTextureLayout, COMPUTE_UNIFORMS_LAYOUT]
|
|
263
|
+
};
|
|
264
|
+
const computeShader = device.createShader({
|
|
265
|
+
id: 'mipmap-generation-compute',
|
|
266
|
+
source: shaderSource,
|
|
267
|
+
language: 'wgsl',
|
|
268
|
+
stage: 'compute'
|
|
269
|
+
});
|
|
270
|
+
const computePipeline = device.createComputePipeline({
|
|
271
|
+
id: `mipmap-generation-compute:${texture.format}`,
|
|
272
|
+
shader: computeShader,
|
|
273
|
+
shaderLayout: computeShaderLayout
|
|
274
|
+
});
|
|
275
|
+
const uniformsBuffer = device.createBuffer({
|
|
276
|
+
byteLength: 32,
|
|
277
|
+
usage: Buffer.UNIFORM | Buffer.COPY_DST
|
|
278
|
+
});
|
|
279
|
+
const uniformValues = new Uint32Array(8);
|
|
280
|
+
|
|
281
|
+
let sourceWidth = texture.width;
|
|
282
|
+
let sourceHeight = texture.height;
|
|
283
|
+
let sourceDepth = texture.depth;
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
for (
|
|
287
|
+
let destinationMipLevel = 1;
|
|
288
|
+
destinationMipLevel < texture.mipLevels;
|
|
289
|
+
++destinationMipLevel
|
|
290
|
+
) {
|
|
291
|
+
validateFormatCapabilities(device, texture, ['filter', 'store'], 'compute');
|
|
292
|
+
const destinationWidth = Math.max(1, sourceWidth >> 1);
|
|
293
|
+
const destinationHeight = Math.max(1, sourceHeight >> 1);
|
|
294
|
+
const destinationDepth = Math.max(1, sourceDepth >> 1);
|
|
295
|
+
|
|
296
|
+
uniformValues[0] = sourceWidth;
|
|
297
|
+
uniformValues[1] = sourceHeight;
|
|
298
|
+
uniformValues[2] = sourceDepth;
|
|
299
|
+
uniformValues[3] = destinationWidth;
|
|
300
|
+
uniformValues[4] = destinationHeight;
|
|
301
|
+
uniformValues[5] = destinationDepth;
|
|
302
|
+
uniformValues[6] = 0;
|
|
303
|
+
uniformsBuffer.write(uniformValues);
|
|
304
|
+
|
|
305
|
+
const sourceView = texture.createView({
|
|
306
|
+
dimension: '3d',
|
|
307
|
+
baseMipLevel: destinationMipLevel - 1,
|
|
308
|
+
mipLevelCount: 1,
|
|
309
|
+
baseArrayLayer: 0,
|
|
310
|
+
arrayLayerCount: 1
|
|
311
|
+
});
|
|
312
|
+
const destinationView = texture.createView({
|
|
313
|
+
dimension: '3d',
|
|
314
|
+
baseMipLevel: destinationMipLevel,
|
|
315
|
+
mipLevelCount: 1,
|
|
316
|
+
baseArrayLayer: 0,
|
|
317
|
+
arrayLayerCount: 1
|
|
318
|
+
});
|
|
319
|
+
computePipeline.setBindings({
|
|
320
|
+
sourceTexture: sourceView,
|
|
321
|
+
destinationTexture: destinationView,
|
|
322
|
+
uniforms: uniformsBuffer
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
const workgroupsX = Math.ceil(destinationWidth / WORKGROUP_SIZE.x);
|
|
327
|
+
const workgroupsY = Math.ceil(destinationHeight / WORKGROUP_SIZE.y);
|
|
328
|
+
const workgroupsZ = Math.ceil(destinationDepth / WORKGROUP_SIZE.z);
|
|
329
|
+
const computePass = device.beginComputePass({}) as WebGPUComputePass;
|
|
330
|
+
|
|
331
|
+
computePass.setPipeline(computePipeline);
|
|
332
|
+
computePass.dispatch(workgroupsX, workgroupsY, workgroupsZ);
|
|
333
|
+
computePass.end();
|
|
334
|
+
device.submit();
|
|
335
|
+
} finally {
|
|
336
|
+
sourceView.destroy();
|
|
337
|
+
destinationView.destroy();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
sourceWidth = destinationWidth;
|
|
341
|
+
sourceHeight = destinationHeight;
|
|
342
|
+
sourceDepth = destinationDepth;
|
|
343
|
+
}
|
|
344
|
+
} finally {
|
|
345
|
+
computePipeline.destroy();
|
|
346
|
+
computeShader.destroy();
|
|
347
|
+
uniformsBuffer.destroy();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function validateFormatCapabilities(
|
|
352
|
+
device: WebGPUDevice,
|
|
353
|
+
texture: Texture,
|
|
354
|
+
requiredCapabilities: ReadonlyArray<TextureCapability>,
|
|
355
|
+
path: MipmapPath
|
|
356
|
+
): void {
|
|
357
|
+
const {format, dimension} = texture;
|
|
358
|
+
const capabilities = device.getTextureFormatCapabilities(format);
|
|
359
|
+
const missingCapabilities = requiredCapabilities.filter(capability => !capabilities[capability]);
|
|
360
|
+
|
|
361
|
+
if (missingCapabilities.length > 0) {
|
|
362
|
+
const required = requiredCapabilities.join(' + ');
|
|
363
|
+
const actual = requiredCapabilities
|
|
364
|
+
.map(capability => `${capability}=${capabilities[capability]}`)
|
|
365
|
+
.join(', ');
|
|
366
|
+
throw new Error(
|
|
367
|
+
`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". ` +
|
|
368
|
+
`Required capabilities: ${required}. ` +
|
|
369
|
+
`Actual capabilities: ${actual}.`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function getSourceTextureType(dimension: RenderTextureViewDimension): string {
|
|
375
|
+
switch (dimension) {
|
|
376
|
+
case '2d':
|
|
377
|
+
return 'texture_2d<f32>';
|
|
378
|
+
case '2d-array':
|
|
379
|
+
return 'texture_2d_array<f32>';
|
|
380
|
+
case 'cube':
|
|
381
|
+
return 'texture_cube<f32>';
|
|
382
|
+
case 'cube-array':
|
|
383
|
+
return 'texture_cube_array<f32>';
|
|
384
|
+
default:
|
|
385
|
+
throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function getRenderMipmapWGSL(dimension: RenderTextureViewDimension): string {
|
|
390
|
+
const sourceSnippet = getRenderMipmapSampleSnippet(dimension);
|
|
391
|
+
|
|
392
|
+
return `
|
|
393
|
+
struct MipmapUniforms {
|
|
394
|
+
sourceLayer: u32,
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
fn _touchUniform(uniforms: MipmapUniforms) {
|
|
398
|
+
let unusedSourceLayer = uniforms.sourceLayer;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const faceMat = array(
|
|
402
|
+
mat3x3f(
|
|
403
|
+
0.0, 0.0, -2.0,
|
|
404
|
+
0.0, -2.0, 0.0,
|
|
405
|
+
1.0, 1.0, 1.0
|
|
406
|
+
), // pos-x
|
|
407
|
+
mat3x3f(
|
|
408
|
+
0.0, 0.0, 2.0,
|
|
409
|
+
0.0, -2.0, 0.0,
|
|
410
|
+
-1.0, 1.0, -1.0
|
|
411
|
+
), // neg-x
|
|
412
|
+
mat3x3f(
|
|
413
|
+
2.0, 0.0, 0.0,
|
|
414
|
+
0.0, 0.0, 2.0,
|
|
415
|
+
-1.0, 1.0, -1.0
|
|
416
|
+
), // pos-y
|
|
417
|
+
mat3x3f(
|
|
418
|
+
2.0, 0.0, 0.0,
|
|
419
|
+
0.0, 0.0, -2.0,
|
|
420
|
+
-1.0, -1.0, 1.0
|
|
421
|
+
), // neg-y
|
|
422
|
+
mat3x3f(
|
|
423
|
+
2.0, 0.0, 0.0,
|
|
424
|
+
0.0, -2.0, 0.0,
|
|
425
|
+
-1.0, 1.0, 1.0
|
|
426
|
+
), // pos-z
|
|
427
|
+
mat3x3f(
|
|
428
|
+
-2.0, 0.0, 0.0,
|
|
429
|
+
0.0, -2.0, 0.0,
|
|
430
|
+
1.0, 1.0, -1.0
|
|
431
|
+
) // neg-z
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
struct FragmentInputs {
|
|
435
|
+
@builtin(position) position: vec4f,
|
|
436
|
+
@location(0) texcoord: vec2f
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
struct VertexOutput {
|
|
440
|
+
@builtin(position) position: vec4f,
|
|
441
|
+
@location(0) texcoord: vec2f
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
@group(0) @binding(0) var sourceSampler: sampler;
|
|
445
|
+
@group(0) @binding(1) var sourceTexture: ${getSourceTextureType(dimension)};
|
|
446
|
+
@group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
|
|
447
|
+
|
|
448
|
+
@vertex
|
|
449
|
+
fn vertexMain(
|
|
450
|
+
@builtin(vertex_index) vertexIndex: u32
|
|
451
|
+
) -> VertexOutput {
|
|
452
|
+
const positions = array(
|
|
453
|
+
vec2f(-1.0, -1.0),
|
|
454
|
+
vec2f(-1.0, 3.0),
|
|
455
|
+
vec2f( 3.0, -1.0)
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
let xy = positions[vertexIndex];
|
|
459
|
+
return VertexOutput(
|
|
460
|
+
vec4f(xy, 0.0, 1.0),
|
|
461
|
+
xy * vec2f(0.5, -0.5) + vec2f(0.5)
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
@fragment
|
|
466
|
+
fn fragmentMain(fsInput: VertexOutput) -> @location(0) vec4f {
|
|
467
|
+
_touchUniform(uniforms);
|
|
468
|
+
return ${sourceSnippet};
|
|
469
|
+
}
|
|
470
|
+
`;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function getRenderMipmapSampleSnippet(dimension: RenderTextureViewDimension): string {
|
|
474
|
+
const layer = 'uniforms.sourceLayer';
|
|
475
|
+
|
|
476
|
+
switch (dimension) {
|
|
477
|
+
case '2d':
|
|
478
|
+
return 'textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, 0.0)';
|
|
479
|
+
case '2d-array':
|
|
480
|
+
return (
|
|
481
|
+
'textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, ' +
|
|
482
|
+
`i32(${layer}), 0.0)`
|
|
483
|
+
);
|
|
484
|
+
case 'cube':
|
|
485
|
+
return (
|
|
486
|
+
'textureSampleLevel(sourceTexture, sourceSampler, ' +
|
|
487
|
+
`faceMat[i32(${layer})] * vec3f(fract(fsInput.texcoord), 1.0), 0.0)`
|
|
488
|
+
);
|
|
489
|
+
case 'cube-array':
|
|
490
|
+
return (
|
|
491
|
+
'textureSampleLevel(sourceTexture, sourceSampler, ' +
|
|
492
|
+
`faceMat[i32(${layer} % 6u)] * vec3f(fract(fsInput.texcoord), 1.0), ` +
|
|
493
|
+
`i32(${layer} / 6u), 0.0)`
|
|
494
|
+
);
|
|
495
|
+
default:
|
|
496
|
+
throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function get3DComputeMipmapWGSL(format: TextureFormatColor): string {
|
|
501
|
+
return `
|
|
502
|
+
struct MipmapUniforms {
|
|
503
|
+
sourceWidth: u32,
|
|
504
|
+
sourceHeight: u32,
|
|
505
|
+
sourceDepth: u32,
|
|
506
|
+
destinationWidth: u32,
|
|
507
|
+
destinationHeight: u32,
|
|
508
|
+
destinationDepth: u32,
|
|
509
|
+
padding: u32,
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
@group(0) @binding(0) var sourceTexture: texture_3d<f32>;
|
|
513
|
+
@group(0) @binding(1) var destinationTexture: texture_storage_3d<${format}, write>;
|
|
514
|
+
@group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
|
|
515
|
+
|
|
516
|
+
@compute @workgroup_size(${WORKGROUP_SIZE.x}, ${WORKGROUP_SIZE.y}, ${WORKGROUP_SIZE.z})
|
|
517
|
+
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
518
|
+
if (
|
|
519
|
+
id.x >= uniforms.destinationWidth ||
|
|
520
|
+
id.y >= uniforms.destinationHeight ||
|
|
521
|
+
id.z >= uniforms.destinationDepth
|
|
522
|
+
) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
let sourceBase = id * 2u;
|
|
527
|
+
let sourceX0 = min(sourceBase.x, uniforms.sourceWidth - 1u);
|
|
528
|
+
let sourceY0 = min(sourceBase.y, uniforms.sourceHeight - 1u);
|
|
529
|
+
let sourceZ0 = min(sourceBase.z, uniforms.sourceDepth - 1u);
|
|
530
|
+
|
|
531
|
+
let sourceX1 = min(sourceBase.x + 1u, uniforms.sourceWidth - 1u);
|
|
532
|
+
let sourceY1 = min(sourceBase.y + 1u, uniforms.sourceHeight - 1u);
|
|
533
|
+
let sourceZ1 = min(sourceBase.z + 1u, uniforms.sourceDepth - 1u);
|
|
534
|
+
|
|
535
|
+
var sum = textureLoad(
|
|
536
|
+
sourceTexture,
|
|
537
|
+
vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ0)),
|
|
538
|
+
0
|
|
539
|
+
);
|
|
540
|
+
sum += textureLoad(
|
|
541
|
+
sourceTexture,
|
|
542
|
+
vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ0)),
|
|
543
|
+
0
|
|
544
|
+
);
|
|
545
|
+
sum += textureLoad(
|
|
546
|
+
sourceTexture,
|
|
547
|
+
vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ0)),
|
|
548
|
+
0
|
|
549
|
+
);
|
|
550
|
+
sum += textureLoad(
|
|
551
|
+
sourceTexture,
|
|
552
|
+
vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ0)),
|
|
553
|
+
0
|
|
554
|
+
);
|
|
555
|
+
sum += textureLoad(
|
|
556
|
+
sourceTexture,
|
|
557
|
+
vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ1)),
|
|
558
|
+
0
|
|
559
|
+
);
|
|
560
|
+
sum += textureLoad(
|
|
561
|
+
sourceTexture,
|
|
562
|
+
vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ1)),
|
|
563
|
+
0
|
|
564
|
+
);
|
|
565
|
+
sum += textureLoad(
|
|
566
|
+
sourceTexture,
|
|
567
|
+
vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ1)),
|
|
568
|
+
0
|
|
569
|
+
);
|
|
570
|
+
sum += textureLoad(
|
|
571
|
+
sourceTexture,
|
|
572
|
+
vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ1)),
|
|
573
|
+
0
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
textureStore(
|
|
577
|
+
destinationTexture,
|
|
578
|
+
vec3<i32>(i32(id.x), i32(id.y), i32(id.z)),
|
|
579
|
+
vec4<f32>(sum.xyz / 8.0, sum.w / 8.0)
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
`;
|
|
583
|
+
}
|