Browse Source

Code refactoring

Popov72 4 years ago
parent
commit
27fe5dedbc

+ 248 - 0
src/Engines/WebGPU/webgpuCacheSampler.ts

@@ -0,0 +1,248 @@
+import * as WebGPUConstants from './webgpuConstants';
+import { Constants } from '../constants';
+import { InternalTexture } from '../../Materials/Textures/internalTexture';
+import { WebGPUTextureHelper } from "./webgpuTextureHelper";
+
+const filterToBits = [
+    0 | 0 << 1 | 0 << 2, // not used
+    0 | 0 << 1 | 0 << 2, // TEXTURE_NEAREST_SAMPLINGMODE / TEXTURE_NEAREST_NEAREST
+    1 | 1 << 1 | 0 << 2, // TEXTURE_BILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR
+    1 | 1 << 1 | 1 << 2, // TEXTURE_TRILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR_MIPLINEAR
+    0 | 0 << 1 | 0 << 2, // TEXTURE_NEAREST_NEAREST_MIPNEAREST
+    0 | 1 << 1 | 0 << 2, // TEXTURE_NEAREST_LINEAR_MIPNEAREST
+    0 | 1 << 1 | 1 << 2, // TEXTURE_NEAREST_LINEAR_MIPLINEAR
+    0 | 1 << 1 | 0 << 2, // TEXTURE_NEAREST_LINEAR
+    0 | 0 << 1 | 1 << 2, // TEXTURE_NEAREST_NEAREST_MIPLINEAR
+    1 | 0 << 1 | 0 << 2, // TEXTURE_LINEAR_NEAREST_MIPNEAREST
+    1 | 0 << 1 | 1 << 2, // TEXTURE_LINEAR_NEAREST_MIPLINEAR
+    1 | 1 << 1 | 0 << 2, // TEXTURE_LINEAR_LINEAR_MIPNEAREST
+    1 | 0 << 1 | 0 << 2, // TEXTURE_LINEAR_NEAREST
+];
+
+// subtract 0x01FF from the comparison function value before indexing this array!
+const comparisonFunctionToBits = [
+    0 << 3 | 0 << 4 | 0 << 5 | 0 << 6, // undefined
+    0 << 3 | 0 << 4 | 0 << 5 | 1 << 6, // NEVER
+    0 << 3 | 0 << 4 | 1 << 5 | 0 << 6, // LESS
+    0 << 3 | 0 << 4 | 1 << 5 | 1 << 6, // EQUAL
+    0 << 3 | 1 << 4 | 0 << 5 | 0 << 6, // LEQUAL
+    0 << 3 | 1 << 4 | 0 << 5 | 1 << 6, // GREATER
+    0 << 3 | 1 << 4 | 1 << 5 | 0 << 6, // NOTEQUAL
+    0 << 3 | 1 << 4 | 1 << 5 | 1 << 6, // GEQUAL
+    1 << 3 | 0 << 4 | 0 << 5 | 0 << 6, // ALWAYS
+];
+
+const filterNoMipToBits = [
+    0 << 7, // not used
+    1 << 7, // TEXTURE_NEAREST_SAMPLINGMODE / TEXTURE_NEAREST_NEAREST
+    1 << 7, // TEXTURE_BILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR
+    0 << 7, // TEXTURE_TRILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR_MIPLINEAR
+    0 << 7, // TEXTURE_NEAREST_NEAREST_MIPNEAREST
+    0 << 7, // TEXTURE_NEAREST_LINEAR_MIPNEAREST
+    0 << 7, // TEXTURE_NEAREST_LINEAR_MIPLINEAR
+    1 << 7, // TEXTURE_NEAREST_LINEAR
+    0 << 7, // TEXTURE_NEAREST_NEAREST_MIPLINEAR
+    0 << 7, // TEXTURE_LINEAR_NEAREST_MIPNEAREST
+    0 << 7, // TEXTURE_LINEAR_NEAREST_MIPLINEAR
+    0 << 7, // TEXTURE_LINEAR_LINEAR_MIPNEAREST
+    1 << 7, // TEXTURE_LINEAR_NEAREST
+];
+
+/** @hidden */
+export class WebGPUCacheSampler {
+
+    private static _samplers: { [hash: number]: GPUSampler } = {};
+
+    private _device: GPUDevice;
+
+    constructor(device: GPUDevice) {
+        this._device = device;
+    }
+
+    private static _GetSamplerHashCode(texture: InternalTexture): number {
+        let code =
+            filterToBits[texture.samplingMode] +
+            comparisonFunctionToBits[(texture._comparisonFunction || 0x0202) - 0x0200 + 1] +
+            filterNoMipToBits[texture.samplingMode] + // handle the lodMinClamp = lodMaxClamp = 0 case when no filter used for mip mapping
+            ((texture._cachedWrapU ?? 1) << 8) +
+            ((texture._cachedWrapV ?? 1) << 10) +
+            ((texture._cachedWrapR ?? 1) << 12) +
+            ((texture.generateMipMaps ? 1 : 0) << 14) + // need to factor this in because _getSamplerFilterDescriptor depends on samplingMode AND generateMipMaps!
+            ((texture._cachedAnisotropicFilteringLevel ?? 1) << 15);
+
+        return code;
+    }
+
+    private static _GetSamplerFilterDescriptor(internalTexture: InternalTexture): {
+        magFilter: GPUFilterMode,
+        minFilter: GPUFilterMode,
+        mipmapFilter: GPUFilterMode,
+        lodMinClamp?: number,
+        lodMaxClamp?: number,
+    } {
+        let magFilter: GPUFilterMode, minFilter: GPUFilterMode, mipmapFilter: GPUFilterMode, lodMinClamp: number | undefined, lodMaxClamp: number | undefined;
+        const useMipMaps = internalTexture.generateMipMaps;
+        switch (internalTexture.samplingMode) {
+            case Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST:
+                magFilter = WebGPUConstants.FilterMode.Linear;
+                minFilter = WebGPUConstants.FilterMode.Linear;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                if (!useMipMaps) {
+                    lodMinClamp = lodMaxClamp = 0;
+                }
+                break;
+            case Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR:
+            case Constants.TEXTURE_TRILINEAR_SAMPLINGMODE:
+                magFilter = WebGPUConstants.FilterMode.Linear;
+                minFilter = WebGPUConstants.FilterMode.Linear;
+                if (!useMipMaps) {
+                    mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                    lodMinClamp = lodMaxClamp = 0;
+                } else {
+                    mipmapFilter = WebGPUConstants.FilterMode.Linear;
+                }
+                break;
+            case Constants.TEXTURE_NEAREST_NEAREST_MIPLINEAR:
+                magFilter = WebGPUConstants.FilterMode.Nearest;
+                minFilter = WebGPUConstants.FilterMode.Nearest;
+                if (!useMipMaps) {
+                    mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                    lodMinClamp = lodMaxClamp = 0;
+                } else {
+                    mipmapFilter = WebGPUConstants.FilterMode.Linear;
+                }
+                break;
+            case Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST:
+                magFilter = WebGPUConstants.FilterMode.Nearest;
+                minFilter = WebGPUConstants.FilterMode.Nearest;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                if (!useMipMaps) {
+                    lodMinClamp = lodMaxClamp = 0;
+                }
+                break;
+            case Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST:
+                magFilter = WebGPUConstants.FilterMode.Nearest;
+                minFilter = WebGPUConstants.FilterMode.Linear;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                if (!useMipMaps) {
+                    lodMinClamp = lodMaxClamp = 0;
+                }
+                break;
+            case Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR:
+                magFilter = WebGPUConstants.FilterMode.Nearest;
+                minFilter = WebGPUConstants.FilterMode.Linear;
+                if (!useMipMaps) {
+                    mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                    lodMinClamp = lodMaxClamp = 0;
+                } else {
+                    mipmapFilter = WebGPUConstants.FilterMode.Linear;
+                }
+                break;
+            case Constants.TEXTURE_NEAREST_LINEAR:
+                magFilter = WebGPUConstants.FilterMode.Nearest;
+                minFilter = WebGPUConstants.FilterMode.Linear;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                lodMinClamp = lodMaxClamp = 0;
+                break;
+            case Constants.TEXTURE_NEAREST_NEAREST:
+            case Constants.TEXTURE_NEAREST_SAMPLINGMODE:
+                magFilter = WebGPUConstants.FilterMode.Nearest;
+                minFilter = WebGPUConstants.FilterMode.Nearest;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                lodMinClamp = lodMaxClamp = 0;
+                break;
+            case Constants.TEXTURE_LINEAR_NEAREST_MIPNEAREST:
+                magFilter = WebGPUConstants.FilterMode.Linear;
+                minFilter = WebGPUConstants.FilterMode.Nearest;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                if (!useMipMaps) {
+                    lodMinClamp = lodMaxClamp = 0;
+                }
+                break;
+            case Constants.TEXTURE_LINEAR_NEAREST_MIPLINEAR:
+                magFilter = WebGPUConstants.FilterMode.Linear;
+                minFilter = WebGPUConstants.FilterMode.Nearest;
+                if (!useMipMaps) {
+                    mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                    lodMinClamp = lodMaxClamp = 0;
+                } else {
+                    mipmapFilter = WebGPUConstants.FilterMode.Linear;
+                }
+                break;
+            case Constants.TEXTURE_LINEAR_LINEAR:
+            case Constants.TEXTURE_BILINEAR_SAMPLINGMODE:
+                magFilter = WebGPUConstants.FilterMode.Linear;
+                minFilter = WebGPUConstants.FilterMode.Linear;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                lodMinClamp = lodMaxClamp = 0;
+                break;
+            case Constants.TEXTURE_LINEAR_NEAREST:
+                magFilter = WebGPUConstants.FilterMode.Linear;
+                minFilter = WebGPUConstants.FilterMode.Nearest;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                lodMinClamp = lodMaxClamp = 0;
+                break;
+            default:
+                magFilter = WebGPUConstants.FilterMode.Nearest;
+                minFilter = WebGPUConstants.FilterMode.Nearest;
+                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
+                lodMinClamp = lodMaxClamp = 0;
+                break;
+        }
+
+        return {
+            magFilter,
+            minFilter,
+            mipmapFilter,
+            lodMinClamp,
+            lodMaxClamp,
+        };
+    }
+
+    private static _GetWrappingMode(mode: number): GPUAddressMode {
+        switch (mode) {
+            case Constants.TEXTURE_WRAP_ADDRESSMODE:
+                return WebGPUConstants.AddressMode.Repeat;
+            case Constants.TEXTURE_CLAMP_ADDRESSMODE:
+                return WebGPUConstants.AddressMode.ClampToEdge;
+            case Constants.TEXTURE_MIRROR_ADDRESSMODE:
+                return WebGPUConstants.AddressMode.MirrorRepeat;
+        }
+        return WebGPUConstants.AddressMode.Repeat;
+    }
+
+    private static _GetSamplerWrappingDescriptor(internalTexture: InternalTexture): {
+        addressModeU: GPUAddressMode,
+        addressModeV: GPUAddressMode,
+        addressModeW: GPUAddressMode
+    } {
+        return {
+            addressModeU: this._GetWrappingMode(internalTexture._cachedWrapU!),
+            addressModeV: this._GetWrappingMode(internalTexture._cachedWrapV!),
+            addressModeW: this._GetWrappingMode(internalTexture._cachedWrapR!),
+        };
+    }
+
+    private static _GetSamplerDescriptor(internalTexture: InternalTexture): GPUSamplerDescriptor {
+        return {
+            ...this._GetSamplerFilterDescriptor(internalTexture),
+            ...this._GetSamplerWrappingDescriptor(internalTexture),
+            compare: internalTexture._comparisonFunction ? WebGPUTextureHelper.GetCompareFunction(internalTexture._comparisonFunction) : undefined,
+            maxAnisotropy: internalTexture._cachedAnisotropicFilteringLevel ?? 1,
+        };
+    }
+
+    public getSampler(internalTexture: InternalTexture, bypassCache = false): GPUSampler {
+        const hash = bypassCache ? 0 : WebGPUCacheSampler._GetSamplerHashCode(internalTexture);
+
+        let sampler = bypassCache ? undefined : WebGPUCacheSampler._samplers[hash];
+        if (!sampler) {
+            sampler =  this._device.createSampler(WebGPUCacheSampler._GetSamplerDescriptor(internalTexture));
+            if (!bypassCache) {
+                WebGPUCacheSampler._samplers[hash] = sampler;
+            }
+        }
+
+        return sampler;
+    }
+}

