浏览代码

Add suport for multiple render textures to webgpu

Popov72 4 年之前
父节点
当前提交
70ec2fdd04

+ 1 - 1
src/Engines/WebGPU/webgpuTextureHelper.ts

@@ -841,7 +841,7 @@ export class WebGPUTextureHelper {
         gpuTextureWrapper.format = this.getWebGPUTextureFormat(texture.type, texture.format);
 
         gpuTextureWrapper.textureUsages =
-            texture._source === InternalTextureSource.RenderTarget ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment :
+            texture._source === InternalTextureSource.RenderTarget || texture.source === InternalTextureSource.MultiRenderTarget ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment :
             texture._source === InternalTextureSource.Depth ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.OutputAttachment : -1;
 
         const hasMipMaps = texture.generateMipMaps;

+ 1 - 1
src/Engines/constants.ts

@@ -171,7 +171,7 @@ export class Constants {
     /** Compressed BC2 */
     public static readonly TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3 = 33778;
     /** Compressed BC1 */
-    public static readonly TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1 = 33777;
+    public static readonly TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1 = 33776; // fix this when merging
 
     /** UNSIGNED_BYTE */
     public static readonly TEXTURETYPE_UNSIGNED_BYTE = 0;

+ 215 - 11
src/Engines/webgpuEngine.ts

@@ -32,6 +32,7 @@ import { IColor4Like } from '../Maths/math.like';
 import { IWebRequest } from '../Misc/interfaces/iWebRequest';
 import { UniformBuffer } from '../Materials/uniformBuffer';
 import { WebGPURenderPassWrapper } from './WebGPU/webgpuRenderPassWrapper';
+import { IMultiRenderTargetOptions } from '../Materials/Textures/multiRenderTarget';
 
 declare type VideoTexture = import("../Materials/Textures/videoTexture").VideoTexture;
 declare type RenderTargetTexture = import("../Materials/Textures/renderTargetTexture").RenderTargetTexture;
@@ -172,6 +173,7 @@ export class WebGPUEngine extends Engine {
     private _lastCachedWrapU: number;
     private _lastCachedWrapV: number;
     private _lastCachedWrapR: number;
+    private _mrtAttachments: number[];
     private _counters: {
         numPipelineDescriptorCreation: number;
         numBindGroupsCreation: number;
@@ -1384,7 +1386,7 @@ export class WebGPUEngine extends Engine {
                 this._textureHelper.createGPUTextureForInternalTexture(texture);
             }
 
-            this._generateMipmaps(texture, texture.source === InternalTextureSource.RenderTarget ? this._renderTargetEncoder : undefined);
+            this._generateMipmaps(texture, texture.source === InternalTextureSource.RenderTarget || texture.source === InternalTextureSource.MultiRenderTarget ? this._renderTargetEncoder : undefined);
         }
     }
 
@@ -1930,6 +1932,105 @@ export class WebGPUEngine extends Engine {
         return texture;
     }
 
+    public createMultipleRenderTarget(size: any, options: IMultiRenderTargetOptions): InternalTexture[] {
+        let generateMipMaps = false;
+        let generateDepthBuffer = true;
+        let generateStencilBuffer = false;
+        let generateDepthTexture = false;
+        let textureCount = 1;
+
+        let defaultType = Constants.TEXTURETYPE_UNSIGNED_INT;
+        let defaultSamplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
+
+        let types = new Array<number>();
+        let samplingModes = new Array<number>();
+
+        if (options !== undefined) {
+            generateMipMaps = options.generateMipMaps === undefined ? false : options.generateMipMaps;
+            generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
+            generateStencilBuffer = options.generateStencilBuffer === undefined ? false : options.generateStencilBuffer;
+            generateDepthTexture = options.generateDepthTexture === undefined ? false : options.generateDepthTexture;
+            textureCount = options.textureCount || 1;
+
+            if (options.types) {
+                types = options.types;
+            }
+            if (options.samplingModes) {
+                samplingModes = options.samplingModes;
+            }
+
+        }
+
+        const width = size.width || size;
+        const height = size.height || size;
+
+        let depthStencilTexture = null;
+        if (generateDepthBuffer || generateStencilBuffer || generateDepthTexture) {
+            depthStencilTexture = this.createDepthStencilTexture({ width, height }, {
+                bilinearFiltering: false,
+                comparisonFunction: 0,
+                generateStencil: generateStencilBuffer,
+                isCube: false,
+                samples: 1,
+            });
+        }
+
+        const textures = [];
+        const attachments = [];
+
+        for (let i = 0; i < textureCount; i++) {
+            let samplingMode = samplingModes[i] || defaultSamplingMode;
+            let type = types[i] || defaultType;
+
+            if (type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloatLinearFiltering) {
+                // if floating point linear (gl.FLOAT) then force to NEAREST_SAMPLINGMODE
+                samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
+            }
+            else if (type === Constants.TEXTURETYPE_HALF_FLOAT && !this._caps.textureHalfFloatLinearFiltering) {
+                // if floating point linear (HALF_FLOAT) then force to NEAREST_SAMPLINGMODE
+                samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
+            }
+
+            if (type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloat) {
+                type = Constants.TEXTURETYPE_UNSIGNED_INT;
+                Logger.Warn("Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type");
+            }
+
+            const texture = new InternalTexture(this, InternalTextureSource.MultiRenderTarget);
+
+            textures.push(texture);
+            attachments.push(i + 1);
+
+            texture._depthStencilTexture = i === 0 ? depthStencilTexture : null;
+            texture._framebuffer = {};
+            texture._depthStencilBuffer = {};
+            texture.baseWidth = width;
+            texture.baseHeight = height;
+            texture.width = width;
+            texture.height = height;
+            texture.isReady = true;
+            texture.samples = 1;
+            texture.generateMipMaps = generateMipMaps;
+            texture.samplingMode = samplingMode;
+            texture.type = type;
+            texture._generateDepthBuffer = generateDepthBuffer;
+            texture._generateStencilBuffer = generateStencilBuffer ? true : false;
+            texture._attachments = attachments;
+            texture._textureArray = textures;
+
+            this._internalTexturesCache.push(texture);
+
+            this._textureHelper.createGPUTextureForInternalTexture(texture);
+        }
+
+        if (depthStencilTexture) {
+            textures.push(depthStencilTexture);
+            this._internalTexturesCache.push(depthStencilTexture);
+        }
+
+        return textures;
+    }
+
     public createRenderTargetCubeTexture(size: number, options?: Partial<RenderTargetCreationOptions>): InternalTexture {
         let fullOptions = {
             generateMipMaps: true,
@@ -2022,6 +2123,7 @@ export class WebGPUEngine extends Engine {
             ...options
         };
 
+        // TODO WEBGPU allow to choose the format?
         internalTexture.format = internalOptions.generateStencil ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT;
 
         this._setupDepthStencilTexture(internalTexture, size, internalOptions.generateStencil, internalOptions.bilinearFiltering, internalOptions.comparisonFunction, internalOptions.samples);
@@ -2045,6 +2147,7 @@ export class WebGPUEngine extends Engine {
             ...options
         };
 
+        // TODO WEBGPU allow to choose the format?
         internalTexture.format = internalOptions.generateStencil ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT;
 
         this._setupDepthStencilTexture(internalTexture, size, internalOptions.generateStencil, internalOptions.bilinearFiltering, internalOptions.comparisonFunction, internalOptions.samples);
@@ -2073,6 +2176,24 @@ export class WebGPUEngine extends Engine {
         return samples;
     }
 
+    public updateMultipleRenderTargetTextureSampleCount(textures: Nullable<InternalTexture[]>, samples: number): number {
+        if (!textures || textures[0].samples === samples) {
+            return samples;
+        }
+
+        samples = Math.min(samples, this.getCaps().maxMSAASamples);
+
+        // Note that the last texture of textures is the depth texture (if the depth texture has been generated by the MRT class) and so the MSAA texture
+        // will be recreated for this texture too. As a consequence, there's no need to explicitely recreate the MSAA texture for textures[0]._depthStencilTexture
+        for (let i = 0; i < textures.length; ++i) {
+            const texture = textures[i];
+            this._textureHelper.createMSAATexture(texture, samples);
+            texture.samples = samples;
+        }
+
+        return samples;
+    }
+
     //------------------------------------------------------------------------------
     //                              Render Commands
     //------------------------------------------------------------------------------
@@ -2129,7 +2250,7 @@ export class WebGPUEngine extends Engine {
                 (this as any)._count++;
                 if ((this as any)._count !== dbgVerboseLogsNumFrames) {
                     console.log("%c frame #" + (this as any)._count + " - begin", "background: #ffff00");
-                    }
+                }
             }
         }
     }
@@ -2176,27 +2297,60 @@ export class WebGPUEngine extends Engine {
     private _startRenderTargetRenderPass(internalTexture: InternalTexture, clearColor: Nullable<IColor4Like>, clearDepth: boolean, clearStencil: boolean = false) {
         const gpuWrapper = internalTexture._hardwareTexture as WebGPUHardwareTexture;
         const gpuTexture = gpuWrapper.underlyingResource!;
-        const gpuMSAATexture = gpuWrapper.msaaTexture;
 
         const depthStencilTexture = internalTexture._depthStencilTexture;
         const gpuDepthStencilWrapper = depthStencilTexture?._hardwareTexture as Nullable<WebGPUHardwareTexture>;
         const gpuDepthStencilTexture = gpuDepthStencilWrapper?.underlyingResource as Nullable<GPUTexture>;
         const gpuDepthStencilMSAATexture = gpuDepthStencilWrapper?.msaaTexture;
 
-        const colorTextureView = gpuTexture.createView(this._rttRenderPassWrapper.colorAttachmentViewDescriptor!);
-        const colorMSAATextureView = gpuMSAATexture?.createView(this._rttRenderPassWrapper.colorAttachmentViewDescriptor!);
         const depthTextureView = gpuDepthStencilTexture?.createView(this._rttRenderPassWrapper.depthAttachmentViewDescriptor!);
         const depthMSAATextureView = gpuDepthStencilMSAATexture?.createView(this._rttRenderPassWrapper.depthAttachmentViewDescriptor!);
 
-        this._debugPushGroup("render target pass", 1);
+        const colorAttachments: GPURenderPassColorAttachmentDescriptor[] = [];
+
+        if (internalTexture._attachments && internalTexture._textureArray) {
+            // multi render targets
+            for (let i = 0; i < internalTexture._attachments.length; ++i) {
+                const index = internalTexture._attachments[i];
+                if (index > 0) {
+                    const mrtTexture = internalTexture._textureArray[index - 1];
+                    const gpuMRTWrapper = mrtTexture?._hardwareTexture as Nullable<WebGPUHardwareTexture>;
+                    const gpuMRTTexture = gpuMRTWrapper?.underlyingResource;
+                    if (gpuMRTWrapper && gpuMRTTexture) {
+                        const gpuMSAATexture = gpuMRTWrapper.msaaTexture;
+                        const colorTextureView = gpuMRTTexture.createView(this._rttRenderPassWrapper.colorAttachmentViewDescriptor!);
+                        const colorMSAATextureView = gpuMSAATexture?.createView(this._rttRenderPassWrapper.colorAttachmentViewDescriptor!);
+
+                        colorAttachments.push({
+                            attachment: colorMSAATextureView ? colorMSAATextureView : colorTextureView,
+                            resolveTarget: gpuMSAATexture ? colorTextureView : undefined,
+                            loadValue: clearColor !== null ? clearColor : WebGPUConstants.LoadOp.Load,
+                            storeOp: WebGPUConstants.StoreOp.Store,
+                        });
+                    }
+                } else {
+                    // TODO WEBGPU what to do?
+                }
+            }
+            this._mrtAttachments = internalTexture._attachments;
+        } else {
+            // single render target
+            const gpuMSAATexture = gpuWrapper.msaaTexture;
+            const colorTextureView = gpuTexture.createView(this._rttRenderPassWrapper.colorAttachmentViewDescriptor!);
+            const colorMSAATextureView = gpuMSAATexture?.createView(this._rttRenderPassWrapper.colorAttachmentViewDescriptor!);
 
-        this._rttRenderPassWrapper.renderPassDescriptor = {
-            colorAttachments: [{
+            colorAttachments.push({
                 attachment: colorMSAATextureView ? colorMSAATextureView : colorTextureView,
                 resolveTarget: gpuMSAATexture ? colorTextureView : undefined,
                 loadValue: clearColor !== null ? clearColor : WebGPUConstants.LoadOp.Load,
                 storeOp: WebGPUConstants.StoreOp.Store,
-            }],
+            });
+        }
+
+        this._debugPushGroup("render target pass", 1);
+
+        this._rttRenderPassWrapper.renderPassDescriptor = {
+            colorAttachments,
             depthStencilAttachment: depthStencilTexture && gpuDepthStencilTexture ? {
                 attachment: depthMSAATextureView ? depthMSAATextureView : depthTextureView!,
                 depthLoadValue: clearDepth && depthStencilTexture._generateDepthBuffer ? this._clearDepthValue : WebGPUConstants.LoadOp.Load,
@@ -2246,6 +2400,10 @@ export class WebGPUEngine extends Engine {
         return this._currentRenderPass!;
     }
 
+    private _currentRenderPassIsMRT(): boolean {
+        return !!this._currentRenderTarget?._attachments ?? false;
+    }
+
     private _startMainRenderPass(): void {
         if (this._mainRenderPassWrapper.renderPass) {
             this._endMainRenderPass();
@@ -2296,6 +2454,10 @@ export class WebGPUEngine extends Engine {
         }
     }
 
+    public bindAttachments(attachments: number[]): void {
+        this._mrtAttachments = attachments;
+    }
+
     public bindFramebuffer(texture: InternalTexture, faceIndex: number = 0, requiredWidth?: number, requiredHeight?: number, forceFullscreenViewport?: boolean, lodLevel = 0, layer = 0): void {
         const hardwareTexture = texture._hardwareTexture as Nullable<WebGPUHardwareTexture>;
         const gpuTexture = hardwareTexture?.underlyingResource as Nullable<GPUTexture>;
@@ -2391,6 +2553,32 @@ export class WebGPUEngine extends Engine {
         this._setColorFormat(this._mainRenderPassWrapper);
     }
 
+    public unBindMultiColorAttachmentFramebuffer(textures: InternalTexture[], disableGenerateMipMaps: boolean = false, onBeforeUnbind?: () => void): void {
+        if (onBeforeUnbind) {
+            onBeforeUnbind();
+        }
+
+        const attachments = textures[0]._attachments!;
+        const count = attachments.length;
+
+        if (this._currentRenderPass && this._currentRenderPass !== this._mainRenderPassWrapper.renderPass) {
+            this._endRenderTargetRenderPass();
+        }
+
+        for (let i = 0; i < count; i++) {
+            const texture = textures[i];
+            if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
+                this._generateMipmaps(texture);
+            }
+        }
+
+        this._currentRenderTarget = null;
+
+        this._currentRenderPass = this._mainRenderPassWrapper.renderPass;
+        this._setDepthTextureFormat(this._mainRenderPassWrapper);
+        this._setColorFormat(this._mainRenderPassWrapper);
+    }
+
     public restoreDefaultFramebuffer(): void {
         if (this._currentRenderTarget) {
             this.unBindFramebuffer(this._currentRenderTarget);
@@ -2831,12 +3019,28 @@ export class WebGPUEngine extends Engine {
     }
 
     private _getColorStateDescriptors(): GPUColorStateDescriptor[] {
-        return [{
+        const descriptor: GPUColorStateDescriptor = {
             format: this._colorFormat,
             alphaBlend: this._getAphaBlendState(),
             colorBlend: this._getColorBlendState(),
             writeMask: this._getWriteMask(),
-        }];
+        };
+
+        const descriptors: GPUColorStateDescriptor[] = [];
+
+        if (this._currentRenderPassIsMRT()) {
+            for (let i = 0; i < this._mrtAttachments.length; ++i) {
+                if (this._mrtAttachments[i] > 0) {
+                    descriptors.push(descriptor);
+                } else {
+                    // TODO WEBGPU what to do when this._mrtAttachments[i] === 0? The corresponding texture should be bound as an "empty" texture
+                }
+            }
+        } else {
+            descriptors.push(descriptor);
+        }
+
+        return descriptors;
     }
 
     private _getStages(): IWebGPURenderPipelineStageDescriptor {

+ 1 - 1
src/PostProcesses/RenderPipeline/Pipelines/ssao2RenderingPipeline.ts

@@ -198,7 +198,7 @@ export class SSAO2RenderingPipeline extends PostProcessRenderPipeline {
         this._forceGeometryBuffer = forceGeometryBuffer;
 
         if (!this.isSupported) {
-            Logger.Error("SSAO 2 needs WebGL 2 support.");
+            Logger.Error("The current engine does not support SSAO 2.");
             return;
         }
 

+ 1 - 1
src/Rendering/geometryBufferRenderer.ts

@@ -220,7 +220,7 @@ export class GeometryBufferRenderer {
         // Alpha test
         if (material) {
             let needUv = false;
-            if (material.needAlphaTesting()) {
+            if (material.needAlphaTesting() && material.getAlphaTestTexture()) {
                 defines.push("#define ALPHATEST");
                 needUv = true;
             }

+ 1 - 1
src/Shaders/ShadersInclude/mrtFragmentDeclaration.fx

@@ -1,3 +1,3 @@
-#if __VERSION__ >= 200
+#if defined(WEBGL2) || defined(WEBGPU)
 layout(location = 0) out vec4 glFragData[{X}];
 #endif

+ 1 - 1
src/Shaders/geometry.fragment.fx

@@ -34,7 +34,7 @@ uniform vec3 vBumpInfos;
 uniform vec2 vTangentSpaceParams;
 #endif
 
-#ifdef REFLECTIVITY
+#if defined(REFLECTIVITY) && (defined(HAS_SPECULAR) || defined(HAS_REFLECTIVITY))
 varying vec2 vReflectivityUV;
 uniform sampler2D reflectivitySampler;
 #endif

+ 6 - 1
src/Shaders/screenSpaceReflection.fragment.fx

@@ -2,9 +2,11 @@
 // http://imanolfotia.com/blog/update/2017/03/11/ScreenSpaceReflections.html
 
 uniform sampler2D textureSampler;
+#ifdef SSR_SUPPORTED
+uniform sampler2D reflectivitySampler;
 uniform sampler2D normalSampler;
 uniform sampler2D positionSampler;
-uniform sampler2D reflectivitySampler;
+#endif
 
 uniform mat4 view;
 uniform mat4 projection;
@@ -18,6 +20,8 @@ uniform float reflectionSpecularFalloffExponent;
 // Varyings
 varying vec2 vUV;
 
+#ifdef SSR_SUPPORTED
+
 // Structs
 struct ReflectionInfo {
     vec3 color;
@@ -121,6 +125,7 @@ vec3 hash(vec3 a)
     a += dot(a, a.yxz + 19.19);
     return fract((a.xxy + a.yxx) * a.zyx);
 }
+#endif
 
 void main()
 {