Explorar el Código

First take at render target texture

Popov72 hace 4 años
padre
commit
ed95972eae

+ 7 - 0
src/Engines/constants.ts

@@ -152,6 +152,13 @@ export class Constants {
     public static readonly TEXTUREFORMAT_RGB_INTEGER = 10;
     /** RGBA_INTEGER */
     public static readonly TEXTUREFORMAT_RGBA_INTEGER = 11;
+    /** BGRA */
+    public static readonly TEXTUREFORMAT_BGRA = 12;
+
+    /** Depth 24 bits + Stencil 8 bits */
+    public static readonly TEXTUREFORMAT_DEPTH24_STENCIL8 = 13;
+    /** Depth 32 bits float */
+    public static readonly TEXTUREFORMAT_DEPTH32_FLOAT = 14;
 
     /** Compressed BC7 */
     public static readonly TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM = 36492;

+ 4 - 4
src/Engines/engine.ts

@@ -1175,8 +1175,8 @@ export class Engine extends ThinEngine {
      * @param channel defines the channel to use
      * @param postProcess defines the source postprocess
      */
-    public setTextureFromPostProcess(channel: number, postProcess: Nullable<PostProcess>): void {
-        this._bindTexture(channel, postProcess ? postProcess._textures.data[postProcess._currentRenderTextureInd] : null);
+    public setTextureFromPostProcess(channel: number, postProcess: Nullable<PostProcess>, name: string): void {
+        this._bindTexture(channel, postProcess ? postProcess._textures.data[postProcess._currentRenderTextureInd] : null, name);
     }
 
     /**
@@ -1184,8 +1184,8 @@ export class Engine extends ThinEngine {
      * @param channel The channel the texture should be bound to
      * @param postProcess The post process which's output should be bound
      */
-    public setTextureFromPostProcessOutput(channel: number, postProcess: Nullable<PostProcess>): void {
-        this._bindTexture(channel, postProcess ? postProcess._outputTexture : null);
+    public setTextureFromPostProcessOutput(channel: number, postProcess: Nullable<PostProcess>, name: string): void {
+        this._bindTexture(channel, postProcess ? postProcess._outputTexture : null, name);
     }
 
     protected _rebuildBuffers(): void {

+ 4 - 2
src/Engines/thinEngine.ts

@@ -1280,7 +1280,7 @@ export class ThinEngine {
         this._gl.clear(mode);
     }
 
-    private _viewportCached = { x: 0, y: 0, z: 0, w: 0 };
+    protected _viewportCached = { x: 0, y: 0, z: 0, w: 0 };
 
     /** @hidden */
     public _viewport(x: number, y: number, width: number, height: number): void {
@@ -3591,6 +3591,8 @@ export class ThinEngine {
         if (texture._irradianceTexture) {
             texture._irradianceTexture.dispose();
         }
+
+        texture._depthStencilTexture?.dispose();
     }
 
     protected _deleteTexture(texture: Nullable<WebGLTexture>): void {
@@ -3670,7 +3672,7 @@ export class ThinEngine {
     }
 
     /** @hidden */
-    public _bindTexture(channel: number, texture: Nullable<InternalTexture>): void {
+    public _bindTexture(channel: number, texture: Nullable<InternalTexture>, name: string): void {
         if (channel === undefined) {
             return;
         }

+ 294 - 95
src/Engines/webgpuEngine.ts

@@ -32,6 +32,13 @@ import { IColor4Like } from '../Maths/math.like';
 
 declare type VideoTexture = import("../Materials/Textures/videoTexture").VideoTexture;
 
+// TODO WEBGPU remove when not needed anymore
+function assert(condition: any, msg?: string): asserts condition {
+    if (!condition) {
+        throw new Error(msg);
+    }
+}
+
 /**
  * Options to load the associated Glslang library
  */
@@ -119,7 +126,9 @@ export class WebGPUEngine extends Engine {
     // Page Life cycle and constants
     private readonly _uploadEncoderDescriptor = { label: "upload" };
     private readonly _renderEncoderDescriptor = { label: "render" };
+    private readonly _renderTargetEncoderDescriptor = { label: "renderTarget" };
     private readonly _clearDepthValue = 1;
+    private readonly _clearReverseDepthValue = 0;
     private readonly _clearStencilValue = 0;
     private readonly _defaultSampleCount = 4; // Only supported value for now.
 
@@ -150,11 +159,13 @@ export class WebGPUEngine extends Engine {
     // Frame Life Cycle (recreated each frame)
     private _uploadEncoder: GPUCommandEncoder;
     private _renderEncoder: GPUCommandEncoder;
+    private _renderTargetEncoder: GPUCommandEncoder;
 
-    private _commandBuffers: GPUCommandBuffer[] = [null as any, null as any];
+    private _commandBuffers: GPUCommandBuffer[] = [null as any, null as any, null as any];
 
     // Frame Buffer Life Cycle (recreated for each render target pass)
     private _currentRenderPass: Nullable<GPURenderPassEncoder> = null;
+    private _mainRenderPass: Nullable<GPURenderPassEncoder> = null;
 
     // DrawCall Life Cycle
     // Effect is on the parent class
@@ -212,7 +223,7 @@ export class WebGPUEngine extends Engine {
 
         options.deviceDescriptor = options.deviceDescriptor || { };
         options.swapChainFormat = options.swapChainFormat || WebGPUConstants.TextureFormat.BGRA8Unorm;
-        options.antialiasing = options.antialiasing === undefined ? true : options.antialiasing;
+        options.antialiasing = false;//options.antialiasing === undefined ? true : options.antialiasing;
 
         Logger.Log(`Babylon.js v${Engine.Version} - WebGPU engine`);
         if (!navigator.gpu) {
@@ -301,6 +312,7 @@ export class WebGPUEngine extends Engine {
 
                 this._uploadEncoder = this._device.createCommandEncoder(this._uploadEncoderDescriptor);
                 this._renderEncoder = this._device.createCommandEncoder(this._renderEncoderDescriptor);
+                this._renderTargetEncoder = this._device.createCommandEncoder(this._renderTargetEncoderDescriptor);
 
                 this._initializeLimits();
                 this._initializeContextAndSwapChain();
@@ -474,12 +486,16 @@ export class WebGPUEngine extends Engine {
     //------------------------------------------------------------------------------
 
     public wipeCaches(bruteForce?: boolean): void {
-        if (this.preventCacheWipeBetweenFrames) {
+        if (this.preventCacheWipeBetweenFrames && !bruteForce) {
             return;
         }
         this.resetTextureCache();
 
         this._currentEffect = null;
+        this._viewportCached.x = 0;
+        this._viewportCached.y = 0;
+        this._viewportCached.z = 0;
+        this._viewportCached.w = 0;
         this._currentIndexBuffer = null;
         this._currentVertexBuffers = null;
 
@@ -487,8 +503,16 @@ export class WebGPUEngine extends Engine {
             this._currentProgram = null;
 
             this._stencilState.reset();
+
             this._depthCullingState.reset();
+            this._depthCullingState.depthFunc = Constants.LEQUAL;
+
             this._alphaState.reset();
+            this._alphaMode = Constants.ALPHA_ADD;
+            this._alphaEquation = Constants.ALPHA_DISABLE;
+
+            this._colorWrite = true;
+            this._colorWriteChanged = true;
         }
 
         this._cachedVertexBuffers = null;
@@ -508,41 +532,33 @@ export class WebGPUEngine extends Engine {
     //                              Dynamic WebGPU States
     //------------------------------------------------------------------------------
 
+    /** @hidden */
     public _viewport(x: number, y: number, width: number, height: number): void {
-        // TODO WEBGPU. Cache.
-        // if (x !== this._viewportCached.x ||
-        //     y !== this._viewportCached.y ||
-        //     width !== this._viewportCached.z ||
-        //     height !== this._viewportCached.w) {
-        //     this._viewportCached.x = x;
-        //     this._viewportCached.y = y;
-        //     this._viewportCached.z = width;
-        //     this._viewportCached.w = height;
-
-        //     this._gl.viewport(x, y, width, height);
-        // }
-        if (!this._currentRenderPass) {
-            this._startRenderPass();
-        }
-        // TODO WEBGPU. Viewport.
-        // Use 0 1 like the default webgl values.
-        // this._currentRenderPass!.setViewport(x, y, width, height, 0, 1);
+        if (x !== this._viewportCached.x ||
+            y !== this._viewportCached.y ||
+            width !== this._viewportCached.z ||
+            height !== this._viewportCached.w) {
+             this._viewportCached.x = x;
+             this._viewportCached.y = y;
+             this._viewportCached.z = width;
+             this._viewportCached.w = height;
+
+             const renderPass = this._getCurrentRenderPass();
+
+             renderPass.setViewport(x, y, width, height, 0, 1);
+        }
     }
 
     public enableScissor(x: number, y: number, width: number, height: number): void {
-        if (!this._currentRenderPass) {
-            this._startRenderPass();
-        }
+        const renderPass = this._getCurrentRenderPass();
 
-        this._currentRenderPass!.setScissorRect(x, y, width, height);
+        renderPass.setScissorRect(x, y, width, height);
     }
 
     public disableScissor() {
-        if (!this._currentRenderPass) {
-            this._startRenderPass();
-        }
+        const renderPass = this._getCurrentRenderPass();
 
-        this._currentRenderPass!.setScissorRect(0, 0, this.getRenderWidth(), this.getRenderHeight());
+        renderPass.setScissorRect(0, 0, this.getRenderWidth(), this.getRenderHeight());
     }
 
     public clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
@@ -551,13 +567,28 @@ export class WebGPUEngine extends Engine {
             color.a = 1;
         }
 
-        // TODO WEBGPU handle calling this function multiple times in a frame (for eg in the case of multi cameras) - handle reverse depth buffer
-        this._mainColorAttachments[0].loadValue = backBuffer && color ? color : WebGPUConstants.LoadOp.Load;
+        // We need to recreate the render pass so that the new parameters for clear color / depth / stencil are taken into account
+        if (this._currentRenderTarget) {
+            if (this._currentRenderPass) {
+                this._currentRenderPass.endPass();
+            }
+            this._currentRenderPass = this._createRenderPassForRenderTarget(this._currentRenderTarget!, backBuffer ? color : null, depth, stencil);
+        } else {
+            if (this.useReverseDepthBuffer) {
+                this._depthCullingState.depthFunc = Constants.GREATER;
+            }
+
+            this._mainColorAttachments[0].loadValue = backBuffer && color ? color : WebGPUConstants.LoadOp.Load;
 
-        this._mainDepthAttachment.depthLoadValue = depth ? this._clearDepthValue : WebGPUConstants.LoadOp.Load;
-        this._mainDepthAttachment.stencilLoadValue = stencil ? this._clearStencilValue : WebGPUConstants.LoadOp.Load;
+            this._mainDepthAttachment.depthLoadValue = depth ? (this.useReverseDepthBuffer ? this._clearReverseDepthValue : this._clearDepthValue) : WebGPUConstants.LoadOp.Load;
+            this._mainDepthAttachment.stencilLoadValue = stencil ? this._clearStencilValue : WebGPUConstants.LoadOp.Load;
 
-        this._startMainRenderPass();
+            if (this._mainRenderPass) {
+                this._endMainRenderPass();
+            }
+
+            this._startMainRenderPass();
+        }
     }
 
     //------------------------------------------------------------------------------
@@ -853,6 +884,9 @@ export class WebGPUEngine extends Engine {
             webGpuContext.sources = shader.sources;
         }
         else {
+            //!console.log(vertexSourceCode);
+            //!console.log(fragmentSourceCode);
+
             webGpuContext.sources = {
                 fragment: fragmentSourceCode,
                 vertex: vertexSourceCode
@@ -943,6 +977,8 @@ export class WebGPUEngine extends Engine {
     /** @hidden */
     public _releaseTexture(texture: InternalTexture): void {
         texture._hardwareTexture?.release();
+        texture._irradianceTexture?.dispose();
+        texture._depthStencilTexture?.dispose();
 
         // TODO WEBGPU remove debug code
         (texture as any)._released = true;
@@ -1065,6 +1101,11 @@ export class WebGPUEngine extends Engine {
 
     private _getWebGPUTextureFormat(type: number, format: number): GPUTextureFormat {
         switch (format) {
+            case Constants.TEXTUREFORMAT_DEPTH24_STENCIL8:
+                return WebGPUConstants.TextureFormat.Depth24PlusStencil8;
+            case Constants.TEXTUREFORMAT_DEPTH32_FLOAT:
+                return WebGPUConstants.TextureFormat.Depth32Float;
+
             case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:
                 return WebGPUConstants.TextureFormat.BC7RGBAUnorm;
             case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
@@ -1109,6 +1150,8 @@ export class WebGPUEngine extends Engine {
                         throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
                     case Constants.TEXTUREFORMAT_RGBA:
                         return WebGPUConstants.TextureFormat.RGBA8Unorm;
+                    case Constants.TEXTUREFORMAT_BGRA:
+                        return WebGPUConstants.TextureFormat.BGRA8Unorm;
                     case Constants.TEXTUREFORMAT_RED_INTEGER:
                         return WebGPUConstants.TextureFormat.R8Uint;
                     case Constants.TEXTUREFORMAT_RG_INTEGER:
@@ -1376,6 +1419,31 @@ export class WebGPUEngine extends Engine {
         }
     }
 
+    private _setInternalTexture(name: string, internalTexture: Nullable<InternalTexture>): void {
+        if (this._currentEffect) {
+            const webgpuPipelineContext = this._currentEffect._pipelineContext as WebGPUPipelineContext;
+
+            if (webgpuPipelineContext.samplers[name]) {
+                if (webgpuPipelineContext.samplers[name]!.texture !== internalTexture) {
+                    webgpuPipelineContext.bindGroups = null as any; // the bind groups need to be rebuilt (at least the bind group owning this texture, but it's easier to just have them all rebuilt)
+                }
+                webgpuPipelineContext.samplers[name]!.texture = internalTexture!;
+            }
+            else {
+                // TODO WEBGPU. 121 mapping samplers <-> availableSamplers
+                const availableSampler = webgpuPipelineContext.availableSamplers[name];
+                if (availableSampler) {
+                    webgpuPipelineContext.samplers[name] = {
+                        setIndex: availableSampler.setIndex,
+                        textureBinding: availableSampler.bindingIndex,
+                        samplerBinding: availableSampler.bindingIndex + 1,
+                        texture: internalTexture!
+                    };
+                }
+            }
+        }
+    }
+
     public setTexture(channel: number, _: Nullable<WebGLUniformLocation>, texture: Nullable<BaseTexture>, name: string): void {
         if (this._currentEffect) {
             const webgpuPipelineContext = this._currentEffect._pipelineContext as WebGPUPipelineContext;
@@ -1400,24 +1468,7 @@ export class WebGPUEngine extends Engine {
                 debugger;
             }
 
-            if (webgpuPipelineContext.samplers[name]) {
-                if (webgpuPipelineContext.samplers[name]!.texture !== internalTexture) {
-                    webgpuPipelineContext.bindGroups = null as any; // the bind groups need to be rebuilt (at least the bind group owning this texture, but it's easier to just have them all rebuilt)
-                }
-                webgpuPipelineContext.samplers[name]!.texture = internalTexture!;
-            }
-            else {
-                // TODO WEBGPU. 121 mapping samplers <-> availableSamplers
-                const availableSampler = webgpuPipelineContext.availableSamplers[name];
-                if (availableSampler) {
-                    webgpuPipelineContext.samplers[name] = {
-                        setIndex: availableSampler.setIndex,
-                        textureBinding: availableSampler.bindingIndex,
-                        samplerBinding: availableSampler.bindingIndex + 1,
-                        texture: internalTexture!
-                    };
-                }
-            }
+            this._setInternalTexture(name, internalTexture);
 
             // Video
             if ((<VideoTexture>texture).video) {
@@ -1441,7 +1492,7 @@ export class WebGPUEngine extends Engine {
     }
 
     /** @hidden */
-    public _bindTexture(channel: number, texture: InternalTexture): void {
+    public _bindTexture(channel: number, texture: InternalTexture, name: string): void {
         if (channel === undefined) {
             return;
         }
@@ -1452,7 +1503,7 @@ export class WebGPUEngine extends Engine {
 
         this._activeChannel = channel;
 
-        this._bindTextureDirectly(0, texture);
+        this._setInternalTexture(name, texture);
     }
 
     private _createGPUTextureForInternalTexture(texture: InternalTexture, width?: number, height?: number): WebGPUHardwareTexture {
@@ -1471,8 +1522,12 @@ export class WebGPUEngine extends Engine {
 
         gpuTextureWrapper.format = this._getWebGPUTextureFormat(texture.type, texture.format);
 
+        const textureUsages =
+            texture._source === InternalTextureSource.RenderTarget ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.OutputAttachment :
+            texture._source === InternalTextureSource.Depth ? /*WebGPUConstants.TextureUsage.Sampled |*/ WebGPUConstants.TextureUsage.OutputAttachment : -1;
+
         if (texture.isCube) {
-            const gpuTexture = this._textureHelper.createCubeTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder);
+            const gpuTexture = this._textureHelper.createCubeTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
 
             gpuTextureWrapper.set(gpuTexture);
             gpuTextureWrapper.createView({
@@ -1483,7 +1538,7 @@ export class WebGPUEngine extends Engine {
                 aspect: WebGPUConstants.TextureAspect.All
             });
         } else {
-            const gpuTexture = this._textureHelper.createTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder);
+            const gpuTexture = this._textureHelper.createTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
 
             gpuTextureWrapper.set(gpuTexture);
             gpuTextureWrapper.createView();
@@ -1716,11 +1771,6 @@ export class WebGPUEngine extends Engine {
     //                              Render Target Textures
     //------------------------------------------------------------------------------
 
-    /** @hidden */
-    public _setupDepthStencilTexture(internalTexture: InternalTexture, size: number | { width: number, height: number, layers?: number }, generateStencil: boolean, bilinearFiltering: boolean, comparisonFunction: number): void {
-        console.warn("_setupDepthStencilTexture not implemented yet in WebGPU");
-    }
-
     public createRenderTargetTexture(size: any, options: boolean | RenderTargetCreationOptions): InternalTexture {
         let fullOptions = new RenderTargetCreationOptions();
 
@@ -1730,21 +1780,25 @@ export class WebGPUEngine extends Engine {
             fullOptions.generateStencilBuffer = fullOptions.generateDepthBuffer && options.generateStencilBuffer;
             fullOptions.type = options.type === undefined ? Constants.TEXTURETYPE_UNSIGNED_INT : options.type;
             fullOptions.samplingMode = options.samplingMode === undefined ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : options.samplingMode;
-            // TODO WEBGPU fullOptions.format not set?
+            fullOptions.format = options.format === undefined ? Constants.TEXTUREFORMAT_RGBA : options.format;
         } else {
             fullOptions.generateMipMaps = <boolean>options;
             fullOptions.generateDepthBuffer = true;
             fullOptions.generateStencilBuffer = false;
             fullOptions.type = Constants.TEXTURETYPE_UNSIGNED_INT;
             fullOptions.samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
-            // TODO WEBGPU fullOptions.format not set?
+            fullOptions.format = Constants.TEXTUREFORMAT_RGBA;
         }
-        var texture = new InternalTexture(this, InternalTextureSource.RenderTarget);
 
-        var width = size.width || size;
-        var height = size.height || size;
+        // TODO WEBGPU remove two lines
+        //fullOptions.type = Constants.TEXTURETYPE_UNSIGNED_BYTE;
+        //fullOptions.format = Constants.TEXTUREFORMAT_BGRA;
+
+        const texture = new InternalTexture(this, InternalTextureSource.RenderTarget);
 
-        // TODO WEBGPU handle layers
+        const width = size.width || size;
+        const height = size.height || size;
+        const layers = size.layers || 0;
 
         texture._depthStencilBuffer = {};
         texture._framebuffer = {};
@@ -1752,16 +1806,34 @@ export class WebGPUEngine extends Engine {
         texture.baseHeight = height;
         texture.width = width;
         texture.height = height;
+        texture.depth = layers;
         texture.isReady = true;
-        texture.samples = 1;
+        texture.samples = this._mainPassSampleCount;
         texture.generateMipMaps = fullOptions.generateMipMaps ? true : false;
         texture.samplingMode = fullOptions.samplingMode;
         texture.type = fullOptions.type;
+        texture.format = fullOptions.format;
         texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
         texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
 
         this._internalTexturesCache.push(texture);
 
+        if (texture._generateDepthBuffer || texture._generateStencilBuffer) {
+            texture._depthStencilTexture = this.createDepthStencilTexture({ width, height, layers }, {
+                bilinearFiltering:
+                    fullOptions.samplingMode === undefined ||
+                    fullOptions.samplingMode === Constants.TEXTURE_BILINEAR_SAMPLINGMODE || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR ||
+                    fullOptions.samplingMode === Constants.TEXTURE_TRILINEAR_SAMPLINGMODE || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR ||
+                    fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST || fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR ||
+                    fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST,
+                comparisonFunction: 0,
+                generateStencil: texture._generateStencilBuffer,
+                isCube: texture.isCube
+            });
+        }
+
+        this._createGPUTextureForInternalTexture(texture);
+
         return texture;
     }
 
@@ -1776,10 +1848,43 @@ export class WebGPUEngine extends Engine {
     }
 
     /** @hidden */
+    public _setupDepthStencilTexture(internalTexture: InternalTexture, size: number | { width: number, height: number, layers?: number }, generateStencil: boolean, bilinearFiltering: boolean, comparisonFunction: number): void {
+        const width = (<{ width: number, height: number, layers?: number }>size).width || <number>size;
+        const height = (<{ width: number, height: number, layers?: number }>size).height || <number>size;
+        const layers = (<{ width: number, height: number, layers?: number }>size).layers || 0;
+
+        internalTexture.baseWidth = width;
+        internalTexture.baseHeight = height;
+        internalTexture.width = width;
+        internalTexture.height = height;
+        internalTexture.is2DArray = layers > 0;
+        internalTexture.depth = layers;
+        internalTexture.isReady = true;
+        internalTexture.samples = this._mainPassSampleCount;
+        internalTexture.generateMipMaps = false;
+        internalTexture._generateDepthBuffer = true;
+        internalTexture._generateStencilBuffer = generateStencil;
+        internalTexture.samplingMode = bilinearFiltering ? Constants.TEXTURE_BILINEAR_SAMPLINGMODE : Constants.TEXTURE_NEAREST_SAMPLINGMODE;
+        internalTexture.type = Constants.TEXTURETYPE_UNSIGNED_INT;
+        internalTexture._comparisonFunction = comparisonFunction;
+    }
+
+    /** @hidden */
     public _createDepthStencilTexture(size: number | { width: number, height: number, layers?: number }, options: DepthTextureCreationOptions): InternalTexture {
         const internalTexture = new InternalTexture(this, InternalTextureSource.Depth);
 
-        console.warn("_createDepthStencilTexture not implemented yet in WebGPU");
+        const internalOptions = {
+            bilinearFiltering: false,
+            comparisonFunction: 0,
+            generateStencil: false,
+            ...options
+        };
+
+        internalTexture.format = internalOptions.generateStencil ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT;
+
+        this._setupDepthStencilTexture(internalTexture, size, internalOptions.generateStencil, internalOptions.bilinearFiltering, internalOptions.comparisonFunction);
+
+        this._createGPUTextureForInternalTexture(internalTexture);
 
         return internalTexture;
     }
@@ -1794,6 +1899,21 @@ export class WebGPUEngine extends Engine {
         return internalTexture;
     }
 
+    public updateRenderTargetTextureSampleCount(texture: Nullable<InternalTexture>, samples: number): number {
+        if (!texture) {
+            return 1;
+        }
+
+        if (texture.samples === samples) {
+            return samples;
+        }
+
+        // TODO WEBGPU handle sampleCount
+        //console.warn("updateRenderTargetTextureSampleCount not implemented yet in WebGPU");
+
+        return 1;
+    }
+
     //------------------------------------------------------------------------------
     //                              Render Commands
     //------------------------------------------------------------------------------
@@ -1819,15 +1939,17 @@ export class WebGPUEngine extends Engine {
      * End the current frame
      */
     public endFrame() {
-        this._endRenderPass();
+        this._endMainRenderPass();
 
         this._commandBuffers[0] = this._uploadEncoder.finish();
-        this._commandBuffers[1] = this._renderEncoder.finish();
+        this._commandBuffers[1] = this._renderTargetEncoder.finish();
+        this._commandBuffers[2] = this._renderEncoder.finish();
 
         this._device.defaultQueue.submit(this._commandBuffers);
 
         this._uploadEncoder = this._device.createCommandEncoder(this._uploadEncoderDescriptor);
         this._renderEncoder = this._device.createCommandEncoder(this._renderEncoderDescriptor);
+        this._renderTargetEncoder = this._device.createCommandEncoder(this._renderTargetEncoderDescriptor);
 
         super.endFrame();
 
@@ -1841,9 +1963,41 @@ export class WebGPUEngine extends Engine {
     //                              Render Pass
     //------------------------------------------------------------------------------
 
-    private _startRenderPass(): void {
+    private _createRenderPassForRenderTarget(internalTexture: InternalTexture, clearColor: Nullable<IColor4Like>, clearDepth: boolean, clearStencil: boolean = false): GPURenderPassEncoder {
+        const colorTexture = internalTexture._hardwareTexture!.underlyingResource;
+        const depthTexture = internalTexture._depthStencilTexture?._hardwareTexture?.underlyingResource;
+
+        const renderPass = this._renderTargetEncoder.beginRenderPass({
+            colorAttachments: [{
+                attachment: colorTexture.createView(),
+                loadValue: clearColor !== null ? clearColor : WebGPUConstants.LoadOp.Load,
+                storeOp: WebGPUConstants.StoreOp.Store
+            }],
+            depthStencilAttachment: internalTexture._generateDepthBuffer || internalTexture._generateStencilBuffer ? {
+                attachment: depthTexture.createView(),
+                depthLoadValue: clearDepth && internalTexture._generateDepthBuffer ? this._clearDepthValue : WebGPUConstants.LoadOp.Load,
+                depthStoreOp: WebGPUConstants.StoreOp.Store,
+                stencilLoadValue: clearStencil && internalTexture._generateStencilBuffer ? this._clearStencilValue : WebGPUConstants.LoadOp.Load,
+                stencilStoreOp: WebGPUConstants.StoreOp.Store,
+            } : undefined
+        });
+
+        return renderPass;
+    }
+
+    private _getCurrentRenderPass(): GPURenderPassEncoder {
+        if (this._currentRenderTarget && !this._currentRenderPass) {
+            this._currentRenderPass = this._createRenderPassForRenderTarget(this._currentRenderTarget, null, false, false);
+        } else if (!this._currentRenderPass) {
+            this._startMainRenderPass();
+        }
+
+        return this._currentRenderPass!;
+    }
+
+    private _startMainRenderPass(): void {
         if (this._currentRenderPass) {
-            this._endRenderPass();
+            this._endMainRenderPass();
         }
 
         this._swapChainTexture = this._swapChain.getCurrentTexture();
@@ -1860,21 +2014,43 @@ export class WebGPUEngine extends Engine {
             colorAttachments: this._mainColorAttachments,
             depthStencilAttachment: this._mainDepthAttachment
         });
+
+        if (this._cachedViewport) {
+            this.setViewport(this._cachedViewport);
+        }
+
+        this._currentRenderPass.setBlendColor(this._alphaState._blendConstants as any);
+
+        // TODO WEBGPU set the scissor rect and the stencil reference value
+
+        this._mainRenderPass = this._currentRenderPass;
     }
 
-    private _endRenderPass(): void {
-        if (this._currentRenderPass) {
+    private _endMainRenderPass(): void {
+        if (this._currentRenderPass === this._mainRenderPass && this._currentRenderPass !== null) {
             this._currentRenderPass.endPass();
             this._currentRenderPass = null;
+            this._mainRenderPass = null;
         }
     }
 
     public bindFramebuffer(texture: InternalTexture, faceIndex?: number, 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>;
+
+        if (!hardwareTexture || !gpuTexture) {
+            // TODO WEBGPU remove this log
+            console.error("bindFramebuffer: Trying to bind a texture that does not have a hardware texture or that has a webgpu texture empty!", texture, hardwareTexture, gpuTexture);
+            return;
+        }
+
         if (this._currentRenderTarget) {
             this.unBindFramebuffer(this._currentRenderTarget);
         }
         this._currentRenderTarget = texture;
-        this._currentFramebuffer = texture._MSAAFramebuffer ? texture._MSAAFramebuffer : texture._framebuffer;
+
+        this._currentRenderPass = null; // lazy creation of the render pass, hoping the render pass will be created by a call to clear()...
+
         if (this._cachedViewport && !forceFullscreenViewport) {
             this.setViewport(this._cachedViewport, requiredWidth, requiredHeight);
         } else {
@@ -1893,18 +2069,40 @@ export class WebGPUEngine extends Engine {
 
             this._viewport(0, 0, requiredWidth, requiredHeight);
         }
+
+        this.wipeCaches();
     }
 
     public unBindFramebuffer(texture: InternalTexture, disableGenerateMipMaps = false, onBeforeUnbind?: () => void): void {
+        // TODO WEBGPU remove the assert debugging code
+        assert(texture === this._currentRenderTarget);
+
         this._currentRenderTarget = null;
 
+        if (this._currentRenderPass && this._currentRenderPass !== this._mainRenderPass) {
+            this._currentRenderPass.endPass();
+        }
+
         if (onBeforeUnbind) {
-            if (texture._MSAAFramebuffer) {
-                this._currentFramebuffer = texture._framebuffer;
-            }
             onBeforeUnbind();
         }
-        this._currentFramebuffer = null;
+
+        this._currentRenderPass = this._mainRenderPass;
+    }
+
+    public restoreDefaultFramebuffer(): void {
+        if (this._currentRenderTarget) {
+            this.unBindFramebuffer(this._currentRenderTarget);
+        } else {
+            this._currentRenderPass = this._mainRenderPass;
+        }
+        if (this._currentRenderPass) {
+            if (this._cachedViewport) {
+                this.setViewport(this._cachedViewport);
+            }
+        }
+
+        this.wipeCaches();
     }
 
     //------------------------------------------------------------------------------
@@ -1994,9 +2192,13 @@ export class WebGPUEngine extends Engine {
         }
     }
 
-    private _getDepthStencilStateDescriptor(): GPUDepthStencilStateDescriptor {
+    private _getDepthStencilStateDescriptor(): GPUDepthStencilStateDescriptor | undefined {
         // TODO WEBGPU. Depth State according to the cached state.
         // And the current render pass attachment setup.
+        if (this._currentRenderTarget && !this._currentRenderTarget._depthStencilTexture) {
+            return undefined;
+        }
+
         const stencilFrontBack: GPUStencilStateFaceDescriptor = {
             compare: this._getCompareFunction(this._stencilState.stencilFunc),
             depthFailOp: this._getOpFunction(this._stencilState.stencilOpDepthFail, WebGPUConstants.StencilOperation.Keep),
@@ -2007,7 +2209,7 @@ export class WebGPUEngine extends Engine {
         return {
             depthWriteEnabled: this.getDepthWrite(),
             depthCompare: this._getCompareFunction(this.getDepthFunction()),
-            format: WebGPUConstants.TextureFormat.Depth24PlusStencil8,
+            format: this._currentRenderTarget && this._currentRenderTarget._depthStencilTexture ? this._getWebGPUTextureFormat(-1, this._currentRenderTarget._depthStencilTexture.format) : WebGPUConstants.TextureFormat.Depth24PlusStencil8,
             stencilFront: stencilFrontBack,
             stencilBack: stencilFrontBack,
             stencilReadMask: this._stencilState.stencilFuncMask,
@@ -2232,9 +2434,8 @@ export class WebGPUEngine extends Engine {
     }
 
     private _getColorStateDescriptors(): GPUColorStateDescriptor[] {
-        // TODO WEBGPU. Manage Multi render target.
         return [{
-            format: this._options.swapChainFormat!,
+            format: this._currentRenderTarget ? this._getWebGPUTextureFormat(this._currentRenderTarget.type, this._currentRenderTarget.format) : this._options.swapChainFormat!,
             alphaBlend: this._getAphaBlendState(),
             colorBlend: this._getColorBlendState(),
             writeMask: this._getWriteMask(),
@@ -2560,7 +2761,7 @@ export class WebGPUEngine extends Engine {
                         if (!hardwareTexture.sampler) {
                             const samplerDescriptor: GPUSamplerDescriptor = this._getSamplerDescriptor(bindingInfo.texture!);
                             const gpuSampler = this._device.createSampler(samplerDescriptor);
-                            hardwareTexture.setSampler(gpuSampler);
+                            hardwareTexture.sampler = gpuSampler;
                         }
 
                         // TODO WEBGPU Remove this when all testings are ok
@@ -2624,7 +2825,7 @@ export class WebGPUEngine extends Engine {
     }
 
     private _bindVertexInputs(vertexInputs: IWebGPUPipelineContextVertexInputsCache, setIndexFormat: boolean): void {
-        const renderPass = this._bundleEncoder || this._currentRenderPass!;
+        const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
 
         if (vertexInputs.indexBuffer) {
             // TODO WEBGPU. Check if cache would be worth it.
@@ -2646,14 +2847,14 @@ export class WebGPUEngine extends Engine {
 
     private _setRenderBindGroups(bindGroups: GPUBindGroup[]): void {
         // TODO WEBGPU. Only set groups if changes happened.
-        const renderPass = this._bundleEncoder || this._currentRenderPass!;
+        const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
         for (let i = 0; i < bindGroups.length; i++) {
             renderPass.setBindGroup(i, bindGroups[i]);
         }
     }
 
     private _setRenderPipeline(fillMode: number): void {
-        const renderPass = this._bundleEncoder || this._currentRenderPass!;
+        const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
 
         const topology = this._getTopology(fillMode);
         const setIndexFormatInRenderPass = this._indexFormatInRenderPass(topology);
@@ -2669,12 +2870,12 @@ export class WebGPUEngine extends Engine {
 
         if (this._alphaState.alphaBlend && this._alphaState._isBlendConstantsDirty) {
             // TODO WebGPU. should use renderPass.
-            this._currentRenderPass!.setBlendColor(this._alphaState._blendConstants as any);
+            this._getCurrentRenderPass().setBlendColor(this._alphaState._blendConstants as any);
         }
     }
 
     public drawElementsType(fillMode: number, indexStart: number, indexCount: number, instancesCount: number = 1): void {
-        const renderPass = this._bundleEncoder || this._currentRenderPass!;
+        const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
 
         this._setRenderPipeline(fillMode);
 
@@ -2682,7 +2883,7 @@ export class WebGPUEngine extends Engine {
     }
 
     public drawArraysType(fillMode: number, verticesStart: number, verticesCount: number, instancesCount: number = 1): void {
-        const renderPass = this._bundleEncoder || this._currentRenderPass!;
+        const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
 
         this._currentIndexBuffer = null;
 
@@ -2739,11 +2940,9 @@ export class WebGPUEngine extends Engine {
      * @param bundles defines the bundle to replay
      */
     public executeBundles(bundles: GPURenderBundle[]): void {
-        if (!this._currentRenderPass) {
-            this._startRenderPass();
-        }
+        const renderPass = this._getCurrentRenderPass();
 
-        this._currentRenderPass!.executeBundles(bundles);
+        renderPass.executeBundles(bundles);
     }
 
     //------------------------------------------------------------------------------

+ 2 - 0
src/Materials/Textures/renderTargetTexture.ts

@@ -367,6 +367,8 @@ export class RenderTargetTexture extends Texture {
             return;
         }
 
+        internalTexture._depthStencilTexture?.dispose();
+
         var engine = this.getScene()!.getEngine();
         internalTexture._depthStencilTexture = engine.createDepthStencilTexture(this._size, {
             bilinearFiltering,

+ 3 - 3
src/Materials/effect.ts

@@ -801,7 +801,7 @@ export class Effect implements IDisposable {
      * @hidden
      */
     public _bindTexture(channel: string, texture: Nullable<InternalTexture>): void {
-        this._engine._bindTexture(this._samplers[channel], texture);
+        this._engine._bindTexture(this._samplers[channel], texture, channel);
     }
 
     /**
@@ -853,7 +853,7 @@ export class Effect implements IDisposable {
      * @param postProcess Post process to get the input texture from.
      */
     public setTextureFromPostProcess(channel: string, postProcess: Nullable<PostProcess>): void {
-        this._engine.setTextureFromPostProcess(this._samplers[channel], postProcess);
+        this._engine.setTextureFromPostProcess(this._samplers[channel], postProcess, name);
     }
 
     /**
@@ -863,7 +863,7 @@ export class Effect implements IDisposable {
      * @param postProcess Post process to get the output texture from.
      */
     public setTextureFromPostProcessOutput(channel: string, postProcess: Nullable<PostProcess>): void {
-        this._engine.setTextureFromPostProcessOutput(this._samplers[channel], postProcess);
+        this._engine.setTextureFromPostProcessOutput(this._samplers[channel], postProcess, name);
     }
 
     /**