Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes and workarounds to bring multiple examples to work on WebGPU #5170

Merged
merged 2 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/src/examples/graphics/clustered-area-lights.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Observer } from '@playcanvas/observer';
class AreaLightsExample {
static CATEGORY = 'Graphics';
static NAME = 'Clustered Area Lights';
static WEBGPU_ENABLED = true;

controls(data: Observer) {
return <>
Expand Down
6 changes: 5 additions & 1 deletion examples/src/examples/graphics/ground-fog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Observer } from '@playcanvas/observer';
class GroundFogExample {
static CATEGORY = 'Graphics';
static NAME = 'Ground Fog';
static WEBGPU_ENABLED = true;

static FILES = {
'shader.vert': /* glsl */ `
Expand Down Expand Up @@ -113,7 +114,10 @@ class GroundFogExample {
const gfxOptions = {
deviceTypes: [deviceType],
glslangUrl: '/static/lib/glslang/glslang.js',
twgslUrl: '/static/lib/twgsl/twgsl.js'
twgslUrl: '/static/lib/twgsl/twgsl.js',

// WebGPU does not currently support antialiased depth resolve, disable it till we implement a shader resolve solution
antialias: false
};

pc.createGraphicsDevice(canvas, gfxOptions).then((device: pc.GraphicsDevice) => {
Expand Down
6 changes: 5 additions & 1 deletion examples/src/examples/graphics/post-effects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Observer } from '@playcanvas/observer';
class PostEffectsExample {
static CATEGORY = 'Graphics';
static NAME = 'Post Effects';
static WEBGPU_ENABLED = true;

controls(data: Observer) {
return <>
Expand Down Expand Up @@ -102,7 +103,10 @@ class PostEffectsExample {
const gfxOptions = {
deviceTypes: [deviceType],
glslangUrl: '/static/lib/glslang/glslang.js',
twgslUrl: '/static/lib/twgsl/twgsl.js'
twgslUrl: '/static/lib/twgsl/twgsl.js',

// WebGPU does not currently support antialiased depth resolve, disable it till we implement a shader resolve solution
antialias: false
};

pc.createGraphicsDevice(canvas, gfxOptions).then((device: pc.GraphicsDevice) => {
Expand Down
5 changes: 5 additions & 0 deletions src/platform/graphics/graphics-device-create.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { WebglGraphicsDevice } from './webgl/webgl-graphics-device.js';
* specified array does not contain [{@link DEVICETYPE_WEBGL2} or {@link DEVICETYPE_WEBGL1}], those
* are internally added to its end in this order. Typically, you'd only specify
* {@link DEVICETYPE_WEBGPU}, or leave it empty.
* @param {boolean} [options.antialias] - Boolean that indicates whether or not to perform
* anti-aliasing if possible. Defaults to true.
* @param {string} [options.glslangUrl] - An url to glslang script, required if
* {@link DEVICETYPE_WEBGPU} type is added to deviceTypes array. Not used for
* {@link DEVICETYPE_WEBGL} device type creation.
Expand All @@ -22,6 +24,9 @@ import { WebglGraphicsDevice } from './webgl/webgl-graphics-device.js';
*/
function createGraphicsDevice(canvas, options = {}) {

// defaults
options.antialias ??= true;

const deviceTypes = options.deviceTypes ?? [];

// automatically added fallbacks
Expand Down
29 changes: 29 additions & 0 deletions src/platform/graphics/graphics-device.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,55 +28,63 @@ class GraphicsDevice extends EventHandler {
* The canvas DOM element that provides the underlying WebGL context used by the graphics device.
*
* @type {HTMLCanvasElement}
* @readonly
*/
canvas;

/**
* True if the deviceType is WebGPU
*
* @type {boolean}
* @readonly
*/
isWebGPU = false;

/**
* The scope namespace for shader attributes and variables.
*
* @type {ScopeSpace}
* @readonly
*/
scope;

/**
* The maximum number of supported bones using uniform buffers.
*
* @type {number}
* @readonly
*/
boneLimit;

/**
* The maximum supported texture anisotropy setting.
*
* @type {number}
* @readonly
*/
maxAnisotropy;

/**
* The maximum supported dimension of a cube map.
*
* @type {number}
* @readonly
*/
maxCubeMapSize;

/**
* The maximum supported dimension of a texture.
*
* @type {number}
* @readonly
*/
maxTextureSize;

/**
* The maximum supported dimension of a 3D texture (any axis).
*
* @type {number}
* @readonly
*/
maxVolumeSize;

Expand All @@ -85,9 +93,18 @@ class GraphicsDevice extends EventHandler {
* 'lowp'.
*
* @type {string}
* @readonly
*/
precision;

/**
* The number of hardware anti-aliasing samples used by the frame buffer.
*
* @readonly
* @type {number}
*/
samples;

/**
* Currently active render target.
*
Expand All @@ -96,13 +113,22 @@ class GraphicsDevice extends EventHandler {
*/
renderTarget = null;

/**
* Index of the currently active render pass.
*
* @type {number}
* @ignore
*/
renderPassIndex;

/** @type {boolean} */
insideRenderPass = false;

/**
* True if hardware instancing is supported.
*
* @type {boolean}
* @readonly
*/
supportsInstancing;

Expand All @@ -118,13 +144,15 @@ class GraphicsDevice extends EventHandler {
* True if 32-bit floating-point textures can be used as a frame buffer.
*
* @type {boolean}
* @readonly
*/
textureFloatRenderable;

/**
* True if 16-bit floating-point textures can be used as a frame buffer.
*
* @type {boolean}
* @readonly
*/
textureHalfFloatRenderable;

Expand Down Expand Up @@ -539,6 +567,7 @@ class GraphicsDevice extends EventHandler {
* @ignore
*/
frameStart() {
this.renderPassIndex = 0;
}
}

Expand Down
56 changes: 56 additions & 0 deletions src/platform/graphics/render-pass.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Debug } from '../../core/debug.js';
import { Tracing } from '../../core/tracing.js';
import { Color } from '../../core/math/color.js';
import { TRACEID_RENDER_PASS, TRACEID_RENDER_PASS_DETAIL } from '../../core/constants.js';
import { DebugGraphics } from '../graphics/debug-graphics.js';

class ColorAttachmentOps {
Expand Down Expand Up @@ -227,6 +230,10 @@ class RenderPass {
const realPass = this.renderTarget !== undefined;
DebugGraphics.pushGpuMarker(device, `Pass:${this.name}`);

Debug.call(() => {
this.log(device, device.renderPassIndex);
});

this.before?.();

if (realPass) {
Expand All @@ -241,9 +248,58 @@ class RenderPass {

this.after?.();

device.renderPassIndex++;

DebugGraphics.popGpuMarker(device);

}

// #if _DEBUG
log(device, index) {
if (Tracing.get(TRACEID_RENDER_PASS) || Tracing.get(TRACEID_RENDER_PASS_DETAIL)) {

let rt = this.renderTarget;
if (rt === null && device.isWebGPU) {
rt = device.frameBuffer;
}
const hasColor = rt?.colorBuffer ?? rt?.impl.assignedColorTexture;
const hasDepth = rt?.depth;
const hasStencil = rt?.stencil;
const rtInfo = rt === undefined ? '' : ` RT: ${(rt ? rt.name : 'NULL')} ` +
`${hasColor ? '[Color]' : ''}` +
`${hasDepth ? '[Depth]' : ''}` +
`${hasStencil ? '[Stencil]' : ''}` +
`${(this.samples > 0 ? ' samples: ' + this.samples : '')}`;

Debug.trace(TRACEID_RENDER_PASS,
`${index.toString().padEnd(2, ' ')}: ${this.name.padEnd(20, ' ')}` +
rtInfo.padEnd(30));

if (this.colorOps && hasColor) {
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` colorOps: ` +
`${this.colorOps.clear ? 'clear' : 'load'}->` +
`${this.colorOps.store ? 'store' : 'discard'} ` +
`${this.colorOps.resolve ? 'resolve ' : ''}` +
`${this.colorOps.mipmaps ? 'mipmaps ' : ''}`);
}

if (this.depthStencilOps) {

if (hasDepth) {
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` depthOps: ` +
`${this.depthStencilOps.clearDepth ? 'clear' : 'load'}->` +
`${this.depthStencilOps.storeDepth ? 'store' : 'discard'}`);
}

if (hasStencil) {
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` stencOps: ` +
`${this.depthStencilOps.clearStencil ? 'clear' : 'load'}->` +
`${this.depthStencilOps.storeStencil ? 'store' : 'discard'}`);
}
}
}
}
// #endif
}

export { RenderPass, ColorAttachmentOps, DepthStencilAttachmentOps };
9 changes: 7 additions & 2 deletions src/platform/graphics/webgpu/webgpu-graphics-device.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
this.isWebGPU = true;
this._deviceType = DEVICETYPE_WEBGPU;

// WebGPU currently only supports 1 and 4 samples
this.samples = options.antialias ? 4 : 1;

this.initDeviceCaps();
}

Expand Down Expand Up @@ -198,7 +201,7 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
name: 'WebgpuFramebuffer',
graphicsDevice: this,
depth: true,
samples: 4
samples: this.samples
});
}

Expand All @@ -216,6 +219,8 @@ class WebgpuGraphicsDevice extends GraphicsDevice {

frameStart() {

super.frameStart();

WebgpuDebug.memory(this);
WebgpuDebug.validate(this);

Expand Down Expand Up @@ -587,7 +592,7 @@ class WebgpuGraphicsDevice extends GraphicsDevice {
// cannot copy depth from multisampled buffer. On WebGPU, it cannot be resolve at the end of the pass either,
// and so we need to implement a custom depth resolve shader based copy
// This is currently needed for uSceneDepthMap when the camera renders to multisampled render target
Debug.assert(source.samples <= 1, `copyRenderTarget does not currently support copy of depth from multisampled texture`, sourceRT);
Debug.assert(source.samples <= 1, `copyRenderTarget does not currently support copy of depth from multisampled texture ${sourceRT.name}`, sourceRT);

/** @type {GPUImageCopyTexture} */
const copySrc = {
Expand Down
6 changes: 6 additions & 0 deletions src/platform/graphics/webgpu/webgpu-render-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class WebgpuRenderTarget {
this.assignedColorTexture = gpuTexture;

const view = gpuTexture.createView();
DebugHelper.setLabel(view, 'Framebuffer.assignedColor');

// use it as render buffer or resolve target
const colorAttachment = this.renderPassDescriptor.colorAttachments[0];
Expand All @@ -145,6 +146,8 @@ class WebgpuRenderTarget {
init(device, renderTarget) {

Debug.assert(!this.initialized);
Debug.assert(this.renderPassDescriptor, 'The render target has been destroyed and cannot be used anymore.', { renderTarget });

const wgpu = device.wgpu;

WebgpuDebug.memory(device);
Expand Down Expand Up @@ -234,8 +237,11 @@ class WebgpuRenderTarget {

// allocate multi-sampled color buffer
this.multisampledColorBuffer = wgpu.createTexture(multisampledTextureDesc);
DebugHelper.setLabel(this.multisampledColorBuffer, `${renderTarget.name}.multisampledColor`);

colorAttachment.view = this.multisampledColorBuffer.createView();
DebugHelper.setLabel(colorAttachment.view, `${renderTarget.name}.multisampledColorView`);

colorAttachment.resolveTarget = colorView;

} else {
Expand Down
Loading