|
@@ -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;
|
|
|
+}
|