+ 43 - 278
src/Engines/WebGPU/webgpuTextureHelper.ts

@@ -25,7 +25,6 @@ import { Nullable } from '../../types';
 import { InternalTexture, InternalTextureSource } from '../../Materials/Textures/internalTexture';
 import { HardwareTextureWrapper } from '../../Materials/Textures/hardwareTextureWrapper';
 import { BaseTexture } from '../../Materials/Textures/baseTexture';
-import { Engine } from '../engine';
 import { WebGPUHardwareTexture } from './webgpuHardwareTexture';
 import { IColor4Like } from '../../Maths/math.like';
 
@@ -110,51 +109,6 @@ const clearFragmentSource = `
     }
     `;
 
-const filterToBits = [
-    0 | 0 << 1 | 0 << 2, // not used
-    0 | 0 << 1 | 0 << 2, // TEXTURE_NEAREST_SAMPLINGMODE / TEXTURE_NEAREST_NEAREST
-    1 | 1 << 1 | 0 << 2, // TEXTURE_BILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR
-    1 | 1 << 1 | 1 << 2, // TEXTURE_TRILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR_MIPLINEAR
-    0 | 0 << 1 | 0 << 2, // TEXTURE_NEAREST_NEAREST_MIPNEAREST
-    0 | 1 << 1 | 0 << 2, // TEXTURE_NEAREST_LINEAR_MIPNEAREST
-    0 | 1 << 1 | 1 << 2, // TEXTURE_NEAREST_LINEAR_MIPLINEAR
-    0 | 1 << 1 | 0 << 2, // TEXTURE_NEAREST_LINEAR
-    0 | 0 << 1 | 1 << 2, // TEXTURE_NEAREST_NEAREST_MIPLINEAR
-    1 | 0 << 1 | 0 << 2, // TEXTURE_LINEAR_NEAREST_MIPNEAREST
-    1 | 0 << 1 | 1 << 2, // TEXTURE_LINEAR_NEAREST_MIPLINEAR
-    1 | 1 << 1 | 0 << 2, // TEXTURE_LINEAR_LINEAR_MIPNEAREST
-    1 | 0 << 1 | 0 << 2, // TEXTURE_LINEAR_NEAREST
-];
-
-// subtract 0x01FF from the comparison function value before indexing this array!
-const comparisonFunctionToBits = [
-    0 << 3 | 0 << 4 | 0 << 5 | 0 << 6, // undefined
-    0 << 3 | 0 << 4 | 0 << 5 | 1 << 6, // NEVER
-    0 << 3 | 0 << 4 | 1 << 5 | 0 << 6, // LESS
-    0 << 3 | 0 << 4 | 1 << 5 | 1 << 6, // EQUAL
-    0 << 3 | 1 << 4 | 0 << 5 | 0 << 6, // LEQUAL
-    0 << 3 | 1 << 4 | 0 << 5 | 1 << 6, // GREATER
-    0 << 3 | 1 << 4 | 1 << 5 | 0 << 6, // NOTEQUAL
-    0 << 3 | 1 << 4 | 1 << 5 | 1 << 6, // GEQUAL
-    1 << 3 | 0 << 4 | 0 << 5 | 0 << 6, // ALWAYS
-];
-
-const filterNoMipToBits = [
-    0 << 7, // not used
-    1 << 7, // TEXTURE_NEAREST_SAMPLINGMODE / TEXTURE_NEAREST_NEAREST
-    1 << 7, // TEXTURE_BILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR
-    0 << 7, // TEXTURE_TRILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR_MIPLINEAR
-    0 << 7, // TEXTURE_NEAREST_NEAREST_MIPNEAREST
-    0 << 7, // TEXTURE_NEAREST_LINEAR_MIPNEAREST
-    0 << 7, // TEXTURE_NEAREST_LINEAR_MIPLINEAR
-    1 << 7, // TEXTURE_NEAREST_LINEAR
-    0 << 7, // TEXTURE_NEAREST_NEAREST_MIPLINEAR
-    0 << 7, // TEXTURE_LINEAR_NEAREST_MIPNEAREST
-    0 << 7, // TEXTURE_LINEAR_NEAREST_MIPLINEAR
-    0 << 7, // TEXTURE_LINEAR_LINEAR_MIPNEAREST
-    1 << 7, // TEXTURE_LINEAR_NEAREST
-];
-
 enum PipelineType {
     MipMap = 0,
     InvertYPremultiplyAlpha = 1,
@@ -183,7 +137,6 @@ export class WebGPUTextureHelper {
     private _pipelines: { [format: string]: Array<GPURenderPipeline> } = {};
     private _compiledShaders: GPUShaderModule[][] = [];
     private _deferredReleaseTextures: Array<[Nullable<HardwareTextureWrapper | GPUTexture>, Nullable<BaseTexture>, Nullable<InternalTexture>]> = [];
-    private _samplers: { [hash: number]: GPUSampler } = {};
     private _commandEncoderForCreation: GPUCommandEncoder;
 
     public static ComputeNumMipmapLevels(width: number, height: number) {
@@ -260,7 +213,7 @@ export class WebGPUTextureHelper {
         return pipeline;
     }
 
-    private _getTextureTypeFromFormat(format: GPUTextureFormat): number {
+    private static _GetTextureTypeFromFormat(format: GPUTextureFormat): number {
         switch (format) {
             // One Component = 8 bits
             case WebGPUConstants.TextureFormat.R8Unorm:
@@ -340,7 +293,7 @@ export class WebGPUTextureHelper {
         return Constants.TEXTURETYPE_UNSIGNED_BYTE;
     }
 
-    private _getBlockInformationFromFormat(format: GPUTextureFormat): { width: number, height: number, length: number } {
+    private static _GetBlockInformationFromFormat(format: GPUTextureFormat): { width: number, height: number, length: number } {
         switch (format) {
             // 8 bits formats
             case WebGPUConstants.TextureFormat.R8Unorm:
@@ -433,19 +386,42 @@ export class WebGPUTextureHelper {
         return { width: 1, height: 1, length: 4 };
     }
 
-    private _isHardwareTexture(texture: HardwareTextureWrapper | GPUTexture): texture is HardwareTextureWrapper {
+    private static _IsHardwareTexture(texture: HardwareTextureWrapper | GPUTexture): texture is HardwareTextureWrapper {
         return !!(texture as HardwareTextureWrapper).release;
     }
 
-    private _isInternalTexture(texture: InternalTexture | GPUTexture): texture is InternalTexture {
+    private static _IsInternalTexture(texture: InternalTexture | GPUTexture): texture is InternalTexture {
         return !!(texture as InternalTexture).dispose;
     }
 
-    public isImageBitmap(imageBitmap: ImageBitmap | { width: number, height: number }): imageBitmap is ImageBitmap {
+    public static GetCompareFunction(compareFunction: Nullable<number>): GPUCompareFunction {
+        switch (compareFunction) {
+            case Constants.ALWAYS:
+                return WebGPUConstants.CompareFunction.Always;
+            case Constants.EQUAL:
+                return WebGPUConstants.CompareFunction.Equal;
+            case Constants.GREATER:
+                return WebGPUConstants.CompareFunction.Greater;
+            case Constants.GEQUAL:
+                return WebGPUConstants.CompareFunction.GreaterEqual;
+            case Constants.LESS:
+                return WebGPUConstants.CompareFunction.Less;
+            case Constants.LEQUAL:
+                return WebGPUConstants.CompareFunction.LessEqual;
+            case Constants.NEVER:
+                return WebGPUConstants.CompareFunction.Never;
+            case Constants.NOTEQUAL:
+                return WebGPUConstants.CompareFunction.NotEqual;
+            default:
+                return WebGPUConstants.CompareFunction.Less;
+        }
+    }
+
+    public static IsImageBitmap(imageBitmap: ImageBitmap | { width: number, height: number }): imageBitmap is ImageBitmap {
         return (imageBitmap as ImageBitmap).close !== undefined;
     }
 
-    public isImageBitmapArray(imageBitmap: ImageBitmap[] | { width: number, height: number }): imageBitmap is ImageBitmap[] {
+    public static IsImageBitmapArray(imageBitmap: ImageBitmap[] | { width: number, height: number }): imageBitmap is ImageBitmap[] {
         return Array.isArray(imageBitmap as ImageBitmap[]) && (imageBitmap as ImageBitmap[])[0].close !== undefined;
     }
 
@@ -453,7 +429,7 @@ export class WebGPUTextureHelper {
         this._commandEncoderForCreation = encoder;
     }
 
-    public isCompressedFormat(format: GPUTextureFormat): boolean {
+    public static IsCompressedFormat(format: GPUTextureFormat): boolean {
         switch (format) {
             case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
             case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
@@ -475,7 +451,7 @@ export class WebGPUTextureHelper {
         return false;
     }
 
-    public getWebGPUTextureFormat(type: number, format: number): GPUTextureFormat {
+    public static GetWebGPUTextureFormat(type: number, format: number): GPUTextureFormat {
         switch (format) {
             case Constants.TEXTUREFORMAT_DEPTH24_STENCIL8:
                 return WebGPUConstants.TextureFormat.Depth24PlusStencil8;
@@ -766,7 +742,7 @@ export class WebGPUTextureHelper {
 
         const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(imageBitmap.width, imageBitmap.height) : 1;
         const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
-        const additionalUsages = hasMipmaps && !this.isCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
+        const additionalUsages = hasMipmaps && !WebGPUTextureHelper.IsCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
 
         const gpuTexture = this._device.createTexture({
             size: textureSize,
@@ -777,7 +753,7 @@ export class WebGPUTextureHelper {
             mipLevelCount
         });
 
-        if (this.isImageBitmap(imageBitmap)) {
+        if (WebGPUTextureHelper.IsImageBitmap(imageBitmap)) {
             this.updateTexture(imageBitmap, gpuTexture, imageBitmap.width, imageBitmap.height, layerCount, format, 0, 0, invertY, premultiplyAlpha, 0, 0, commandEncoder);
 
             if (hasMipmaps && generateMipmaps) {
@@ -791,12 +767,12 @@ export class WebGPUTextureHelper {
     public createCubeTexture(imageBitmaps: ImageBitmap[] | { width: number, height: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
         sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
     {
-        const width = this.isImageBitmapArray(imageBitmaps) ? imageBitmaps[0].width : imageBitmaps.width;
-        const height = this.isImageBitmapArray(imageBitmaps) ? imageBitmaps[0].height : imageBitmaps.height;
+        const width = WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps) ? imageBitmaps[0].width : imageBitmaps.width;
+        const height = WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps) ? imageBitmaps[0].height : imageBitmaps.height;
 
         const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(width, height) : 1;
         const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
-        const additionalUsages = hasMipmaps && !this.isCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
+        const additionalUsages = hasMipmaps && !WebGPUTextureHelper.IsCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
 
         const gpuTexture = this._device.createTexture({
             size: {
@@ -811,7 +787,7 @@ export class WebGPUTextureHelper {
             mipLevelCount
         });
 
-        if (this.isImageBitmapArray(imageBitmaps)) {
+        if (WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps)) {
             this.updateCubeTextures(imageBitmaps, gpuTexture, width, height, format, invertY, premultiplyAlpha, 0, 0, commandEncoder);
 
             if (hasMipmaps && generateMipmaps) {
@@ -916,7 +892,7 @@ export class WebGPUTextureHelper {
 
         const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
 
-        gpuTextureWrapper.format = this.getWebGPUTextureFormat(texture.type, texture.format);
+        gpuTextureWrapper.format = WebGPUTextureHelper.GetWebGPUTextureFormat(texture.type, texture.format);
 
         gpuTextureWrapper.textureUsages =
             texture._source === InternalTextureSource.RenderTarget || texture.source === InternalTextureSource.MultiRenderTarget ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment :
@@ -1003,7 +979,7 @@ export class WebGPUTextureHelper {
     public updateTexture(imageBitmap: ImageBitmap | Uint8Array, gpuTexture: GPUTexture, width: number, height: number, layers: number, format: GPUTextureFormat, faceIndex: number = 0, mipLevel: number = 0, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
         commandEncoder?: GPUCommandEncoder): void
     {
-        const blockInformation = this._getBlockInformationFromFormat(format);
+        const blockInformation = WebGPUTextureHelper._GetBlockInformationFromFormat(format);
 
         const textureCopyView: GPUTextureCopyView = {
             texture: gpuTexture,
@@ -1078,7 +1054,7 @@ export class WebGPUTextureHelper {
     }
 
     public readPixels(texture: GPUTexture, x: number, y: number, width: number, height: number, format: GPUTextureFormat, faceIndex: number = 0, mipLevel: number = 0, buffer: Nullable<ArrayBufferView> = null): Promise<ArrayBufferView> {
-        const blockInformation = this._getBlockInformationFromFormat(format);
+        const blockInformation = WebGPUTextureHelper._GetBlockInformationFromFormat(format);
 
         const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
 
@@ -1110,7 +1086,7 @@ export class WebGPUTextureHelper {
 
         this._device.defaultQueue.submit([commandEncoder!.finish()]);
 
-        const type = this._getTextureTypeFromFormat(format);
+        const type = WebGPUTextureHelper._GetTextureTypeFromFormat(format);
         const floatFormat = type === Constants.TEXTURETYPE_FLOAT ? 2 : type === Constants.TEXTURETYPE_HALF_FLOAT ? 1 : 0;
 
         return this._bufferManager.readDataFromBuffer(gpuBuffer, size, width, height, bytesPerRow, bytesPerRowAligned, floatFormat, 0, buffer);
@@ -1121,7 +1097,7 @@ export class WebGPUTextureHelper {
     //------------------------------------------------------------------------------
 
     public releaseTexture(texture: InternalTexture | GPUTexture): void {
-        if (this._isInternalTexture(texture)) {
+        if (WebGPUTextureHelper._IsInternalTexture(texture)) {
             const hardwareTexture = texture._hardwareTexture;
             const irradianceTexture = texture._irradianceTexture;
             const depthStencilTexture = texture._depthStencilTexture;
@@ -1138,7 +1114,7 @@ export class WebGPUTextureHelper {
             const [hardwareTexture, irradianceTexture, depthStencilTexture] = this._deferredReleaseTextures[i];
 
             if (hardwareTexture) {
-                if (this._isHardwareTexture(hardwareTexture)) {
+                if (WebGPUTextureHelper._IsHardwareTexture(hardwareTexture)) {
                     hardwareTexture.release();
                 } else {
                     hardwareTexture.destroy();
@@ -1150,215 +1126,4 @@ export class WebGPUTextureHelper {
 
         this._deferredReleaseTextures.length = 0;
     }
-
-    //------------------------------------------------------------------------------
-    //                              Samplers
-    //------------------------------------------------------------------------------
-
-    private static _GetSamplerHashCode(texture: InternalTexture): number {
-        let code =
-            filterToBits[texture.samplingMode] +
-            comparisonFunctionToBits[(texture._comparisonFunction || 0x0202) - 0x0200 + 1] +
-            filterNoMipToBits[texture.samplingMode] + // handle the lodMinClamp = lodMaxClamp = 0 case when no filter used for mip mapping
-            ((texture._cachedWrapU ?? 1) << 8) +
-            ((texture._cachedWrapV ?? 1) << 10) +
-            ((texture._cachedWrapR ?? 1) << 12) +
-            ((texture.generateMipMaps ? 1 : 0) << 14) + // need to factor this in because _getSamplerFilterDescriptor depends on samplingMode AND generateMipMaps!
-            ((texture._cachedAnisotropicFilteringLevel ?? 1) << 15);
-
-        return code;
-    }
-
-   public getCompareFunction(compareFunction: Nullable<number>): GPUCompareFunction {
-        switch (compareFunction) {
-            case Constants.ALWAYS:
-                return WebGPUConstants.CompareFunction.Always;
-            case Constants.EQUAL:
-                return WebGPUConstants.CompareFunction.Equal;
-            case Constants.GREATER:
-                return WebGPUConstants.CompareFunction.Greater;
-            case Constants.GEQUAL:
-                return WebGPUConstants.CompareFunction.GreaterEqual;
-            case Constants.LESS:
-                return WebGPUConstants.CompareFunction.Less;
-            case Constants.LEQUAL:
-                return WebGPUConstants.CompareFunction.LessEqual;
-            case Constants.NEVER:
-                return WebGPUConstants.CompareFunction.Never;
-            case Constants.NOTEQUAL:
-                return WebGPUConstants.CompareFunction.NotEqual;
-            default:
-                return WebGPUConstants.CompareFunction.Less;
-        }
-    }
-
-    private _getSamplerFilterDescriptor(internalTexture: InternalTexture): {
-        magFilter: GPUFilterMode,
-        minFilter: GPUFilterMode,
-        mipmapFilter: GPUFilterMode,
-        lodMinClamp?: number,
-        lodMaxClamp?: number,
-    } {
-        let magFilter: GPUFilterMode, minFilter: GPUFilterMode, mipmapFilter: GPUFilterMode, lodMinClamp: number | undefined, lodMaxClamp: number | undefined;
-        const useMipMaps = internalTexture.generateMipMaps;
-        switch (internalTexture.samplingMode) {
-            case Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST:
-                magFilter = WebGPUConstants.FilterMode.Linear;
-                minFilter = WebGPUConstants.FilterMode.Linear;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                if (!useMipMaps) {
-                    lodMinClamp = lodMaxClamp = 0;
-                }
-                break;
-            case Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR:
-            case Constants.TEXTURE_TRILINEAR_SAMPLINGMODE:
-                magFilter = WebGPUConstants.FilterMode.Linear;
-                minFilter = WebGPUConstants.FilterMode.Linear;
-                if (!useMipMaps) {
-                    mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                    lodMinClamp = lodMaxClamp = 0;
-                } else {
-                    mipmapFilter = WebGPUConstants.FilterMode.Linear;
-                }
-                break;
-            case Constants.TEXTURE_NEAREST_NEAREST_MIPLINEAR:
-                magFilter = WebGPUConstants.FilterMode.Nearest;
-                minFilter = WebGPUConstants.FilterMode.Nearest;
-                if (!useMipMaps) {
-                    mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                    lodMinClamp = lodMaxClamp = 0;
-                } else {
-                    mipmapFilter = WebGPUConstants.FilterMode.Linear;
-                }
-                break;
-            case Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST:
-                magFilter = WebGPUConstants.FilterMode.Nearest;
-                minFilter = WebGPUConstants.FilterMode.Nearest;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                if (!useMipMaps) {
-                    lodMinClamp = lodMaxClamp = 0;
-                }
-                break;
-            case Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST:
-                magFilter = WebGPUConstants.FilterMode.Nearest;
-                minFilter = WebGPUConstants.FilterMode.Linear;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                if (!useMipMaps) {
-                    lodMinClamp = lodMaxClamp = 0;
-                }
-                break;
-            case Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR:
-                magFilter = WebGPUConstants.FilterMode.Nearest;
-                minFilter = WebGPUConstants.FilterMode.Linear;
-                if (!useMipMaps) {
-                    mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                    lodMinClamp = lodMaxClamp = 0;
-                } else {
-                    mipmapFilter = WebGPUConstants.FilterMode.Linear;
-                }
-                break;
-            case Constants.TEXTURE_NEAREST_LINEAR:
-                magFilter = WebGPUConstants.FilterMode.Nearest;
-                minFilter = WebGPUConstants.FilterMode.Linear;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                lodMinClamp = lodMaxClamp = 0;
-                break;
-            case Constants.TEXTURE_NEAREST_NEAREST:
-            case Constants.TEXTURE_NEAREST_SAMPLINGMODE:
-                magFilter = WebGPUConstants.FilterMode.Nearest;
-                minFilter = WebGPUConstants.FilterMode.Nearest;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                lodMinClamp = lodMaxClamp = 0;
-                break;
-            case Constants.TEXTURE_LINEAR_NEAREST_MIPNEAREST:
-                magFilter = WebGPUConstants.FilterMode.Linear;
-                minFilter = WebGPUConstants.FilterMode.Nearest;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                if (!useMipMaps) {
-                    lodMinClamp = lodMaxClamp = 0;
-                }
-                break;
-            case Constants.TEXTURE_LINEAR_NEAREST_MIPLINEAR:
-                magFilter = WebGPUConstants.FilterMode.Linear;
-                minFilter = WebGPUConstants.FilterMode.Nearest;
-                if (!useMipMaps) {
-                    mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                    lodMinClamp = lodMaxClamp = 0;
-                } else {
-                    mipmapFilter = WebGPUConstants.FilterMode.Linear;
-                }
-                break;
-            case Constants.TEXTURE_LINEAR_LINEAR:
-            case Constants.TEXTURE_BILINEAR_SAMPLINGMODE:
-                magFilter = WebGPUConstants.FilterMode.Linear;
-                minFilter = WebGPUConstants.FilterMode.Linear;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                lodMinClamp = lodMaxClamp = 0;
-                break;
-            case Constants.TEXTURE_LINEAR_NEAREST:
-                magFilter = WebGPUConstants.FilterMode.Linear;
-                minFilter = WebGPUConstants.FilterMode.Nearest;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                lodMinClamp = lodMaxClamp = 0;
-                break;
-            default:
-                magFilter = WebGPUConstants.FilterMode.Nearest;
-                minFilter = WebGPUConstants.FilterMode.Nearest;
-                mipmapFilter = WebGPUConstants.FilterMode.Nearest;
-                lodMinClamp = lodMaxClamp = 0;
-                break;
-        }
-
-        return {
-            magFilter,
-            minFilter,
-            mipmapFilter,
-            lodMinClamp,
-            lodMaxClamp,
-        };
-    }
-
-    private _getWrappingMode(mode: number): GPUAddressMode {
-        switch (mode) {
-            case Engine.TEXTURE_WRAP_ADDRESSMODE:
-                return WebGPUConstants.AddressMode.Repeat;
-            case Engine.TEXTURE_CLAMP_ADDRESSMODE:
-                return WebGPUConstants.AddressMode.ClampToEdge;
-            case Engine.TEXTURE_MIRROR_ADDRESSMODE:
-                return WebGPUConstants.AddressMode.MirrorRepeat;
-        }
-        return WebGPUConstants.AddressMode.Repeat;
-    }
-
-    private _getSamplerWrappingDescriptor(internalTexture: InternalTexture): {
-        addressModeU: GPUAddressMode,
-        addressModeV: GPUAddressMode,
-        addressModeW: GPUAddressMode
-    } {
-        return {
-            addressModeU: this._getWrappingMode(internalTexture._cachedWrapU!),
-            addressModeV: this._getWrappingMode(internalTexture._cachedWrapV!),
-            addressModeW: this._getWrappingMode(internalTexture._cachedWrapR!),
-        };
-    }
-
-    private _getSamplerDescriptor(internalTexture: InternalTexture): GPUSamplerDescriptor {
-        return {
-            ...this._getSamplerFilterDescriptor(internalTexture),
-            ...this._getSamplerWrappingDescriptor(internalTexture),
-            compare: internalTexture._comparisonFunction ? this.getCompareFunction(internalTexture._comparisonFunction) : undefined,
-            maxAnisotropy: internalTexture._cachedAnisotropicFilteringLevel ?? 1,
-        };
-    }
-
-    public getSampler(internalTexture: InternalTexture): GPUSampler {
-        const hash = WebGPUTextureHelper._GetSamplerHashCode(internalTexture);
-
-        let sampler = this._samplers[hash];
-        if (!sampler) {
-            sampler = this._samplers[hash] = this._device.createSampler(this._getSamplerDescriptor(internalTexture));
-        }
-
-        return sampler;
-    }
 }

+ 9 - 5
src/Engines/webgpuEngine.ts

@@ -33,6 +33,7 @@ import { IWebRequest } from '../Misc/interfaces/iWebRequest';
 import { UniformBuffer } from '../Materials/uniformBuffer';
 import { WebGPURenderPassWrapper } from './WebGPU/webgpuRenderPassWrapper';
 import { IMultiRenderTargetOptions } from '../Materials/Textures/multiRenderTarget';
+import { WebGPUCacheSampler } from "./WebGPU/webgpuCacheSampler";
 
 declare type VideoTexture = import("../Materials/Textures/videoTexture").VideoTexture;
 declare type RenderTargetTexture = import("../Materials/Textures/renderTargetTexture").RenderTargetTexture;
@@ -168,6 +169,7 @@ export class WebGPUEngine extends Engine {
     private _mainPassSampleCount: number;
     private _textureHelper: WebGPUTextureHelper;
     private _bufferManager: WebGPUBufferManager;
+    private _cacheSampler: WebGPUCacheSampler;
     private _emptyVertexBuffer: VertexBuffer;
     private _lastCachedWrapU: number;
     private _lastCachedWrapV: number;
@@ -360,6 +362,7 @@ export class WebGPUEngine extends Engine {
             .then(() => {
                 this._bufferManager = new WebGPUBufferManager(this._device);
                 this._textureHelper = new WebGPUTextureHelper(this._device, this._glslang, this._bufferManager);
+                this._cacheSampler = new WebGPUCacheSampler(this._device);
 
                 if (dbgVerboseLogsForFirstFrames) {
                     if ((this as any)._count === undefined) {
@@ -450,6 +453,7 @@ export class WebGPUEngine extends Engine {
             depthTextureExtension: true,
             vertexArrayObject: false,
             instancedArrays: true,
+            timerQuery: undefined,
             canUseTimestampForTimerQuery: false,
             multiview: false,
             oculusMultiview: false,
@@ -1293,7 +1297,7 @@ export class WebGPUEngine extends Engine {
                     if (!texture._hardwareTexture?.underlyingResource) { // the texture could have been created before reaching this point so don't recreate it if already existing
                         const gpuTextureWrapper = this._textureHelper.createGPUTextureForInternalTexture(texture, imageBitmap.width, imageBitmap.height);
 
-                        if (this._textureHelper.isImageBitmap(imageBitmap)) {
+                        if (WebGPUTextureHelper.IsImageBitmap(imageBitmap)) {
                             this._textureHelper.updateTexture(imageBitmap, gpuTextureWrapper.underlyingResource!, imageBitmap.width, imageBitmap.height, texture.depth, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0, this._uploadEncoder);
                             if (!noMipmap && !isCompressed) {
                                 this._generateMipmaps(texture, this._uploadEncoder);
@@ -2990,7 +2994,7 @@ export class WebGPUEngine extends Engine {
         this._currentRenderTarget = texture;
 
         this._rttRenderPassWrapper.colorAttachmentGPUTextures[0] = hardwareTexture;
-        this._rttRenderPassWrapper.depthTextureFormat = this._currentRenderTarget._depthStencilTexture ? this._textureHelper.getWebGPUTextureFormat(-1, this._currentRenderTarget._depthStencilTexture.format) : undefined;
+        this._rttRenderPassWrapper.depthTextureFormat = this._currentRenderTarget._depthStencilTexture ? WebGPUTextureHelper.GetWebGPUTextureFormat(-1, this._currentRenderTarget._depthStencilTexture.format) : undefined;
 
         this._setDepthTextureFormat(this._rttRenderPassWrapper);
         this._setColorFormat(this._rttRenderPassWrapper);
@@ -3315,7 +3319,7 @@ export class WebGPUEngine extends Engine {
         }
 
         const stencilFrontBack: GPUStencilStateFaceDescriptor = {
-            compare: this._textureHelper.getCompareFunction(this._stencilState.stencilFunc),
+            compare: WebGPUTextureHelper.GetCompareFunction(this._stencilState.stencilFunc),
             depthFailOp: this._getOpFunction(this._stencilState.stencilOpDepthFail, WebGPUConstants.StencilOperation.Keep),
             failOp: this._getOpFunction(this._stencilState.stencilOpStencilFail, WebGPUConstants.StencilOperation.Keep),
             passOp: this._getOpFunction(this._stencilState.stencilOpStencilDepthPass, WebGPUConstants.StencilOperation.Replace)
@@ -3323,7 +3327,7 @@ export class WebGPUEngine extends Engine {
 
         return {
             depthWriteEnabled: this.getDepthWrite(),
-            depthCompare: this.getDepthBuffer() ? this._textureHelper.getCompareFunction(this.getDepthFunction()) : WebGPUConstants.CompareFunction.Always,
+            depthCompare: this.getDepthBuffer() ? WebGPUTextureHelper.GetCompareFunction(this.getDepthFunction()) : WebGPUConstants.CompareFunction.Always,
             format: this._depthTextureFormat,
             stencilFront: stencilFrontBack,
             stencilBack: stencilFrontBack,
@@ -3895,7 +3899,7 @@ export class WebGPUEngine extends Engine {
                         }
                         entries.push({
                             binding: bindingInfo.samplerBinding,
-                            resource: this._textureHelper.getSampler(texture),
+                            resource: this._cacheSampler.getSampler(texture),
                         });
                     } else {
                         Logger.Error(`Sampler "${bindingDefinition.name}" could not be bound. bindingDefinition=${JSON.stringify(bindingDefinition)}, webgpuPipelineContext.samplers=${JSON.stringify(webgpuPipelineContext.samplers)}`, 50);