Browse Source

Add raw texture management (save for 2DArray and 3D)

Popov72 4 years ago
parent
commit
8ef0b280fa
1 changed files with 270 additions and 8 deletions
  1. 270 8
      src/Engines/webgpuEngine.ts

+ 270 - 8
src/Engines/webgpuEngine.ts

@@ -29,6 +29,7 @@ import { DepthTextureCreationOptions } from './depthTextureCreationOptions';
 import { HardwareTextureWrapper } from '../Materials/Textures/hardwareTextureWrapper';
 import { WebGPUHardwareTexture } from './WebGPU/webgpuHardwareTexture';
 import { IColor4Like } from '../Maths/math.like';
+import { IWebRequest } from '../Misc/interfaces/iWebRequest';
 
 declare type VideoTexture = import("../Materials/Textures/videoTexture").VideoTexture;
 declare type RenderTargetTexture = import("../Materials/Textures/renderTargetTexture").RenderTargetTexture;
@@ -254,11 +255,12 @@ export class WebGPUEngine extends Engine {
         super(null);
 
         ThinEngine.Features.forceBitmapOverHTMLImageElement = true;
-        ThinEngine.Features.supportRenderAndCopyToLodForFloatTextures = false; // TODO WEBGPU should be true but needs RTT support first for env texture to be generated correctly with this flag on
+        ThinEngine.Features.supportRenderAndCopyToLodForFloatTextures = true;
         ThinEngine.Features.framebuffersHaveYTopToBottom = true;
         ThinEngine.Features.supportDepthStencilTexture = true;
         ThinEngine.Features.supportShadowSamplers = true;
         ThinEngine.Features.uniformBufferHardCheckMatrix = true;
+        ThinEngine.Features.allowTexturePrefiltering = true;
 
         options.deviceDescriptor = options.deviceDescriptor || { };
         options.swapChainFormat = options.swapChainFormat || WebGPUConstants.TextureFormat.BGRA8Unorm;
@@ -1484,6 +1486,156 @@ export class WebGPUEngine extends Engine {
         );
     }
 
