playcanvas
Version:
PlayCanvas WebGL game engine
139 lines (136 loc) • 6.1 kB
JavaScript
import { Shader } from '../shader.js';
import { SHADERLANGUAGE_WGSL } from '../constants.js';
import { DebugHelper, Debug } from '../../../core/debug.js';
import { DebugGraphics } from '../debug-graphics.js';
/**
* @import { WebgpuGraphicsDevice } from './webgpu-graphics-device.js'
* @import { WebgpuShader } from './webgpu-shader.js'
*/ /**
* A WebGPU helper class implementing custom resolve of multi-sampled textures.
*
* @ignore
*/ class WebgpuResolver {
destroy() {
this.shader.destroy();
this.shader = null;
this.pipelineCache = null;
}
/**
* @param {GPUTextureFormat} format - Texture format.
* @returns {GPURenderPipeline} Pipeline for the given format.
* @private
*/ getPipeline(format) {
var pipeline = this.pipelineCache.get(format);
if (!pipeline) {
pipeline = this.createPipeline(format);
this.pipelineCache.set(format, pipeline);
}
return pipeline;
}
/**
* @param {GPUTextureFormat} format - Texture format.
* @returns {GPURenderPipeline} Pipeline for the given format.
* @private
*/ createPipeline(format) {
/** @type {WebgpuShader} */ var webgpuShader = this.shader.impl;
var pipeline = this.device.wgpu.createRenderPipeline({
layout: 'auto',
vertex: {
module: webgpuShader.getVertexShaderModule(),
entryPoint: webgpuShader.vertexEntryPoint
},
fragment: {
module: webgpuShader.getFragmentShaderModule(),
entryPoint: webgpuShader.fragmentEntryPoint,
targets: [
{
format: format
}
]
},
primitive: {
topology: 'triangle-strip'
}
});
DebugHelper.setLabel(pipeline, "RenderPipeline-DepthResolver-" + format);
return pipeline;
}
/**
* @param {GPUCommandEncoder} commandEncoder - Command encoder to use for the resolve.
* @param {GPUTexture} sourceTexture - Source multi-sampled depth texture to resolve.
* @param {GPUTexture} destinationTexture - Destination depth texture to resolve to.
* @private
*/ resolveDepth(commandEncoder, sourceTexture, destinationTexture) {
Debug.assert(sourceTexture.sampleCount > 1);
Debug.assert(destinationTexture.sampleCount === 1);
Debug.assert(sourceTexture.depthOrArrayLayers === destinationTexture.depthOrArrayLayers);
var device = this.device;
var wgpu = device.wgpu;
// pipeline depends on the format
var pipeline = this.getPipeline(destinationTexture.format);
DebugGraphics.pushGpuMarker(device, 'DEPTH_RESOLVE-RENDERER');
var numFaces = sourceTexture.depthOrArrayLayers;
for(var face = 0; face < numFaces; face++){
// copy depth only (not stencil)
var srcView = sourceTexture.createView({
dimension: '2d',
aspect: 'depth-only',
baseMipLevel: 0,
mipLevelCount: 1,
baseArrayLayer: face
});
var dstView = destinationTexture.createView({
dimension: '2d',
baseMipLevel: 0,
mipLevelCount: 1,
baseArrayLayer: face
});
var passEncoder = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: dstView,
loadOp: 'clear',
storeOp: 'store'
}
]
});
DebugHelper.setLabel(passEncoder, 'DepthResolve-PassEncoder');
// no need for a sampler when using textureLoad
var bindGroup = wgpu.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: srcView
}
]
});
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.draw(4);
passEncoder.end();
}
DebugGraphics.popGpuMarker(device);
// clear invalidated state
device.pipeline = null;
}
constructor(device){
/**
* Cache of render pipelines for each texture format, to avoid their per frame creation.
*
* @type {Map<GPUTextureFormat, GPURenderPipeline>}
* @private
*/ this.pipelineCache = new Map();
this.device = device;
// Shader that renders a fullscreen textured quad and copies the depth value from sample index 0
// TODO: could handle all sample indices and use min/max as needed
var code = "\n \n var<private> pos : array<vec2f, 4> = array<vec2f, 4>(\n vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0)\n );\n\n struct VertexOutput {\n @builtin(position) position : vec4f,\n };\n\n @vertex\n fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {\n var output : VertexOutput;\n output.position = vec4f(pos[vertexIndex], 0, 1);\n return output;\n }\n\n @group(0) @binding(0) var img : texture_depth_multisampled_2d;\n\n @fragment\n fn fragmentMain(@builtin(position) fragColor: vec4f) -> @location(0) vec4f {\n // load th depth value from sample index 0\n var depth = textureLoad(img, vec2i(fragColor.xy), 0u);\n return vec4f(depth, 0.0, 0.0, 0.0);\n }\n ";
this.shader = new Shader(device, {
name: 'WebGPUResolverDepthShader',
shaderLanguage: SHADERLANGUAGE_WGSL,
vshader: code,
fshader: code
});
}
}
export { WebgpuResolver };