Skip to content

Commit

Permalink
Depth prepass now works with multisampling on WebGPU (playcanvas#6945)
Browse files Browse the repository at this point in the history
* Depth prepass now works with multisampling on WebGPU

* cleanup

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
  • Loading branch information
mvaligursky and Martin Valigursky authored Sep 11, 2024
1 parent c56b88c commit af325c8
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ assetListLoader.load(() => {
// ------ Custom render passes set up ------

const currentOptions = new pc.CameraFrameOptions();
currentOptions.samples = 1;
currentOptions.samples = 4;
currentOptions.sceneColorMap = false;
currentOptions.ssaoType = pc.SSAOTYPE_LIGHTING;
currentOptions.ssaoBlurEnabled = true;
Expand Down
21 changes: 13 additions & 8 deletions src/extras/render-passes/render-pass-camera-frame.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { LAYERID_SKYBOX, LAYERID_IMMEDIATE, TONEMAP_NONE, GAMMA_NONE } from '../../scene/constants.js';
import {
ADDRESS_CLAMP_TO_EDGE,
FILTER_LINEAR,
FILTER_NEAREST,
PIXELFORMAT_DEPTH,
PIXELFORMAT_RGBA8
ADDRESS_CLAMP_TO_EDGE, FILTER_LINEAR, FILTER_NEAREST,
PIXELFORMAT_DEPTH, PIXELFORMAT_R32F, PIXELFORMAT_RGBA8
} from '../../platform/graphics/constants.js';
import { Texture } from '../../platform/graphics/texture.js';
import { RenderPass } from '../../platform/graphics/render-pass.js';
Expand Down Expand Up @@ -205,11 +202,19 @@ class RenderPassCameraFrame extends RenderPass {
addressV: ADDRESS_CLAMP_TO_EDGE
});

// TODO: handle stencil support
let depthFormat = PIXELFORMAT_DEPTH;
if (options.prepassEnabled && device.isWebGPU && options.samples > 1) {
// on WebGPU the depth format cannot be resolved, so we need to use a float format in that case
// TODO: ideally we expose this using some option or similar public API to hide this implementation detail
depthFormat = PIXELFORMAT_R32F;
}

this.sceneDepth = new Texture(device, {
name: 'SceneDepth',
width: 4,
height: 4,
format: PIXELFORMAT_DEPTH, // TODO: handle stencil support
format: depthFormat,
mipmaps: false,
minFilter: FILTER_NEAREST,
magFilter: FILTER_NEAREST,
Expand Down Expand Up @@ -271,8 +276,8 @@ class RenderPassCameraFrame extends RenderPass {
const { app, device, cameraComponent } = this;
const { scene, renderer } = app;

// ssao needs resolved depth
const resolveDepth = this.options.ssaoType !== SSAOTYPE_NONE;
// ssao & taa need resolved depth
const resolveDepth = this.options.ssaoType !== SSAOTYPE_NONE || this.options.taaEnabled;

this.prePass = new RenderPassPrepass(device, scene, renderer, cameraComponent, this.sceneDepth, resolveDepth, this.sceneOptions, options.samples);
}
Expand Down
33 changes: 19 additions & 14 deletions src/platform/graphics/render-target.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Debug } from '../../core/debug.js';
import { TRACEID_RENDER_TARGET_ALLOC } from '../../core/constants.js';
import { PIXELFORMAT_DEPTH, PIXELFORMAT_DEPTHSTENCIL, isSrgbPixelFormat } from './constants.js';
import { PIXELFORMAT_DEPTH, PIXELFORMAT_DEPTHSTENCIL, PIXELFORMAT_R32F, isSrgbPixelFormat } from './constants.js';
import { DebugGraphics } from './debug-graphics.js';
import { GraphicsDevice } from './graphics-device.js';

Expand Down Expand Up @@ -135,6 +135,19 @@ class RenderTarget {
Debug.assert(!(options instanceof GraphicsDevice), 'pc.RenderTarget constructor no longer accepts GraphicsDevice parameter.');
this.id = id++;

// device, from one of the buffers
const device = options.colorBuffer?.device || options.depthBuffer?.device || options.graphicsDevice;
Debug.assert(device, 'Failed to obtain the device, colorBuffer nor depthBuffer store it.');
this._device = device;

// samples
const { maxSamples } = this._device;
this._samples = Math.min(options.samples ?? 1, maxSamples);
if (device.isWebGPU) {
// WebGPU only supports values of 1 or 4 for samples
this._samples = this._samples > 1 ? maxSamples : 1;
}

// Use the single colorBuffer in the colorBuffers array. This allows us to always just use the array internally.
this._colorBuffer = options.colorBuffer;
if (options.colorBuffer) {
Expand All @@ -153,6 +166,11 @@ class RenderTarget {
} else if (format === PIXELFORMAT_DEPTHSTENCIL) {
this._depth = true;
this._stencil = true;
} else if (format === PIXELFORMAT_R32F && this._depthBuffer.device.isWebGPU && this._samples > 1) {
// on WebGPU, when multisampling is enabled, we use R32F format for the specified buffer,
// which we can resolve depth to using a shader
this._depth = true;
this._stencil = false;
} else {
Debug.warn('Incorrect depthBuffer format. Must be pc.PIXELFORMAT_DEPTH or pc.PIXELFORMAT_DEPTHSTENCIL');
this._depth = false;
Expand All @@ -175,19 +193,6 @@ class RenderTarget {
}
}

// device, from one of the buffers
const device = this._colorBuffer?.device || this._depthBuffer?.device || options.graphicsDevice;
Debug.assert(device, 'Failed to obtain the device, colorBuffer nor depthBuffer store it.');
this._device = device;

const { maxSamples } = this._device;
this._samples = Math.min(options.samples ?? 1, maxSamples);

// WebGPU only supports values of 1 or 4 for samples
if (device.isWebGPU) {
this._samples = this._samples > 1 ? maxSamples : 1;
}

this.autoResolve = options.autoResolve ?? true;

// use specified name, otherwise get one from color or depth buffer
Expand Down
16 changes: 16 additions & 0 deletions src/platform/graphics/webgpu/webgpu-graphics-device.js
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,22 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
// each render pass can use different number of bind groups
this.bindGroupFormats.length = 0;

// resolve depth if needed after the pass has finished
const target = this.renderTarget;
if (target) {

// resolve depth buffer (stencil resolve is not yet implemented)
if (target.depthBuffer && renderPass.depthStencilOps.resolveDepth) {
if (renderPass.samples > 1 && target.autoResolve) {
const depthAttachment = target.impl.depthAttachment;
const destTexture = target.depthBuffer.impl.gpuTexture;
if (depthAttachment && destTexture) {
this.resolver.resolveDepth(this.commandEncoder, depthAttachment.multisampledDepthBuffer, destTexture);
}
}
}
}

// generate mipmaps using the same command buffer encoder
for (let i = 0; i < renderPass.colorArrayOps.length; i++) {
const colorOps = renderPass.colorArrayOps[i];
Expand Down
14 changes: 11 additions & 3 deletions src/platform/graphics/webgpu/webgpu-render-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,14 @@ class WebgpuRenderTarget {

if (samples > 1) { // create a multi-sampled depth buffer for the provided depth buffer

// single-sampled depthBuffer.impl.format can be R32F in some cases, but that cannot be used as a depth
// buffer, only as a texture to resolve it to. We always use depth24plus-stencil8 for msaa depth buffers.
const depthFormat = 'depth24plus-stencil8';
this.depthAttachment.format = depthFormat;
this.depthAttachment.hasStencil = depthFormat === 'depth24plus-stencil8';

// key for matching multi-sampled depth buffer
const key = `${depthBuffer.id}:${width}:${height}:${samples}:${depthBuffer.impl.format}`;
const key = `${depthBuffer.id}:${width}:${height}:${samples}:${depthFormat}`;

// check if we have already allocated a multi-sampled depth buffer for the depth buffer
const msTextures = getMultisampledTextureCache(device);
Expand All @@ -343,8 +349,10 @@ class WebgpuRenderTarget {
size: [width, height, 1],
dimension: '2d',
sampleCount: samples,
format: depthBuffer.impl.format,
usage: GPUTextureUsage.RENDER_ATTACHMENT
format: depthFormat,
usage: GPUTextureUsage.RENDER_ATTACHMENT |
// if msaa and resolve targets are different formats, we need to be able to bind the msaa target as a texture for manual shader resolve
(depthFormat !== depthBuffer.impl.format ? GPUTextureUsage.TEXTURE_BINDING : 0)
};

// allocate multi-sampled depth buffer
Expand Down

0 comments on commit af325c8

Please sign in to comment.