@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
6 lines (5 loc) • 11.9 kB
JavaScript
/*
All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/4.32/esri/copyright.txt for details.
*/
import t from"../../core/Error.js";import"../../core/has.js";import e from"../../core/Logger.js";import{abortMaybe as i}from"../../core/maybe.js";import{isAbortError as r}from"../../core/promiseUtils.js";import{signal as s}from"../../core/signal.js";import{checkWebGLError as o}from"./checkWebGLError.js";import{TextureType as a,ResourceType as n,CompressedTextureFormat as l,TextureSamplingMode as p}from"./enums.js";import{GLObjectType as m}from"./GLObjectType.js";import{estimateMemory as h}from"./TextureDescriptor.js";import{isCompressedData as _,deriveInternalFormat as d,isTexImageSource as c,is3DTarget as u,validateTexture as g,isCompressedFormat as T,getDimensions as x,calcMipmapLevels as E,isSizedPixelFormat as M}from"./textureUtils.js";import{ValidatedTextureDescriptor as A}from"./ValidatedTextureDescriptor.js";const I=null,f=()=>e.getLogger("esri/views/webgl/Texture");let w=class e{constructor(e,i=null,r=null){if(this.type=m.Texture,this._glName=null,this._samplingModeDirty=!1,this._wrapModeDirty=!1,this._wasImmutablyAllocated=!1,this._compressionAbortController=s(null),"context"in e)this._descriptor=e,r=i;else{const r=A.validate(e,i);if(!r)throw new t("Texture descriptor invalid");this._descriptor=r}this._descriptor.target===a.TEXTURE_CUBE_MAP?this._setDataCubeMap(r):this.setData(r)}get glName(){return this._glName}get descriptor(){return this._descriptor}get usedMemory(){return h(this._descriptor)}get cachedMemory(){return this.usedMemory}get isDirty(){return this._samplingModeDirty||this._wrapModeDirty}get isCompressing(){return null!==this._compressionAbortController.value}dispose(){this.abortCompression(),this._glName&&this._descriptor.context.instanceCounter.decrement(n.Texture,this),this._descriptor.context.gl&&this._glName&&(this._descriptor.context.unbindTexture(this),this._descriptor.context.gl.deleteTexture(this._glName),this._glName=null)}release(){this.dispose()}resize(e,i){const r=this._descriptor;if(r.width!==e||r.height!==i){if(this._wasImmutablyAllocated)throw new t("Immutable textures can't be resized!");r.width=e,r.height=i,this._descriptor.target===a.TEXTURE_CUBE_MAP?this._setDataCubeMap(null):this.setData(null)}}enableCompression(t){this._descriptor.shouldCompress=t}setData(t){this.abortCompression(),!_(t)&&this._descriptor.internalFormat&&this._descriptor.internalFormat in l&&(this._descriptor.internalFormat=void 0),this._setData(t),!_(t)&&this._descriptor.shouldCompress&&this._compressOnWorker(t)}updateData(i,r,s,o,a,n,l=0){n||f().error("An attempt to use uninitialized data!"),this._glName||f().error("An attempt to update uninitialized texture!");const p=this._descriptor;p.internalFormat=d(p);const{context:m,pixelFormat:h,dataType:u,target:g,isImmutable:T}=p;if(T&&!this._wasImmutablyAllocated)throw new t("Cannot update immutable texture before allocation!");const x=m.bindTexture(this,e.TEXTURE_UNIT_FOR_UPDATES,!0);(r<0||s<0||r+o>p.width||s+a>p.height)&&f().error("An attempt to update out of bounds of the texture!"),this._configurePixelStorage();const{gl:E}=m;l&&(o&&a||f().warn("Must pass width and height if `UNPACK_SKIP_ROWS` is used"),E.pixelStorei(E.UNPACK_SKIP_ROWS,l)),c(n)?E.texSubImage2D(g,i,r,s,o,a,h,u,n):_(n)?E.compressedTexSubImage2D(g,i,r,s,o,a,p.internalFormat,n.levels[i]):E.texSubImage2D(g,i,r,s,o,a,h,u,n),l&&E.pixelStorei(E.UNPACK_SKIP_ROWS,0),m.bindTexture(x,e.TEXTURE_UNIT_FOR_UPDATES)}updateData3D(i,r,s,o,a,n,l,p){p||f().error("An attempt to use uninitialized data!"),this._glName||f().error("An attempt to update an uninitialized texture!");const m=this._descriptor;m.internalFormat=d(m);const{context:h,pixelFormat:c,dataType:g,isImmutable:T,target:x}=m;if(T&&!this._wasImmutablyAllocated)throw new t("Cannot update immutable texture before allocation!");u(x)||f().warn("Attempting to set 3D texture data on a non-3D texture");const E=h.bindTexture(this,e.TEXTURE_UNIT_FOR_UPDATES);h.setActiveTexture(e.TEXTURE_UNIT_FOR_UPDATES),(r<0||s<0||o<0||r+a>m.width||s+n>m.height||o+l>m.depth)&&f().error("An attempt to update out of bounds of the texture!"),this._configurePixelStorage();const{gl:M}=h;if(_(p))p=p.levels[i],M.compressedTexSubImage3D(x,i,r,s,o,a,n,l,m.internalFormat,p);else{const t=p;M.texSubImage3D(x,i,r,s,o,a,n,l,c,g,t)}h.bindTexture(E,e.TEXTURE_UNIT_FOR_UPDATES)}generateMipmap(){const i=this._descriptor;if(0===i.width||0===i.height)return;if(!i.hasMipmap){if(this._wasImmutablyAllocated)throw new t("Cannot add mipmaps to immutable texture after allocation");i.hasMipmap=!0,this._samplingModeDirty=!0,g(i)}i.samplingMode===p.LINEAR?(this._samplingModeDirty=!0,i.samplingMode=p.LINEAR_MIPMAP_NEAREST):i.samplingMode===p.NEAREST&&(this._samplingModeDirty=!0,i.samplingMode=p.NEAREST_MIPMAP_NEAREST);const r=this._descriptor.context.bindTexture(this,e.TEXTURE_UNIT_FOR_UPDATES);this._descriptor.context.setActiveTexture(e.TEXTURE_UNIT_FOR_UPDATES),this._descriptor.context.gl.generateMipmap(i.target),this._descriptor.context.bindTexture(r,e.TEXTURE_UNIT_FOR_UPDATES)}clearMipmap(){const e=this._descriptor;if(e.hasMipmap){if(this._wasImmutablyAllocated)throw new t("Cannot delete mipmaps to immutable texture after allocation");e.hasMipmap=!1,this._samplingModeDirty=!0,g(e)}e.samplingMode===p.LINEAR_MIPMAP_NEAREST?(this._samplingModeDirty=!0,e.samplingMode=p.LINEAR):e.samplingMode===p.NEAREST_MIPMAP_NEAREST&&(this._samplingModeDirty=!0,e.samplingMode=p.NEAREST)}setSamplingMode(t){t!==this._descriptor.samplingMode&&(this._descriptor.samplingMode=t,this._samplingModeDirty=!0)}setWrapMode(t){t!==this._descriptor.wrapMode&&(this._descriptor.wrapMode=t,g(this._descriptor),this._wrapModeDirty=!0)}applyChanges(){this._samplingModeDirty&&(this._applySamplingMode(),this._samplingModeDirty=!1),this._wrapModeDirty&&(this._applyWrapMode(),this._wrapModeDirty=!1)}abortCompression(){this.isCompressing&&(this._compressionAbortController.value=i(this._compressionAbortController.value))}_setData(i,r){const s=this._descriptor,a=s.context?.gl;if(!a)return;o(a),this._glName||(this._glName=a.createTexture(),this._glName&&s.context.instanceCounter.increment(n.Texture,this)),g(s);const l=s.context.bindTexture(this,e.TEXTURE_UNIT_FOR_UPDATES);s.context.setActiveTexture(e.TEXTURE_UNIT_FOR_UPDATES),this._configurePixelStorage(),o(a);const p=r??s.target,m=u(p);if(c(i))this._setDataFromTexImageSource(i,p);else{const{width:e,height:r,depth:n}=s;if(null==e||null==r)throw new t("Width and height must be specified!");if(m&&null==n)throw new t("Depth must be specified!");if(s.internalFormat=d(s),s.isImmutable&&!this._wasImmutablyAllocated&&this._texStorage(p,s.internalFormat,s.hasMipmap,e,r,n),_(i)){if(!T(s.internalFormat))throw new t("Attempting to use compressed data with an uncompressed format!");this._setDataFromCompressedSource(i,s.internalFormat,p)}else this._texImage(p,0,s.internalFormat,e,r,n,i),o(a),s.hasMipmap&&this.generateMipmap()}this._applySamplingMode(),this._applyWrapMode(),this._applyAnisotropicFilteringParameters(),o(a),s.context.bindTexture(l,e.TEXTURE_UNIT_FOR_UPDATES)}_setDataCubeMap(t=null){for(let e=a.TEXTURE_CUBE_MAP_POSITIVE_X;e<=a.TEXTURE_CUBE_MAP_NEGATIVE_Z;e++)this._setData(t,e)}_configurePixelStorage(){const t=this._descriptor.context.gl,{unpackAlignment:e,flipped:i,preMultiplyAlpha:r}=this._descriptor;t.pixelStorei(t.UNPACK_ALIGNMENT,e),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,i?1:0),t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,r?1:0)}_setDataFromTexImageSource(t,e){const{gl:i}=this._descriptor.context,r=this._descriptor;r.internalFormat=d(r);const s=u(e),{width:a,height:n,depth:l}=x(t);r.width&&r.height,r.width||(r.width=a),r.height||(r.height=n),s&&r.depth,s&&(r.depth=l),r.isImmutable&&!this._wasImmutablyAllocated&&this._texStorage(e,r.internalFormat,r.hasMipmap,a,n,l),this._texImage(e,0,r.internalFormat,a,n,l,t),o(i),r.hasMipmap&&(this.generateMipmap(),o(i))}_setDataFromCompressedSource(t,e,i){const r=this._descriptor,{width:s,height:o,depth:a}=r,n=t.levels,l=E(i,s,o,a),p=Math.min(l,n.length)-1;this._descriptor.context.gl.texParameteri(r.target,r.context.gl.TEXTURE_MAX_LEVEL,p),this._forEachMipmapLevel(((t,r,s,o)=>{const a=n[Math.min(t,n.length-1)];this._compressedTexImage(i,t,e,r,s,o,a)}),p)}_texStorage(e,i,r,s,o,a){const{gl:n}=this._descriptor.context;if(!M(i))throw new t("Immutable textures must have a sized internal format");if(!this._descriptor.isImmutable)return;const l=r?E(e,s,o,a):1;if(u(e)){if(null==a)throw new t("Missing depth dimension for 3D texture upload");n.texStorage3D(e,l,i,s,o,a)}else n.texStorage2D(e,l,i,s,o);this._wasImmutablyAllocated=!0}_texImage(e,i,r,s,o,a,n){const l=this._descriptor.context.gl,p=u(e),{isImmutable:m,pixelFormat:h,dataType:_}=this._descriptor;if(m){if(null!=n){const r=n;if(p){if(null==a)throw new t("Missing depth dimension for 3D texture upload");l.texSubImage3D(e,i,0,0,0,s,o,a,h,_,r)}else l.texSubImage2D(e,i,0,0,s,o,h,_,r)}}else{const m=n;if(p){if(null==a)throw new t("Missing depth dimension for 3D texture upload");l.texImage3D(e,i,r,s,o,a,0,h,_,m)}else l.texImage2D(e,i,r,s,o,0,h,_,m)}}_compressedTexImage(e,i,r,s,o,a,n){const l=this._descriptor.context.gl,p=u(e);if(this._descriptor.isImmutable){if(null!=n)if(p){if(null==a)throw new t("Missing depth dimension for 3D texture upload");l.compressedTexSubImage3D(e,i,0,0,0,s,o,a,r,n)}else l.compressedTexSubImage2D(e,i,0,0,s,o,r,n)}else if(p){if(null==a)throw new t("Missing depth dimension for 3D texture upload");l.compressedTexImage3D(e,i,r,s,o,a,0,n)}else l.compressedTexImage2D(e,i,r,s,o,0,n)}async _compressOnWorker(t){if(!e.compressionWorkerHandle||!e.compressionWorkerHandle.isCompressible(t))return;const i=this._descriptor.context?.gl.getExtension("WEBGL_compressed_texture_etc"),s=this._descriptor.context?.gl.getExtension("WEBGL_compressed_texture_s3tc");if(i||s){const o=new AbortController;this._compressionAbortController.value=o;const a={data:await createImageBitmap(t),flipped:this.descriptor.flipped,width:t.width,height:t.height,hasMipmap:this._descriptor.hasMipmap,hasETC:!!i,hasS3TC:!!s};e.compressionWorkerHandle.invoke(a,this._compressionAbortController.value.signal).then((t=>{t&&this.isCompressing&&this.glName&&(this._descriptor.internalFormat=t.internalFormat,this._setData(t.compressedTexture)),o===this._compressionAbortController.value&&(this._compressionAbortController.value=null)})).catch((t=>{r(t)||o!==this._compressionAbortController.value||(this._compressionAbortController.value=null)}))}}_forEachMipmapLevel(e,i=1/0){let{width:r,height:s,depth:o,hasMipmap:n,target:l}=this._descriptor;const p=l===a.TEXTURE_3D;if(null==r||null==s||p&&null==o)throw new t("Missing texture dimensions for mipmap calculation");for(let t=0;e(t,r,s,o),n&&(1!==r||1!==s||p&&1!==o)&&!(t>=i);++t)r=Math.max(1,r>>1),s=Math.max(1,s>>1),p&&(o=Math.max(1,o>>1))}_applySamplingMode(){const t=this._descriptor,e=t.context?.gl;let i=t.samplingMode,r=t.samplingMode;i===p.LINEAR_MIPMAP_NEAREST||i===p.LINEAR_MIPMAP_LINEAR?(i=p.LINEAR,t.hasMipmap||(r=p.LINEAR)):i!==p.NEAREST_MIPMAP_NEAREST&&i!==p.NEAREST_MIPMAP_LINEAR||(i=p.NEAREST,t.hasMipmap||(r=p.NEAREST)),e.texParameteri(t.target,e.TEXTURE_MAG_FILTER,i),e.texParameteri(t.target,e.TEXTURE_MIN_FILTER,r)}_applyWrapMode(){const t=this._descriptor,e=t.context?.gl;"number"==typeof t.wrapMode?(e.texParameteri(t.target,e.TEXTURE_WRAP_S,t.wrapMode),e.texParameteri(t.target,e.TEXTURE_WRAP_T,t.wrapMode)):(e.texParameteri(t.target,e.TEXTURE_WRAP_S,t.wrapMode.s),e.texParameteri(t.target,e.TEXTURE_WRAP_T,t.wrapMode.t))}_applyAnisotropicFilteringParameters(){const t=this._descriptor,e=t.context.capabilities.textureFilterAnisotropic;if(!e)return;t.context.gl.texParameterf(t.target,e.TEXTURE_MAX_ANISOTROPY,t.maxAnisotropy??1)}};w.TEXTURE_UNIT_FOR_UPDATES=0,w.compressionWorkerHandle=null;export{w as Texture,I as tracer};