UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

531 lines (528 loc) 23.1 kB
import { PIXELFORMAT_RGBA32U, PIXELFORMAT_RGBA32I, PIXELFORMAT_RGBA16U, PIXELFORMAT_RGBA16I, PIXELFORMAT_RGBA8U, PIXELFORMAT_RGBA8I, PIXELFORMAT_RG32U, PIXELFORMAT_RG32I, PIXELFORMAT_RG16U, PIXELFORMAT_RG16I, PIXELFORMAT_RG8U, PIXELFORMAT_RG8I, PIXELFORMAT_R32U, PIXELFORMAT_R32I, PIXELFORMAT_R16U, PIXELFORMAT_R16I, PIXELFORMAT_R8U, PIXELFORMAT_R8I, PIXELFORMAT_SRGBA8, PIXELFORMAT_SRGB8, PIXELFORMAT_111110F, PIXELFORMAT_DEPTHSTENCIL, PIXELFORMAT_DEPTH16, PIXELFORMAT_DEPTH, PIXELFORMAT_R32F, PIXELFORMAT_RGBA32F, PIXELFORMAT_RGB32F, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGB16F, PIXELFORMAT_RG16F, PIXELFORMAT_R16F, PIXELFORMAT_BC7_SRGBA, PIXELFORMAT_ASTC_4x4_SRGB, PIXELFORMAT_ETC2_SRGBA, PIXELFORMAT_ETC2_SRGB, PIXELFORMAT_DXT5_SRGBA, PIXELFORMAT_DXT3_SRGBA, PIXELFORMAT_DXT1_SRGB, PIXELFORMAT_BC7, PIXELFORMAT_BC6UF, PIXELFORMAT_BC6F, PIXELFORMAT_ATC_RGBA, PIXELFORMAT_ATC_RGB, PIXELFORMAT_ASTC_4x4, PIXELFORMAT_ETC2_RGBA, PIXELFORMAT_ETC2_RGB, PIXELFORMAT_PVRTC_4BPP_RGBA_1, PIXELFORMAT_PVRTC_4BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1, PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_ETC1, PIXELFORMAT_DXT5, PIXELFORMAT_DXT3, PIXELFORMAT_DXT1, PIXELFORMAT_SBGRA8, PIXELFORMAT_BGRA8, PIXELFORMAT_RGBA8, PIXELFORMAT_RGB8, PIXELFORMAT_RGBA4, PIXELFORMAT_RGBA5551, PIXELFORMAT_RGB565, PIXELFORMAT_RG8, PIXELFORMAT_R8, PIXELFORMAT_LA8, PIXELFORMAT_L8, PIXELFORMAT_A8 } from '../constants.js'; function downsampleImage(image, size) { const srcW = image.width; const srcH = image.height; if (srcW > size || srcH > size) { const scale = size / Math.max(srcW, srcH); const dstW = Math.floor(srcW * scale); const dstH = Math.floor(srcH * scale); const canvas = document.createElement('canvas'); canvas.width = dstW; canvas.height = dstH; const context = canvas.getContext('2d'); context.drawImage(image, 0, 0, srcW, srcH, 0, 0, dstW, dstH); return canvas; } return image; } class WebglTexture { constructor(texture){ this._glTexture = null; this.dirtyParameterFlags = 0; this.texture = texture; } destroy(device) { if (this._glTexture) { for(let i = 0; i < device.textureUnits.length; i++){ const textureUnit = device.textureUnits[i]; for(let j = 0; j < textureUnit.length; j++){ if (textureUnit[j] === this._glTexture) { textureUnit[j] = null; } } } device.gl.deleteTexture(this._glTexture); this._glTexture = null; } } loseContext() { this._glTexture = null; } propertyChanged(flag) { this.dirtyParameterFlags |= flag; } initialize(device, texture) { const gl = device.gl; this._glTexture = gl.createTexture(); this._glTarget = texture._cubemap ? gl.TEXTURE_CUBE_MAP : texture._volume ? gl.TEXTURE_3D : texture.array ? gl.TEXTURE_2D_ARRAY : gl.TEXTURE_2D; switch(texture._format){ case PIXELFORMAT_A8: this._glFormat = gl.ALPHA; this._glInternalFormat = gl.ALPHA; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_L8: this._glFormat = gl.LUMINANCE; this._glInternalFormat = gl.LUMINANCE; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_LA8: this._glFormat = gl.LUMINANCE_ALPHA; this._glInternalFormat = gl.LUMINANCE_ALPHA; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_R8: this._glFormat = gl.RED; this._glInternalFormat = gl.R8; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_RG8: this._glFormat = gl.RG; this._glInternalFormat = gl.RG8; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_RGB565: this._glFormat = gl.RGB; this._glInternalFormat = gl.RGB565; this._glPixelType = gl.UNSIGNED_SHORT_5_6_5; break; case PIXELFORMAT_RGBA5551: this._glFormat = gl.RGBA; this._glInternalFormat = gl.RGB5_A1; this._glPixelType = gl.UNSIGNED_SHORT_5_5_5_1; break; case PIXELFORMAT_RGBA4: this._glFormat = gl.RGBA; this._glInternalFormat = gl.RGBA4; this._glPixelType = gl.UNSIGNED_SHORT_4_4_4_4; break; case PIXELFORMAT_RGB8: this._glFormat = gl.RGB; this._glInternalFormat = gl.RGB8; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_RGBA8: this._glFormat = gl.RGBA; this._glInternalFormat = gl.RGBA8; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_BGRA8: case PIXELFORMAT_SBGRA8: break; case PIXELFORMAT_DXT1: this._glFormat = gl.RGB; this._glInternalFormat = device.extCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT; break; case PIXELFORMAT_DXT3: this._glFormat = gl.RGBA; this._glInternalFormat = device.extCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT; break; case PIXELFORMAT_DXT5: this._glFormat = gl.RGBA; this._glInternalFormat = device.extCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT; break; case PIXELFORMAT_ETC1: this._glFormat = gl.RGB; this._glInternalFormat = device.extCompressedTextureETC1.COMPRESSED_RGB_ETC1_WEBGL; break; case PIXELFORMAT_PVRTC_2BPP_RGB_1: this._glFormat = gl.RGB; this._glInternalFormat = device.extCompressedTexturePVRTC.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; break; case PIXELFORMAT_PVRTC_2BPP_RGBA_1: this._glFormat = gl.RGBA; this._glInternalFormat = device.extCompressedTexturePVRTC.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; break; case PIXELFORMAT_PVRTC_4BPP_RGB_1: this._glFormat = gl.RGB; this._glInternalFormat = device.extCompressedTexturePVRTC.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; case PIXELFORMAT_PVRTC_4BPP_RGBA_1: this._glFormat = gl.RGBA; this._glInternalFormat = device.extCompressedTexturePVRTC.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; case PIXELFORMAT_ETC2_RGB: this._glFormat = gl.RGB; this._glInternalFormat = device.extCompressedTextureETC.COMPRESSED_RGB8_ETC2; break; case PIXELFORMAT_ETC2_RGBA: this._glFormat = gl.RGBA; this._glInternalFormat = device.extCompressedTextureETC.COMPRESSED_RGBA8_ETC2_EAC; break; case PIXELFORMAT_ASTC_4x4: this._glFormat = gl.RGBA; this._glInternalFormat = device.extCompressedTextureASTC.COMPRESSED_RGBA_ASTC_4x4_KHR; break; case PIXELFORMAT_ATC_RGB: this._glFormat = gl.RGB; this._glInternalFormat = device.extCompressedTextureATC.COMPRESSED_RGB_ATC_WEBGL; break; case PIXELFORMAT_ATC_RGBA: this._glFormat = gl.RGBA; this._glInternalFormat = device.extCompressedTextureATC.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL; break; case PIXELFORMAT_BC6F: this._glFormat = gl.RGB; this._glInternalFormat = device.extTextureCompressionBPTC.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT; break; case PIXELFORMAT_BC6UF: this._glFormat = gl.RGB; this._glInternalFormat = device.extTextureCompressionBPTC.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT; break; case PIXELFORMAT_BC7: this._glFormat = gl.RGBA; this._glInternalFormat = device.extTextureCompressionBPTC.COMPRESSED_RGBA_BPTC_UNORM_EXT; break; case PIXELFORMAT_DXT1_SRGB: this._glFormat = gl.SRGB; this._glInternalFormat = device.extCompressedTextureS3TC_SRGB.COMPRESSED_SRGB_S3TC_DXT1_EXT; break; case PIXELFORMAT_DXT3_SRGBA: this._glFormat = gl.SRGB_ALPHA; this._glInternalFormat = device.extCompressedTextureS3TC_SRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; break; case PIXELFORMAT_DXT5_SRGBA: this._glFormat = gl.SRGB_ALPHA; this._glInternalFormat = device.extCompressedTextureS3TC_SRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; break; case PIXELFORMAT_ETC2_SRGB: this._glFormat = gl.SRGB; this._glInternalFormat = device.extCompressedTextureETC.COMPRESSED_SRGB8_ETC2; break; case PIXELFORMAT_ETC2_SRGBA: this._glFormat = gl.SRGB_ALPHA; this._glInternalFormat = device.extCompressedTextureETC.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; break; case PIXELFORMAT_ASTC_4x4_SRGB: this._glFormat = gl.SRGB_ALPHA; this._glInternalFormat = device.extCompressedTextureASTC.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; break; case PIXELFORMAT_BC7_SRGBA: this._glFormat = gl.RGBA; this._glInternalFormat = device.extTextureCompressionBPTC.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT; break; case PIXELFORMAT_R16F: this._glFormat = gl.RED; this._glInternalFormat = gl.R16F; this._glPixelType = gl.HALF_FLOAT; break; case PIXELFORMAT_RG16F: this._glFormat = gl.RG; this._glInternalFormat = gl.RG16F; this._glPixelType = gl.HALF_FLOAT; break; case PIXELFORMAT_RGB16F: this._glFormat = gl.RGB; this._glInternalFormat = gl.RGB16F; this._glPixelType = gl.HALF_FLOAT; break; case PIXELFORMAT_RGBA16F: this._glFormat = gl.RGBA; this._glInternalFormat = gl.RGBA16F; this._glPixelType = gl.HALF_FLOAT; break; case PIXELFORMAT_RGB32F: this._glFormat = gl.RGB; this._glInternalFormat = gl.RGB32F; this._glPixelType = gl.FLOAT; break; case PIXELFORMAT_RGBA32F: this._glFormat = gl.RGBA; this._glInternalFormat = gl.RGBA32F; this._glPixelType = gl.FLOAT; break; case PIXELFORMAT_R32F: this._glFormat = gl.RED; this._glInternalFormat = gl.R32F; this._glPixelType = gl.FLOAT; break; case PIXELFORMAT_DEPTH: this._glFormat = gl.DEPTH_COMPONENT; this._glInternalFormat = gl.DEPTH_COMPONENT32F; this._glPixelType = gl.FLOAT; break; case PIXELFORMAT_DEPTH16: this._glFormat = gl.DEPTH_COMPONENT; this._glInternalFormat = gl.DEPTH_COMPONENT16; this._glPixelType = gl.UNSIGNED_SHORT; break; case PIXELFORMAT_DEPTHSTENCIL: this._glFormat = gl.DEPTH_STENCIL; this._glInternalFormat = gl.DEPTH24_STENCIL8; this._glPixelType = gl.UNSIGNED_INT_24_8; break; case PIXELFORMAT_111110F: this._glFormat = gl.RGB; this._glInternalFormat = gl.R11F_G11F_B10F; this._glPixelType = gl.UNSIGNED_INT_10F_11F_11F_REV; break; case PIXELFORMAT_SRGB8: this._glFormat = gl.RGB; this._glInternalFormat = gl.SRGB8; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_SRGBA8: this._glFormat = gl.RGBA; this._glInternalFormat = gl.SRGB8_ALPHA8; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_R8I: this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R8I; this._glPixelType = gl.BYTE; break; case PIXELFORMAT_R8U: this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R8UI; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_R16I: this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R16I; this._glPixelType = gl.SHORT; break; case PIXELFORMAT_R16U: this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R16UI; this._glPixelType = gl.UNSIGNED_SHORT; break; case PIXELFORMAT_R32I: this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R32I; this._glPixelType = gl.INT; break; case PIXELFORMAT_R32U: this._glFormat = gl.RED_INTEGER; this._glInternalFormat = gl.R32UI; this._glPixelType = gl.UNSIGNED_INT; break; case PIXELFORMAT_RG8I: this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG8I; this._glPixelType = gl.BYTE; break; case PIXELFORMAT_RG8U: this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG8UI; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_RG16I: this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG16I; this._glPixelType = gl.SHORT; break; case PIXELFORMAT_RG16U: this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG16UI; this._glPixelType = gl.UNSIGNED_SHORT; break; case PIXELFORMAT_RG32I: this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG32I; this._glPixelType = gl.INT; break; case PIXELFORMAT_RG32U: this._glFormat = gl.RG_INTEGER; this._glInternalFormat = gl.RG32UI; this._glPixelType = gl.UNSIGNED_INT; break; case PIXELFORMAT_RGBA8I: this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA8I; this._glPixelType = gl.BYTE; break; case PIXELFORMAT_RGBA8U: this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA8UI; this._glPixelType = gl.UNSIGNED_BYTE; break; case PIXELFORMAT_RGBA16I: this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA16I; this._glPixelType = gl.SHORT; break; case PIXELFORMAT_RGBA16U: this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA16UI; this._glPixelType = gl.UNSIGNED_SHORT; break; case PIXELFORMAT_RGBA32I: this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA32I; this._glPixelType = gl.INT; break; case PIXELFORMAT_RGBA32U: this._glFormat = gl.RGBA_INTEGER; this._glInternalFormat = gl.RGBA32UI; this._glPixelType = gl.UNSIGNED_INT; break; } this._glCreated = false; } upload(device, texture) { const gl = device.gl; if (!texture._needsUpload && (texture._needsMipmapsUpload && texture._mipmapsUploaded || !texture.pot)) { return; } let mipLevel = 0; let mipObject; let resMult; const requiredMipLevels = texture.numLevels; if (texture.array) { gl.texStorage3D(gl.TEXTURE_2D_ARRAY, requiredMipLevels, this._glInternalFormat, texture._width, texture._height, texture._arrayLength); } while(texture._levels[mipLevel] || mipLevel === 0){ if (!texture._needsUpload && mipLevel === 0) { mipLevel++; continue; } else if (mipLevel && (!texture._needsMipmapsUpload || !texture._mipmaps)) { break; } mipObject = texture._levels[mipLevel]; resMult = 1 / Math.pow(2, mipLevel); if (mipLevel === 1 && !texture._compressed && !texture._integerFormat && texture._levels.length < requiredMipLevels) { gl.generateMipmap(this._glTarget); texture._mipmapsUploaded = true; } if (texture._cubemap) { let face; if (device._isBrowserInterface(mipObject[0])) { for(face = 0; face < 6; face++){ if (!texture._levelsUpdated[0][face]) { continue; } let src = mipObject[face]; if (device._isImageBrowserInterface(src)) { if (src.width > device.maxCubeMapSize || src.height > device.maxCubeMapSize) { src = downsampleImage(src, device.maxCubeMapSize); if (mipLevel === 0) { texture._width = src.width; texture._height = src.height; } } } device.setUnpackFlipY(false); device.setUnpackPremultiplyAlpha(texture._premultiplyAlpha); if (this._glCreated) { gl.texSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, mipLevel, 0, 0, this._glFormat, this._glPixelType, src); } else { gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, mipLevel, this._glInternalFormat, this._glFormat, this._glPixelType, src); } } } else { resMult = 1 / Math.pow(2, mipLevel); for(face = 0; face < 6; face++){ if (!texture._levelsUpdated[0][face]) { continue; } const texData = mipObject[face]; if (texture._compressed) { if (this._glCreated && texData) { gl.compressedTexSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, mipLevel, 0, 0, Math.max(texture._width * resMult, 1), Math.max(texture._height * resMult, 1), this._glInternalFormat, texData); } else { gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, mipLevel, this._glInternalFormat, Math.max(texture._width * resMult, 1), Math.max(texture._height * resMult, 1), 0, texData); } } else { device.setUnpackFlipY(false); device.setUnpackPremultiplyAlpha(texture._premultiplyAlpha); if (this._glCreated && texData) { gl.texSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, mipLevel, 0, 0, Math.max(texture._width * resMult, 1), Math.max(texture._height * resMult, 1), this._glFormat, this._glPixelType, texData); } else { gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, mipLevel, this._glInternalFormat, Math.max(texture._width * resMult, 1), Math.max(texture._height * resMult, 1), 0, this._glFormat, this._glPixelType, texData); } } } } } else if (texture._volume) { if (texture._compressed) { gl.compressedTexImage3D(gl.TEXTURE_3D, mipLevel, this._glInternalFormat, Math.max(texture._width * resMult, 1), Math.max(texture._height * resMult, 1), Math.max(texture._depth * resMult, 1), 0, mipObject); } else { device.setUnpackFlipY(false); device.setUnpackPremultiplyAlpha(texture._premultiplyAlpha); gl.texImage3D(gl.TEXTURE_3D, mipLevel, this._glInternalFormat, Math.max(texture._width * resMult, 1), Math.max(texture._height * resMult, 1), Math.max(texture._depth * resMult, 1), 0, this._glFormat, this._glPixelType, mipObject); } } else if (texture.array && typeof mipObject === 'object') { if (texture._arrayLength === mipObject.length) { if (texture._compressed) { for(let index = 0; index < texture._arrayLength; index++){ gl.compressedTexSubImage3D(gl.TEXTURE_2D_ARRAY, mipLevel, 0, 0, index, Math.max(Math.floor(texture._width * resMult), 1), Math.max(Math.floor(texture._height * resMult), 1), 1, this._glInternalFormat, mipObject[index]); } } else { for(let index = 0; index < texture._arrayLength; index++){ gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, mipLevel, 0, 0, index, Math.max(Math.floor(texture._width * resMult), 1), Math.max(Math.floor(texture._height * resMult), 1), 1, this._glFormat, this._glPixelType, mipObject[index]); } } } } else { if (device._isBrowserInterface(mipObject)) { if (device._isImageBrowserInterface(mipObject)) { if (mipObject.width > device.maxTextureSize || mipObject.height > device.maxTextureSize) { mipObject = downsampleImage(mipObject, device.maxTextureSize); if (mipLevel === 0) { texture._width = mipObject.width; texture._height = mipObject.height; } } } const w = mipObject.width || mipObject.videoWidth; const h = mipObject.height || mipObject.videoHeight; device.setUnpackFlipY(texture._flipY); device.setUnpackPremultiplyAlpha(texture._premultiplyAlpha); if (this._glCreated && texture._width === w && texture._height === h && !device._isImageVideoInterface(mipObject)) { gl.texSubImage2D(gl.TEXTURE_2D, mipLevel, 0, 0, this._glFormat, this._glPixelType, mipObject); } else { gl.texImage2D(gl.TEXTURE_2D, mipLevel, this._glInternalFormat, this._glFormat, this._glPixelType, mipObject); if (mipLevel === 0) { texture._width = w; texture._height = h; } } } else { resMult = 1 / Math.pow(2, mipLevel); if (texture._compressed) { if (this._glCreated && mipObject) { gl.compressedTexSubImage2D(gl.TEXTURE_2D, mipLevel, 0, 0, Math.max(Math.floor(texture._width * resMult), 1), Math.max(Math.floor(texture._height * resMult), 1), this._glInternalFormat, mipObject); } else { gl.compressedTexImage2D(gl.TEXTURE_2D, mipLevel, this._glInternalFormat, Math.max(Math.floor(texture._width * resMult), 1), Math.max(Math.floor(texture._height * resMult), 1), 0, mipObject); } } else { device.setUnpackFlipY(false); device.setUnpackPremultiplyAlpha(texture._premultiplyAlpha); if (this._glCreated && mipObject) { gl.texSubImage2D(gl.TEXTURE_2D, mipLevel, 0, 0, Math.max(texture._width * resMult, 1), Math.max(texture._height * resMult, 1), this._glFormat, this._glPixelType, mipObject); } else { gl.texImage2D(gl.TEXTURE_2D, mipLevel, this._glInternalFormat, Math.max(texture._width * resMult, 1), Math.max(texture._height * resMult, 1), 0, this._glFormat, this._glPixelType, mipObject); } } } if (mipLevel === 0) { texture._mipmapsUploaded = false; } else { texture._mipmapsUploaded = true; } } mipLevel++; } if (texture._needsUpload) { if (texture._cubemap) { for(let i = 0; i < 6; i++){ texture._levelsUpdated[0][i] = false; } } else { texture._levelsUpdated[0] = false; } } if (!texture._compressed && !texture._integerFormat && texture._mipmaps && texture._needsMipmapsUpload && texture._levels.length === 1) { gl.generateMipmap(this._glTarget); texture._mipmapsUploaded = true; } if (texture._gpuSize) { texture.adjustVramSizeTracking(device._vram, -texture._gpuSize); } texture._gpuSize = texture.gpuSize; texture.adjustVramSizeTracking(device._vram, texture._gpuSize); this._glCreated = true; } read(x, y, width, height, options) { const texture = this.texture; const device = texture.device; return device.readTextureAsync(texture, x, y, width, height, options); } write(x, y, width, height, data) { const { texture } = this; const { device } = texture; device.setTexture(texture, 0); return device.writeTextureAsync(texture, x, y, width, height, data); } } export { WebglTexture };