+    public createRawTexture(data: Nullable<ArrayBufferView>, width: number, height: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number,
+        compression: Nullable<string> = null, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture
+    {
+        const texture = new InternalTexture(this, InternalTextureSource.Raw);
+        texture.baseWidth = width;
+        texture.baseHeight = height;
+        texture.width = width;
+        texture.height = height;
+        texture.format = format;
+        texture.generateMipMaps = generateMipMaps;
+        texture.samplingMode = samplingMode;
+        texture.invertY = invertY;
+        texture._compression = compression;
+        texture.type = type;
+
+        if (!this._doNotHandleContextLost) {
+            texture._bufferView = data;
+        }
+
+        this._createGPUTextureForInternalTexture(texture, width, height);
+
+        this.updateRawTexture(texture, data, format, invertY, compression, type);
+
+        this._internalTexturesCache.push(texture);
+
+        return texture;
+    }
+
+    public createRawCubeTexture(data: Nullable<ArrayBufferView[]>, size: number, format: number, type: number,
+        generateMipMaps: boolean, invertY: boolean, samplingMode: number,
+        compression: Nullable<string> = null): InternalTexture
+    {
+        const texture = new InternalTexture(this, InternalTextureSource.CubeRaw);
+        texture.isCube = true;
+        texture.format = format === Constants.TEXTUREFORMAT_RGB ? Constants.TEXTUREFORMAT_RGBA : format;
+        texture.type = type;
+        texture.generateMipMaps = generateMipMaps;
+        texture.width = size;
+        texture.height = size;
+        if (!this._doNotHandleContextLost) {
+            texture._bufferViewArray = data;
+        }
+
+        this._createGPUTextureForInternalTexture(texture);
+
+        if (data) {
+            this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
+        }
+
+        return texture;
+    }
+
+    public createRawCubeTextureFromUrl(url: string, scene: Nullable<Scene>, size: number, format: number, type: number, noMipmap: boolean,
+        callback: (ArrayBuffer: ArrayBuffer) => Nullable<ArrayBufferView[]>,
+        mipmapGenerator: Nullable<((faces: ArrayBufferView[]) => ArrayBufferView[][])>,
+        onLoad: Nullable<() => void> = null,
+        onError: Nullable<(message?: string, exception?: any) => void> = null,
+        samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
+        invertY: boolean = false): InternalTexture
+    {
+        const texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode, null);
+        scene?._addPendingData(texture);
+        texture.url = url;
+
+        this._internalTexturesCache.push(texture);
+
+        const onerror = (request?: IWebRequest, exception?: any) => {
+            scene?._removePendingData(texture);
+            if (onError && request) {
+                onError(request.status + " " + request.statusText, exception);
+            }
+        };
+
+        const internalCallback = (data: any) => {
+            const width = texture.width;
+            const faceDataArrays = callback(data);
+
+            if (!faceDataArrays) {
+                return;
+            }
+
+            const faces = [0, 2, 4, 1, 3, 5];
+
+            if (mipmapGenerator) {
+                const needConversion = format === Constants.TEXTUREFORMAT_RGB;
+                const mipData = mipmapGenerator(faceDataArrays);
+                const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
+                const faces = [0, 1, 2, 3, 4, 5];
+                for (let level = 0; level < mipData.length; level++) {
+                    const mipSize = width >> level;
+                    const allFaces = [];
+                    for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
+                        let mipFaceData = mipData[level][faces[faceIndex]];
+                        if (needConversion) {
+                            mipFaceData = _convertRGBtoRGBATextureData(mipFaceData, mipSize, mipSize, type);
+                        }
+                        allFaces.push(new Uint8Array(mipFaceData.buffer, mipFaceData.byteOffset, mipFaceData.byteLength));
+                    }
+                    this._textureHelper.updateCubeTextures(allFaces, gpuTextureWrapper.underlyingResource!, mipSize, mipSize, gpuTextureWrapper.format, invertY, false, 0, 0, this._uploadEncoder);
+                }
+            }
+            else {
+                const allFaces = [];
+                for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
+                    allFaces.push(faceDataArrays[faces[faceIndex]]);
+                }
+                this.updateRawCubeTexture(texture, allFaces, format, type, invertY);
+            }
+
+            texture.isReady = true;
+            scene?._removePendingData(texture);
+
+            if (onLoad) {
+                onLoad();
+            }
+        };
+
+        this._loadFile(url, (data) => {
+            internalCallback(data);
+        }, undefined, scene?.offlineProvider, true, onerror);
+
+        return texture;
+    }
+
+    public createRawTexture2DArray(data: Nullable<ArrayBufferView>, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number,
+        compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture
+    {
+        var source = InternalTextureSource.Raw2DArray;
+        var texture = new InternalTexture(this, source);
+
+        if (dbgShowWarningsNotImplemented) {
+            console.warn("createRawTexture2DArray not implemented yet in WebGPU");
+        }
+
+        return texture;
+    }
+
+    public createRawTexture3D(data: Nullable<ArrayBufferView>, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number,
+        compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture
+    {
+        var source = InternalTextureSource.Raw2DArray;
+        var texture = new InternalTexture(this, source);
+
+        if (dbgShowWarningsNotImplemented) {
+            console.warn("createRawTexture3D not implemented yet in WebGPU");
+        }
+
+        return texture;
+    }
+
     public generateMipMapsForCubemap(texture: InternalTexture, unbind = true) {
         if (texture.generateMipMaps) {
             let gpuTexture = texture._hardwareTexture?.underlyingResource;
@@ -1573,23 +1725,30 @@ export class WebGPUEngine extends Engine {
             else if (texture.isReady()) {
                 internalTexture = <InternalTexture>texture.getInternalTexture();
             }
-            // TODO WEBGPU remove this when raw textures are handled
-            else {
-                internalTexture = <InternalTexture>texture.getInternalTexture();
-            }
-            // TODO WEBGPU uncomment when raw textures are handled
-            /*!else if (texture.isCube) {
+            else if (texture.isCube) {
                 internalTexture = this.emptyCubeTexture;
+                if (dbgGenerateLogs) {
+                    console.log("Using a temporary empty cube texture. internalTexture.uniqueId=", texture.uniqueId, texture);
+                }
             }
             else if (texture.is3D) {
                 internalTexture = this.emptyTexture3D;
+                if (dbgGenerateLogs) {
+                    console.log("Using a temporary empty 3D texture. internalTexture.uniqueId=", texture.uniqueId, texture);
+                }
             }
             else if (texture.is2DArray) {
                 internalTexture = this.emptyTexture2DArray;
+                if (dbgGenerateLogs) {
+                    console.log("Using a temporary empty 2D array texture. internalTexture.uniqueId=", texture.uniqueId, texture);
+                }
             }
             else {
                 internalTexture = this.emptyTexture;
-            }*/
+                if (dbgGenerateLogs) {
+                    console.log("Using a temporary empty texture. internalTexture.uniqueId=", texture.uniqueId, texture);
+                }
+            }
 
             if (internalTexture && !internalTexture.isMultiview) {
                 // CUBIC_MODE and SKYBOX_MODE both require CLAMP_TO_EDGE.  All other modes use REPEAT.
@@ -1616,6 +1775,12 @@ export class WebGPUEngine extends Engine {
             }
 
             this._setInternalTexture(name, internalTexture);
+        } else {
+            if (dbgVerboseLogsForFirstFrames) {
+                if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
+                    console.log("_setTexture called with a null _currentEffect! frame #" + (this as any)._count, " - texture=", texture);
+                }
+            }
         }
 
         return true;
@@ -1862,6 +2027,73 @@ export class WebGPUEngine extends Engine {
         this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
     }
 
+    public updateRawTexture(texture: Nullable<InternalTexture>, bufferView: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
+        if (!texture) {
+            return;
+        }
+
+        if (!this._doNotHandleContextLost) {
+            texture._bufferView = bufferView;
+            texture.invertY = invertY;
+            texture._compression = compression;
+        }
+
+        if (bufferView) {
+            const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
+            const needConversion = format === Constants.TEXTUREFORMAT_RGB;
+
+            if (needConversion) {
+                bufferView = _convertRGBtoRGBATextureData(bufferView, texture.width, texture.height, type);
+            }
+
+            const data = new Uint8Array(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
+
+            this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0, this._uploadEncoder);
+            if (texture.generateMipMaps) {
+                this._generateMipmaps(texture);
+            }
+        }
+
+        texture.isReady = true;
+    }
+
+    public updateRawCubeTexture(texture: InternalTexture, bufferView: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable<string> = null, level: number = 0): void {
+        texture._bufferViewArray = bufferView;
+        texture.invertY = invertY;
+        texture._compression = compression;
+
+        const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
+        const needConversion = format === Constants.TEXTUREFORMAT_RGB;
+
+        const data = [];
+        for (let i = 0; i < bufferView.length; ++i) {
+            let faceData = bufferView[i];
+            if (needConversion) {
+                faceData = _convertRGBtoRGBATextureData(bufferView[i], texture.width, texture.height, type);
+            }
+            data.push(new Uint8Array(faceData.buffer, faceData.byteOffset, faceData.byteLength));
+        }
+
+        this._textureHelper.updateCubeTextures(data, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, gpuTextureWrapper.format, invertY, false, 0, 0, this._uploadEncoder);
+        if (texture.generateMipMaps) {
+            this._generateMipmaps(texture);
+        }
+
+        texture.isReady = true;
+    }
+
+    public updateRawTexture2DArray(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
+        if (dbgShowWarningsNotImplemented) {
+            console.warn("updateRawTexture2DArray not implemented yet in WebGPU");
+        }
+    }
+
+    public updateRawTexture3D(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
+        if (dbgShowWarningsNotImplemented) {
+            console.warn("updateRawTexture2DArray not implemented yet in WebGPU");
+        }
+    }
+
     public readPixels(x: number, y: number, width: number, height: number, hasAlpha = true): Promise<Uint8Array> | Uint8Array {
         const numChannels = 4; // no RGB format in WebGPU
         const size = height * width * numChannels;
@@ -3494,3 +3726,33 @@ export class WebGPUEngine extends Engine {
         return false;
     }
 }
+
+/** @hidden */
+function _convertRGBtoRGBATextureData(rgbData: any, width: number, height: number, textureType: number): ArrayBufferView {
+    // Create new RGBA data container.
+    var rgbaData: any;
+    if (textureType === Constants.TEXTURETYPE_FLOAT) {
+        rgbaData = new Float32Array(width * height * 4);
+    }
+    else {
+        rgbaData = new Uint32Array(width * height * 4);
+    }
+
+    // Convert each pixel.
+    for (let x = 0; x < width; x++) {
+        for (let y = 0; y < height; y++) {
+            let index = (y * width + x) * 3;
+            let newIndex = (y * width + x) * 4;
+
+            // Map Old Value to new value.
+            rgbaData[newIndex + 0] = rgbData[index + 0];
+            rgbaData[newIndex + 1] = rgbData[index + 1];
+            rgbaData[newIndex + 2] = rgbData[index + 2];
+
+            // Add fully opaque alpha channel.
+            rgbaData[newIndex + 3] = 1;
+        }
+    }
+
+    return rgbaData;
+}