Skip to content

Commit 4e99f10

Browse files
authored
WebGPU: Implement manual MSAA resolve + depth texture resolve (#17503)
1 parent b1fc829 commit 4e99f10

File tree

7 files changed

+198
-31
lines changed

7 files changed

+198
-31
lines changed

packages/dev/core/src/Engines/WebGPU/Extensions/engine.multiRender.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ declare module "../../abstractEngine" {
5050
* Resolves the MSAA textures of the (multi) render target into their non-MSAA version.
5151
* Note that if "texture" is not a MSAA render target, no resolve is performed.
5252
* @param texture The render target texture containing the MSAA textures to resolve
53+
* @param resolveColors If true, resolve the color textures (default: true) - still subject to texture.resolveMSAAColors
5354
*/
54-
resolveMultiFramebuffer(texture: RenderTargetWrapper): void;
55+
resolveMultiFramebuffer(texture: RenderTargetWrapper, resolveColors?: boolean): void;
5556

5657
/**
5758
* Select a subsets of attachments to draw to.
@@ -92,6 +93,10 @@ WebGPUEngine.prototype.unBindMultiColorAttachmentFramebuffer = function (
9293

9394
this._endCurrentRenderPass();
9495

96+
if (!rtWrapper.disableAutomaticMSAAResolve) {
97+
this.resolveMultiFramebuffer(rtWrapper, false);
98+
}
99+
95100
if (!disableGenerateMipMaps) {
96101
this.generateMipMapsMultiFramebuffer(rtWrapper);
97102
}
@@ -339,8 +344,8 @@ WebGPUEngine.prototype.generateMipMapsMultiFramebuffer = function (texture: Rend
339344
}
340345
};
341346

342-
WebGPUEngine.prototype.resolveMultiFramebuffer = function (_texture: RenderTargetWrapper): void {
343-
throw new Error("resolveMultiFramebuffer is not yet implemented in WebGPU!");
347+
WebGPUEngine.prototype.resolveMultiFramebuffer = function (texture: RenderTargetWrapper, resolveColors: boolean = true): void {
348+
this.resolveFramebuffer(texture, resolveColors);
344349
};
345350

346351
WebGPUEngine.prototype.bindAttachments = function (attachments: number[]): void {

packages/dev/core/src/Engines/WebGPU/webgpuTextureHelper.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,19 @@ export class WebGPUTextureHelper {
726726
return false;
727727
}
728728

729+
public static HasDepthAspect(format: GPUTextureFormat): boolean {
730+
switch (format) {
731+
case WebGPUConstants.TextureFormat.Depth16Unorm:
732+
case WebGPUConstants.TextureFormat.Depth24Plus:
733+
case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
734+
case WebGPUConstants.TextureFormat.Depth32Float:
735+
case WebGPUConstants.TextureFormat.Depth32FloatStencil8:
736+
return true;
737+
}
738+
739+
return false;
740+
}
741+
729742
public static HasDepthAndStencilAspects(format: GPUTextureFormat): boolean {
730743
switch (format) {
731744
case WebGPUConstants.TextureFormat.Depth32FloatStencil8:

packages/dev/core/src/Engines/WebGPU/webgpuTextureManager.ts

Lines changed: 115 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ const clearVertexSource = `
151151
const clearFragmentSource = `
152152
uniform color: vec4f;
153153
154-
155154
@fragment
156155
fn main(input: FragmentInputs) -> FragmentOutputs {
157156
fragmentOutputs.color = uniforms.color;
@@ -214,11 +213,41 @@ const copyVideoToTextureInvertYFragmentSource = `
214213
}
215214
`;
216215

216+
const resolveDepthVertexSource = `
217+
const pos = array<vec2<f32>, 4>( vec2f(-1.0f, 1.0f), vec2f(1.0f, 1.0f), vec2f(-1.0f, -1.0f), vec2f(1.0f, -1.0f));
218+
219+
@vertex
220+
fn main(input : VertexInputs) -> FragmentInputs {
221+
vertexOutputs.position = vec4f(pos[input.vertexIndex], 0.0, 1.0);
222+
}
223+
`;
224+
225+
const resolveDepthFragmentSource = `
226+
var msaaDepthTexture: texture_depth_multisampled_2d;
227+
228+
@fragment
229+
fn main(input: FragmentInputs) -> FragmentOutputs {
230+
#ifdef USE_MIN
231+
let numSamples = textureNumSamples(msaaDepthTexture);
232+
var depth = 1.0;
233+
234+
for (var i = 0u; i < numSamples; i = i + 1u) {
235+
depth = min(depth, textureLoad(msaaDepthTexture, vec2u(input.position.xy), i));
236+
}
237+
238+
fragmentOutputs.color = vec4f(depth);
239+
#else
240+
fragmentOutputs.color = vec4f(textureLoad(msaaDepthTexture, vec2u(input.position.xy), 0)); // do like WebGL, take the first sample
241+
#endif
242+
}
243+
`;
244+
217245
enum PipelineType {
218246
MipMap = 0,
219247
InvertYPremultiplyAlpha = 1,
220248
Clear = 2,
221249
InvertYPremultiplyAlphaWithOfst = 3,
250+
ResolveDepth = 4,
222251
}
223252

224253
enum VideoPipelineType {
@@ -236,6 +265,7 @@ const shadersForPipelineType = [
236265
{ vertex: invertYPreMultiplyAlphaVertexSource, fragment: invertYPreMultiplyAlphaFragmentSource },
237266
{ vertex: clearVertexSource, fragment: clearFragmentSource },
238267
{ vertex: invertYPreMultiplyAlphaWithOfstVertexSource, fragment: invertYPreMultiplyAlphaWithOfstFragmentSource },
268+
{ vertex: resolveDepthVertexSource, fragment: resolveDepthFragmentSource },
239269
];
240270

241271
/**
@@ -350,7 +380,9 @@ export class WebGPUTextureManager {
350380
? 1 << 3
351381
: type === PipelineType.InvertYPremultiplyAlphaWithOfst
352382
? ((params!.invertY ? 1 : 0) << 4) + ((params!.premultiplyAlpha ? 1 : 0) << 5)
353-
: 0;
383+
: type === PipelineType.ResolveDepth
384+
? 1 << 6
385+
: 0;
354386

355387
if (!this._pipelines[format]) {
356388
this._pipelines[format] = [];
@@ -1001,11 +1033,34 @@ export class WebGPUTextureManager {
10011033
depth = texture.depth;
10021034
}
10031035

1036+
texture.width = texture.baseWidth = width;
1037+
texture.height = texture.baseHeight = height;
1038+
texture.depth = texture.baseDepth = depth;
1039+
10041040
const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
10051041
const isStorageTexture = ((creationFlags ?? 0) & Constants.TEXTURE_CREATIONFLAG_STORAGE) !== 0;
10061042

10071043
gpuTextureWrapper.format = WebGPUTextureHelper.GetWebGPUTextureFormat(texture.type, texture.format, texture._useSRGBBuffer);
10081044

1045+
if (!dontCreateMSAATexture) {
1046+
this.createMSAATexture(texture, texture.samples);
1047+
}
1048+
1049+
if (texture.samples > 1) {
1050+
// In case of a MSAA texture, the current texture will be the "resolve" texture, which cannot have a depth format
1051+
switch (gpuTextureWrapper.format) {
1052+
case WebGPUConstants.TextureFormat.Depth16Unorm:
1053+
gpuTextureWrapper.format = WebGPUConstants.TextureFormat.R16Unorm;
1054+
break;
1055+
case WebGPUConstants.TextureFormat.Depth24Plus:
1056+
case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
1057+
case WebGPUConstants.TextureFormat.Depth32Float:
1058+
case WebGPUConstants.TextureFormat.Depth32FloatStencil8:
1059+
gpuTextureWrapper.format = WebGPUConstants.TextureFormat.R32Float;
1060+
break;
1061+
}
1062+
}
1063+
10091064
gpuTextureWrapper.textureUsages =
10101065
texture._source === InternalTextureSource.RenderTarget || texture.source === InternalTextureSource.MultiRenderTarget
10111066
? WebGPUConstants.TextureUsage.TextureBinding | WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.RenderAttachment
@@ -1105,14 +1160,6 @@ export class WebGPUTextureManager {
11051160
);
11061161
}
11071162

1108-
texture.width = texture.baseWidth = width;
1109-
texture.height = texture.baseHeight = height;
1110-
texture.depth = texture.baseDepth = depth;
1111-
1112-
if (!dontCreateMSAATexture) {
1113-
this.createMSAATexture(texture, texture.samples);
1114-
}
1115-
11161163
return gpuTextureWrapper;
11171164
}
11181165

@@ -1140,13 +1187,70 @@ export class WebGPUTextureManager {
11401187
gpuTextureWrapper.format,
11411188
samples,
11421189
this._commandEncoderForCreation,
1143-
WebGPUConstants.TextureUsage.RenderAttachment,
1190+
WebGPUConstants.TextureUsage.RenderAttachment | WebGPUConstants.TextureUsage.TextureBinding,
11441191
0,
11451192
texture.label ? "MSAA_" + texture.label : "MSAA"
11461193
);
11471194
gpuTextureWrapper.setMSAATexture(gpuMSAATexture, index);
11481195
}
11491196

1197+
public resolveMSAADepthTexture(msaaTexture: GPUTexture, outputTexture: GPUTexture, commandEncoder?: GPUCommandEncoder): void {
1198+
const format = outputTexture.format;
1199+
1200+
const useOwnCommandEncoder = commandEncoder === undefined;
1201+
const [pipeline, bindGroupLayout] = this._getPipeline(format, PipelineType.ResolveDepth);
1202+
1203+
if (useOwnCommandEncoder) {
1204+
commandEncoder = this._device.createCommandEncoder({});
1205+
}
1206+
1207+
commandEncoder!.pushDebugGroup(`resolve MSAA Depth texture${msaaTexture.label ? " - " + msaaTexture.label : ""}`);
1208+
1209+
const renderPassDescriptor: GPURenderPassDescriptor = {
1210+
label: `BabylonWebGPUDevice${this._engine.uniqueId}_resolveMSAADepthTexture${msaaTexture.label ? "_" + msaaTexture.label : ""}`,
1211+
colorAttachments: [
1212+
{
1213+
view: outputTexture,
1214+
loadOp: WebGPUConstants.LoadOp.Load,
1215+
storeOp: WebGPUConstants.StoreOp.Store,
1216+
},
1217+
],
1218+
};
1219+
const passEncoder = commandEncoder!.beginRenderPass(renderPassDescriptor);
1220+
1221+
const descriptor: GPUBindGroupDescriptor = {
1222+
layout: bindGroupLayout,
1223+
entries: [
1224+
{
1225+
binding: 0,
1226+
resource: msaaTexture.createView({
1227+
format: WebGPUTextureHelper.GetDepthFormatOnly(msaaTexture.format),
1228+
dimension: WebGPUConstants.TextureViewDimension.E2d,
1229+
mipLevelCount: 1,
1230+
baseArrayLayer: 0,
1231+
baseMipLevel: 0,
1232+
arrayLayerCount: 1,
1233+
aspect: WebGPUConstants.TextureAspect.DepthOnly,
1234+
}),
1235+
},
1236+
],
1237+
};
1238+
1239+
const bindGroup = this._device.createBindGroup(descriptor);
1240+
1241+
passEncoder.setPipeline(pipeline);
1242+
passEncoder.setBindGroup(0, bindGroup);
1243+
passEncoder.draw(4, 1, 0, 0);
1244+
passEncoder.end();
1245+
1246+
commandEncoder!.popDebugGroup();
1247+
1248+
if (useOwnCommandEncoder) {
1249+
this._device.queue.submit([commandEncoder!.finish()]);
1250+
commandEncoder = null as any;
1251+
}
1252+
}
1253+
11501254
//------------------------------------------------------------------------------
11511255
// Update
11521256
//------------------------------------------------------------------------------

packages/dev/core/src/Engines/renderTargetWrapper.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,11 @@ export class RenderTargetWrapper {
266266
*/
267267
public generateMipMaps(): void {
268268
if (this._engine._currentRenderTarget === this) {
269-
this._engine.unBindFramebuffer(this, true);
269+
if (this.isMulti) {
270+
this._engine.unBindMultiColorAttachmentFramebuffer(this, true);
271+
} else {
272+
this._engine.unBindFramebuffer(this, true);
273+
}
270274
}
271275
if (this.isMulti) {
272276
this._engine.generateMipMapsMultiFramebuffer(this);

packages/dev/core/src/Engines/webgpuEngine.ts

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3114,7 +3114,8 @@ export class WebGPUEngine extends ThinWebGPUEngine {
31143114
// Render Pass
31153115
//------------------------------------------------------------------------------
31163116

3117-
private _startRenderTargetRenderPass(
3117+
/** @internal */
3118+
public _startRenderTargetRenderPass(
31183119
renderTargetWrapper: RenderTargetWrapper,
31193120
setClearStates: boolean,
31203121
clearColor: Nullable<IColor4Like>,
@@ -3130,9 +3131,11 @@ export class WebGPUEngine extends ThinWebGPUEngine {
31303131
const gpuDepthStencilTexture = gpuDepthStencilWrapper?.underlyingResource as Nullable<GPUTexture>;
31313132
const gpuDepthStencilMSAATexture = gpuDepthStencilWrapper?.getMSAATexture(0);
31323133

3133-
const depthTextureView = gpuDepthStencilTexture?.createView(this._rttRenderPassWrapper.depthAttachmentViewDescriptor!);
31343134
const depthMSAATextureView = gpuDepthStencilMSAATexture?.createView(this._rttRenderPassWrapper.depthAttachmentViewDescriptor!);
3135-
const depthTextureHasStencil = gpuDepthStencilWrapper ? WebGPUTextureHelper.HasStencilAspect(gpuDepthStencilWrapper.format) : false;
3135+
const depthTextureView = depthMSAATextureView ? undefined : gpuDepthStencilTexture?.createView(this._rttRenderPassWrapper.depthAttachmentViewDescriptor!);
3136+
// We use the MSAA texture format (if available) to determine if it has a stencil aspect or not because, for MSAA depth textures,
3137+
// the format of the "resolve" texture (gpuDepthStencilWrapper.format) is a single red channel format, not a depth-stencil format.
3138+
const depthTextureHasStencil = gpuDepthStencilWrapper ? WebGPUTextureHelper.HasStencilAspect(gpuDepthStencilMSAATexture?.format ?? gpuDepthStencilWrapper.format) : false;
31363139

31373140
const colorAttachments: (GPURenderPassColorAttachment | null)[] = [];
31383141

@@ -3185,7 +3188,7 @@ export class WebGPUEngine extends ThinWebGPUEngine {
31853188

31863189
colorAttachments.push({
31873190
view: colorMSAATextureView ? colorMSAATextureView : colorTextureView,
3188-
resolveTarget: gpuMSAATexture ? colorTextureView : undefined,
3191+
resolveTarget: gpuMSAATexture && !rtWrapper.disableAutomaticMSAAResolve ? colorTextureView : undefined,
31893192
depthSlice: mrtTexture.is3D ? (rtWrapper.layerIndices?.[i] ?? 0) : undefined,
31903193
clearValue: index !== 0 && mustClearColor ? (isRtInteger ? clearColorForIntegerRt : clearColor) : undefined,
31913194
loadOp: index !== 0 && mustClearColor ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load,
@@ -3216,7 +3219,7 @@ export class WebGPUEngine extends ThinWebGPUEngine {
32163219

32173220
colorAttachments.push({
32183221
view: colorMSAATextureView ? colorMSAATextureView : colorTextureView,
3219-
resolveTarget: gpuMSAATexture ? colorTextureView : undefined,
3222+
resolveTarget: gpuMSAATexture && !rtWrapper.disableAutomaticMSAAResolve ? colorTextureView : undefined,
32203223
depthSlice,
32213224
clearValue: mustClearColor ? (isRtInteger ? clearColorForIntegerRt : clearColor) : undefined,
32223225
loadOp: mustClearColor ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load,
@@ -3398,6 +3401,7 @@ export class WebGPUEngine extends ThinWebGPUEngine {
33983401
this._setColorFormat(this._rttRenderPassWrapper);
33993402

34003403
this._rttRenderPassWrapper.colorAttachmentViewDescriptor = {
3404+
label: texture.label ? texture.label + " - Color Attachment View" : "RTT - Color Attachment View",
34013405
format: this._colorFormat as GPUTextureFormat,
34023406
dimension: texture.is3D ? WebGPUConstants.TextureViewDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
34033407
mipLevelCount: 1,
@@ -3408,6 +3412,7 @@ export class WebGPUEngine extends ThinWebGPUEngine {
34083412
};
34093413

34103414
this._rttRenderPassWrapper.depthAttachmentViewDescriptor = {
3415+
label: texture.label ? texture.label + " - Depth Attachment View" : "RTT - Depth Attachment View",
34113416
format: this._depthTextureFormat!,
34123417
dimension: depthStencilTexture && depthStencilTexture.is3D ? WebGPUConstants.TextureViewDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
34133418
mipLevelCount: 1,
@@ -3491,13 +3496,7 @@ export class WebGPUEngine extends ThinWebGPUEngine {
34913496

34923497
this._endCurrentRenderPass();
34933498

3494-
if (!disableGenerateMipMaps) {
3495-
if (texture.isMulti) {
3496-
this.generateMipMapsMultiFramebuffer(texture);
3497-
} else {
3498-
this.generateMipMapsFramebuffer(texture);
3499-
}
3500-
}
3499+
this._resolveAndGenerateMipMapsFramebuffer(texture, disableGenerateMipMaps);
35013500

35023501
this._currentRenderTarget = null;
35033502

@@ -3515,6 +3514,23 @@ export class WebGPUEngine extends ThinWebGPUEngine {
35153514
this._cacheRenderPipeline.setMRTAttachments(this._mrtAttachments);
35163515
}
35173516

3517+
private _resolveAndGenerateMipMapsFramebuffer(texture: RenderTargetWrapper, disableGenerateMipMaps = false): void {
3518+
const webglRtWrapper = texture;
3519+
3520+
if (!webglRtWrapper.disableAutomaticMSAAResolve) {
3521+
// we pass false as the second parameter because the color resolve has already been done automatically when the render pass ended
3522+
this.resolveFramebuffer(texture, false);
3523+
}
3524+
3525+
if (!disableGenerateMipMaps) {
3526+
if (texture.isMulti) {
3527+
this.generateMipMapsMultiFramebuffer(texture);
3528+
} else {
3529+
this.generateMipMapsFramebuffer(texture);
3530+
}
3531+
}
3532+
}
3533+
35183534
/**
35193535
* Generates mipmaps for the texture of the (single) render target
35203536
* @param texture The render target containing the texture to generate the mipmaps for
@@ -3526,12 +3542,34 @@ export class WebGPUEngine extends ThinWebGPUEngine {
35263542
}
35273543

35283544
/**
3529-
* Resolves the MSAA texture of the (single) render target into its non-MSAA version.
3545+
* Resolves the MSAA texture of the render target into its non-MSAA version.
35303546
* Note that if "texture" is not a MSAA render target, no resolve is performed.
3531-
* @param _texture The render target texture containing the MSAA texture to resolve
3547+
* @param texture The render target texture containing the MSAA texture to resolve
3548+
* @param resolveColors If true, resolve the color textures (default: true) - still subject to texture.resolveMSAAColors
35323549
*/
3533-
public resolveFramebuffer(_texture: RenderTargetWrapper): void {
3534-
throw new Error("resolveFramebuffer is not yet implemented in WebGPU!");
3550+
public resolveFramebuffer(texture: RenderTargetWrapper, resolveColors = true): void {
3551+
if (texture.samples <= 1) {
3552+
return;
3553+
}
3554+
3555+
if (texture.resolveMSAAColors && resolveColors) {
3556+
const disableAutomaticMSAAResolve = texture.disableAutomaticMSAAResolve;
3557+
3558+
texture.disableAutomaticMSAAResolve = false;
3559+
3560+
// Simply bind and unbind the framebuffer to trigger the resolve
3561+
this.bindFramebuffer(texture);
3562+
this._startRenderTargetRenderPass(this._currentRenderTarget!, false, null, false, false);
3563+
this.unBindFramebuffer(texture);
3564+
3565+
texture.disableAutomaticMSAAResolve = disableAutomaticMSAAResolve;
3566+
}
3567+
3568+
if (texture.resolveMSAADepth && texture._depthStencilTexture) {
3569+
const gpuTextureWrapper = texture._depthStencilTexture._hardwareTexture as WebGPUHardwareTexture;
3570+
3571+
this._textureHelper.resolveMSAADepthTexture(gpuTextureWrapper.getMSAATexture(0)!, gpuTextureWrapper.underlyingResource!, this._renderEncoder);
3572+
}
35353573
}
35363574

35373575
/**

0 commit comments

Comments
 (0)