Browse Source

Add support for 3D textures

Popov72 4 years ago
parent
commit
44c53b3c6f

+ 8 - 7
src/Engines/WebGPU/webgpuTextureHelper.ts

@@ -134,13 +134,14 @@ export class WebGPUTextureHelper {
         return false;
     }
 
-    public createTexture(imageBitmap: ImageBitmap | { width: number, height: number, layers: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
+    public createTexture(imageBitmap: ImageBitmap | { width: number, height: number, layers: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, is3D = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
         sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
     {
+        const layerCount = (imageBitmap as any).layers || 1;
         let textureSize = {
             width: imageBitmap.width,
             height: imageBitmap.height,
-            depth: (imageBitmap as any).layers || 1,
+            depth: layerCount,
         };
 
         const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.computeNumMipmapLevels(imageBitmap.width, imageBitmap.height) : 1;
@@ -149,7 +150,7 @@ export class WebGPUTextureHelper {
 
         const gpuTexture = this._device.createTexture({
             size: textureSize,
-            dimension: WebGPUConstants.TextureDimension.E2d,
+            dimension: is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureDimension.E2d,
             format,
             usage:  usages | additionalUsages,
             sampleCount,
@@ -157,7 +158,7 @@ export class WebGPUTextureHelper {
         });
 
         if (this.isImageBitmap(imageBitmap)) {
-            this.updateTexture(imageBitmap, gpuTexture, imageBitmap.width, imageBitmap.height, format, 0, 0, invertY, premultiplyAlpha, 0, 0, commandEncoder);
+            this.updateTexture(imageBitmap, gpuTexture, imageBitmap.width, imageBitmap.height, layerCount, format, 0, 0, invertY, premultiplyAlpha, 0, 0, commandEncoder);
 
             if (hasMipmaps && generateMipmaps) {
                 this.generateMipmaps(gpuTexture, format, mipLevelCount, 0, commandEncoder);
@@ -443,11 +444,11 @@ export class WebGPUTextureHelper {
         for (let f = 0; f < faces.length; ++f) {
             let imageBitmap = imageBitmaps[faces[f]];
 
-            this.updateTexture(imageBitmap, gpuTexture, width, height, format, f, 0, invertY, premultiplyAlpha, offsetX, offsetY, commandEncoder);
+            this.updateTexture(imageBitmap, gpuTexture, width, height, 1, format, f, 0, invertY, premultiplyAlpha, offsetX, offsetY, commandEncoder);
         }
     }
 
-    public updateTexture(imageBitmap: ImageBitmap | Uint8Array, gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, faceIndex: number = 0, mipLevel: number = 0, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
+    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 useOwnCommandEncoder = commandEncoder === undefined;
@@ -471,7 +472,7 @@ export class WebGPUTextureHelper {
         const textureExtent = {
             width: Math.ceil(width / blockInformation.width) * blockInformation.width,
             height: Math.ceil(height / blockInformation.height) * blockInformation.height,
-            depth: 1
+            depth: layers || 1
         };
 
         if ((imageBitmap as Uint8Array).byteLength !== undefined) {

+ 9 - 0
src/Engines/thinEngine.ts

@@ -3388,6 +3388,15 @@ export class ThinEngine {
     }
 
     /**
+     * Update the dimensions of a texture
+     * @param width new width of the texture
+     * @param height new height of the texture
+     * @param depth new depth of the texture
+     */
+    public updateTextureDimensions(texture: InternalTexture, width: number, height: number, depth: number = 1): void {
+    }
+
+    /**
      * Update the sampling mode of a given texture
      * @param texture defines the texture to update
      * @param wrapU defines the texture wrap mode of the u coordinates

+ 79 - 20
src/Engines/webgpuEngine.ts

@@ -1415,7 +1415,7 @@ export class WebGPUEngine extends Engine {
                         const gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture, imageBitmap.width, imageBitmap.height);
 
                         if (this._textureHelper.isImageBitmap(imageBitmap)) {
-                            this._textureHelper.updateTexture(imageBitmap, gpuTextureWrapper.underlyingResource!, imageBitmap.width, imageBitmap.height, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0, this._uploadEncoder);
+                            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);
                             }
@@ -1619,13 +1619,31 @@ export class WebGPUEngine extends Engine {
     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);
+        const source = InternalTextureSource.Raw3D;
+        const texture = new InternalTexture(this, source);
 
-        if (dbgShowWarningsNotImplemented) {
-            console.warn("createRawTexture3D not implemented yet in WebGPU");
+        texture.baseWidth = width;
+        texture.baseHeight = height;
+        texture.baseDepth = depth;
+        texture.width = width;
+        texture.height = height;
+        texture.depth = depth;
+        texture.format = format;
+        texture.type = textureType;
+        texture.generateMipMaps = generateMipMaps;
+        texture.samplingMode = samplingMode;
+        texture.is3D = true;
+
+        if (!this._doNotHandleContextLost) {
+            texture._bufferView = data;
         }
 
+        this._createGPUTextureForInternalTexture(texture, width, height);
+
+        this.updateRawTexture3D(texture, data, format, invertY, compression, textureType);
+
+        this._internalTexturesCache.push(texture);
+
         return texture;
     }
 
@@ -1663,6 +1681,21 @@ export class WebGPUEngine extends Engine {
         }
     }
 
+    public updateTextureDimensions(texture: InternalTexture, width: number, height: number, depth: number = 1): void {
+        if (!texture._hardwareTexture) {
+            // the gpu texture is not created yet, so when it is it will be created with the right dimensions
+            return;
+        }
+
+        if (texture.width === width && texture.height === height && texture.depth === depth) {
+            return;
+        }
+
+        texture._hardwareTexture.release(); // don't defer the releasing! Else we will release at the end of this frame the gpu texture we are about to create in the next line...
+
+        this._createGPUTextureForInternalTexture(texture, width, height, depth);
+    }
+
     private _setInternalTexture(name: string, internalTexture: Nullable<InternalTexture>, baseName?: string, textureIndex = 0): void {
         baseName = baseName ?? name;
         if (this._currentEffect) {
@@ -1831,7 +1864,7 @@ export class WebGPUEngine extends Engine {
         this._setInternalTexture(name, texture);
     }
 
-    private _createGPUTextureForInternalTexture(texture: InternalTexture, width?: number, height?: number): WebGPUHardwareTexture {
+    private _createGPUTextureForInternalTexture(texture: InternalTexture, width?: number, height?: number, depth?: number): WebGPUHardwareTexture {
         if (!texture._hardwareTexture) {
             texture._hardwareTexture = this._createHardwareTexture();
         }
@@ -1842,6 +1875,9 @@ export class WebGPUEngine extends Engine {
         if (height === undefined) {
             height = texture.height;
         }
+        if (depth === undefined) {
+            depth = texture.depth;
+        }
 
         const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
 
@@ -1852,9 +1888,10 @@ export class WebGPUEngine extends Engine {
             texture._source === InternalTextureSource.Depth ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.OutputAttachment : -1;
 
         const generateMipMaps = texture._source === InternalTextureSource.RenderTarget ? false : texture.generateMipMaps;
-        const layerCount = texture.depth || 1;
+        const layerCount = depth || 1;
 
         if (texture.isCube) {
+            // TODO WEBGPU handle depth for cube textures?
             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);
@@ -1866,11 +1903,11 @@ export class WebGPUEngine extends Engine {
                 aspect: WebGPUConstants.TextureAspect.All
             });
         } else {
-            const gpuTexture = this._textureHelper.createTexture({ width, height, layers: layerCount }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
+            const gpuTexture = this._textureHelper.createTexture({ width, height, layers: layerCount }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, texture.is3D, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
 
             gpuTextureWrapper.set(gpuTexture);
             gpuTextureWrapper.createView({
-                dimension: texture.is2DArray ? WebGPUConstants.TextureViewDimension.E2dArray : WebGPUConstants.TextureViewDimension.E2d,
+                dimension: texture.is2DArray ? WebGPUConstants.TextureViewDimension.E2dArray : texture.is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
                 mipLevelCount: generateMipMaps ? WebGPUTextureHelper.computeNumMipmapLevels(width!, height!) : 1,
                 baseArrayLayer: 0,
                 baseMipLevel: 0,
@@ -1881,6 +1918,7 @@ export class WebGPUEngine extends Engine {
 
         texture.width = texture.baseWidth = width;
         texture.height = texture.baseHeight = height;
+        texture.depth = texture.baseDepth = depth;
 
         return gpuTextureWrapper;
     }
@@ -1926,7 +1964,7 @@ export class WebGPUEngine extends Engine {
         }
 
         createImageBitmap(canvas).then((bitmap) => {
-            this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, 0, 0, invertY, premulAlpha, 0, 0, this._uploadEncoder);
+            this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, width, height, texture.depth, gpuTextureWrapper.format, 0, 0, invertY, premulAlpha, 0, 0, this._uploadEncoder);
             if (texture.generateMipMaps) {
                 this._generateMipmaps(texture);
             }
@@ -1944,7 +1982,7 @@ export class WebGPUEngine extends Engine {
 
         const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
 
-        this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, xOffset, yOffset, this._uploadEncoder);
+        this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, texture.depth, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, xOffset, yOffset, this._uploadEncoder);
     }
 
     public updateVideoTexture(texture: Nullable<InternalTexture>, video: HTMLVideoElement, invertY: boolean): void {
@@ -1963,7 +2001,7 @@ export class WebGPUEngine extends Engine {
         }
 
         createImageBitmap(video).then((bitmap) => {
-            this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, gpuTextureWrapper.format, 0, 0, !invertY, false, 0, 0, this._uploadEncoder);
+            this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, texture.depth, gpuTextureWrapper.format, 0, 0, !invertY, false, 0, 0, this._uploadEncoder);
             if (texture.generateMipMaps) {
                 this._generateMipmaps(texture);
             }
@@ -1987,7 +2025,7 @@ export class WebGPUEngine extends Engine {
 
         const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
 
-        this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
+        this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, texture.depth, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
     }
 
     /** @hidden */
@@ -2008,7 +2046,7 @@ export class WebGPUEngine extends Engine {
 
         const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
 
-        this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
+        this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, texture.depth, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
     }
 
     /** @hidden */
@@ -2029,7 +2067,7 @@ export class WebGPUEngine extends Engine {
         const width = Math.ceil(texture.width / (1 << lod));
         const height = Math.ceil(texture.height / (1 << lod));
 
-        this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
+        this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, width, height, texture.depth, 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 {
@@ -2053,7 +2091,7 @@ export class WebGPUEngine extends Engine {
 
             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);
+            this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, texture.depth, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0, this._uploadEncoder);
             if (texture.generateMipMaps) {
                 this._generateMipmaps(texture);
             }
@@ -2087,16 +2125,37 @@ export class WebGPUEngine extends Engine {
         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 {
+    public updateRawTexture2DArray(texture: InternalTexture, bufferView: 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 updateRawTexture3D(texture: InternalTexture, bufferView: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
+        if (!this._doNotHandleContextLost) {
+            texture._bufferView = bufferView;
+            texture.format = format;
+            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, textureType);
+            }
+
+            const data = new Uint8Array(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
+
+            this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, texture.depth, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0, this._uploadEncoder);
+            if (texture.generateMipMaps) {
+                this._generateMipmaps(texture);
+            }
+        }
+
+        texture.isReady = true;
     }
 
     public readPixels(x: number, y: number, width: number, height: number, hasAlpha = true): Promise<ArrayBufferView> {

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

@@ -315,6 +315,8 @@ export class InternalTexture {
      * @param depth defines the new depth (1 by default)
      */
     public updateSize(width: int, height: int, depth: int = 1): void {
+        this._engine.updateTextureDimensions(this, width, height, depth);
+
         this.width = width;
         this.height = height;
         this.depth = depth;