From 48f76d434e199a6d89e22b43932a66fa6b539167 Mon Sep 17 00:00:00 2001 From: krapnik Date: Fri, 11 Jul 2025 14:29:26 +0800 Subject: [PATCH 1/4] Support for compressed texture formats --- src/textures.js | 143 +++++++++++++++++++++++++++++++++++++++++++++++- src/twgl.js | 2 + 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/textures.js b/src/textures.js index ec438357..8321d2f7 100644 --- a/src/textures.js +++ b/src/textures.js @@ -216,6 +216,74 @@ const RED_INTEGER = 0x8D94; const RGB_INTEGER = 0x8D98; const RGBA_INTEGER = 0x8D99; +/* Compressed Texture Formats */ +// s3tc +const COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F1; +const COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F2; +const COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F3; +const COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F4; +// s3tc_srgb +const COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C; +const COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D; +const COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E; +const COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F; +// etc +const COMPRESSED_RGB_ETC1_WEBGL = 0x8D64; +const COMPRESSED_R11_EAC = 0x9270; +const COMPRESSED_SIGNED_R11_EAC = 0x9271; +const COMPRESSED_RG11_EAC = 0x9272; +const COMPRESSED_SIGNED_RG11_EAC = 0x9273; +const COMPRESSED_RGB8_ETC2 = 0x9274; +const COMPRESSED_SRGB8_ETC2 = 0x9275; +const COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276; +const COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277; +const COMPRESSED_RGBA8_ETC2_EAC = 0x9278; +const COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279; +// pvrtc +const COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; +const COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01; +const COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; +const COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03; +// astc +const COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0; +const COMPRESSED_RGBA_ASTC_5x4_KHR = 0x93B1; +const COMPRESSED_RGBA_ASTC_5x5_KHR = 0x93B2; +const COMPRESSED_RGBA_ASTC_6x5_KHR = 0x93B3; +const COMPRESSED_RGBA_ASTC_6x6_KHR = 0x93B4; +const COMPRESSED_RGBA_ASTC_8x5_KHR = 0x93B5; +const COMPRESSED_RGBA_ASTC_8x6_KHR = 0x93B6; +const COMPRESSED_RGBA_ASTC_8x8_KHR = 0x93B7; +const COMPRESSED_RGBA_ASTC_10x5_KHR = 0x93B8; +const COMPRESSED_RGBA_ASTC_10x6_KHR = 0x93B9; +const COMPRESSED_RGBA_ASTC_10x8_KHR = 0x93BA; +const COMPRESSED_RGBA_ASTC_10x10_KHR = 0x93BB; +const COMPRESSED_RGBA_ASTC_12x10_KHR = 0x93BC; +const COMPRESSED_RGBA_ASTC_12x12_KHR = 0x93BD; +const COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR = 0x93D0; +const COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR = 0x93D1; +const COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR = 0x93D2; +const COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR = 0x93D3; +const COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR = 0x93D4; +const COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR = 0x93D5; +const COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR = 0x93D6; +const COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR = 0x93D7; +const COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR = 0x93D8; +const COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR = 0x93D9; +const COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR = 0x93DA; +const COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR = 0x93DB; +const COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR = 0x93DC; +const COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR = 0x93DD; +// bptc +const COMPRESSED_RGBA_BPTC_UNORM_EXT = 0x8E8C; +const COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT = 0x8E8D; +const COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT = 0x8E8E; +const COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT = 0x8E8F; +// rgtc +const COMPRESSED_RED_RGTC1_EXT = 0x8DBB; +const COMPRESSED_SIGNED_RED_RGTC1_EXT = 0x8DBC; +const COMPRESSED_RED_GREEN_RGTC2_EXT = 0x8DBD; +const COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT = 0x8DBE; + const formatInfo = {}; { // NOTE: this is named `numColorComponents` vs `numComponents` so we can let Uglify mangle @@ -318,6 +386,73 @@ function getTextureInternalFormatInfo(internalFormat) { t[DEPTH24_STENCIL8] = { textureFormat: DEPTH_STENCIL, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [UNSIGNED_INT_24_8], }; t[DEPTH32F_STENCIL8] = { textureFormat: DEPTH_STENCIL, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [FLOAT_32_UNSIGNED_INT_24_8_REV], }; + // compressed texture formats + // s3tc:https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc.txt + t[COMPRESSED_RGB_S3TC_DXT1_EXT] = { textureFormat : COMPRESSED_RGB_S3TC_DXT1_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_S3TC_DXT1_EXT] = { textureFormat : COMPRESSED_RGBA_S3TC_DXT1_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_S3TC_DXT3_EXT] = { textureFormat : COMPRESSED_RGBA_S3TC_DXT3_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_S3TC_DXT5_EXT] = { textureFormat : COMPRESSED_RGBA_S3TC_DXT5_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc_srgb.txt + t[COMPRESSED_SRGB_S3TC_DXT1_EXT] = { textureFormat : COMPRESSED_SRGB_S3TC_DXT1_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT] = { textureFormat : COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT] = { textureFormat : COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT] = { textureFormat : COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + // https://registry.khronos.org/OpenGL/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt + t[COMPRESSED_RGB_ETC1_WEBGL] = { textureFormat : COMPRESSED_RGB_ETC1_WEBGL, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_R11_EAC] = { textureFormat : COMPRESSED_R11_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SIGNED_R11_EAC] = { textureFormat : COMPRESSED_SIGNED_R11_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [BYTE], }; + t[COMPRESSED_RG11_EAC] = { textureFormat : COMPRESSED_RG11_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SIGNED_RG11_EAC] = { textureFormat : COMPRESSED_SIGNED_RG11_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [SHORT], }; + t[COMPRESSED_RGB8_ETC2] = { textureFormat : COMPRESSED_RGB8_ETC2, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ETC2] = { textureFormat : COMPRESSED_SRGB8_ETC2, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2] = { textureFormat : COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2] = { textureFormat: COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA8_ETC2_EAC] = { textureFormat : COMPRESSED_RGBA8_ETC2_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ETC2_EAC] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + // https://registry.khronos.org/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + t[COMPRESSED_RGB_PVRTC_4BPPV1_IMG] = { textureFormat : COMPRESSED_RGB_PVRTC_4BPPV1_IMG, colorRenderable : false, textureFilterable : false, bytesPerElement : [4], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGB_PVRTC_2BPPV1_IMG] = { textureFormat : COMPRESSED_RGB_PVRTC_2BPPV1_IMG, colorRenderable : false, textureFilterable : false, bytesPerElement : [2], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_PVRTC_4BPPV1_IMG] = { textureFormat : COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, colorRenderable : false, textureFilterable : false, bytesPerElement : [4], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_PVRTC_2BPPV1_IMG] = { textureFormat : COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, colorRenderable : false, textureFilterable : false, bytesPerElement : [2], type : [UNSIGNED_BYTE], }; + // https://registry.khronos.org/OpenGL/extensions/KHR/KHR_texture_compression_astc_hdr.txt + t[COMPRESSED_RGBA_ASTC_4x4_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_4x4_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_5x4_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_5x4_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_5x5_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_5x5_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_6x5_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_6x5_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_6x6_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_6x6_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_8x5_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_8x5_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_8x6_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_8x6_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_8x8_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_8x8_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_10x5_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_10x5_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_10x6_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_10x6_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_10x8_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_10x8_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_10x10_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_10x10_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_12x10_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_12x10_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA_ASTC_12x12_KHR] = { textureFormat : COMPRESSED_RGBA_ASTC_12x12_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_bptc.txt + t[COMPRESSED_RGBA_BPTC_UNORM_EXT] = { textureFormat : COMPRESSED_RGBA_BPTC_UNORM_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT] = { textureFormat : COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT] = { textureFormat : COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [4], type : [FLOAT], }; + t[COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT] = { textureFormat : COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [4], type : [FLOAT], }; + // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_rgtc.txt + t[COMPRESSED_RED_RGTC1_EXT] = { textureFormat : COMPRESSED_RED_RGTC1_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SIGNED_RED_RGTC1_EXT] = { textureFormat : COMPRESSED_SIGNED_RED_RGTC1_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RED_GREEN_RGTC2_EXT] = { textureFormat : COMPRESSED_RED_GREEN_RGTC2_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT] = { textureFormat : COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; Object.keys(t).forEach(function(internalFormat) { const info = t[internalFormat]; info.bytesPerElementMap = {}; @@ -606,6 +741,7 @@ function setDefaults(newDefaults) { * @property {string} [crossOrigin] What to set the crossOrigin property of images when they are downloaded. * default: undefined. Also see {@link module:twgl.setDefaults}. * + * @property {boolean} [compressed] If set to `true`, compressedTexImage2D will be used to process the texture. Defaults to `false`. * @memberOf module:twgl */ @@ -1522,6 +1658,7 @@ function setTextureFromArray(gl, tex, src, options) { const formatType = getFormatAndTypeForInternalFormat(internalFormat); const format = options.format || formatType.format; const type = options.type || getTextureTypeForArrayType(gl, src, formatType.type); + const compressed = options.compressed; if (!isArrayBuffer(src)) { const Type = typedArrays.getTypedArrayTypeForGLType(type); src = new Type(src); @@ -1577,7 +1714,11 @@ function setTextureFromArray(gl, tex, src, options) { } else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, src); } else { - gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, src); + if( compressed ){ + gl.compressedTexImage2D(target, level, internalFormat, width, height, 0, src); + } else { + gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, src); + } } }); return { diff --git a/src/twgl.js b/src/twgl.js index 95674c5e..49a3aab0 100644 --- a/src/twgl.js +++ b/src/twgl.js @@ -204,6 +204,8 @@ const supportedExtensions = [ 'EXT_frag_depth', 'EXT_sRGB', 'EXT_shader_texture_lod', + 'EXT_texture_compression_bptc', + 'EXT_texture_compression_rgtc', 'EXT_texture_filter_anisotropic', 'OES_element_index_uint', 'OES_standard_derivatives', From f5c1c9442f4aefdd75579f6b4722d3dda86a0e7d Mon Sep 17 00:00:00 2001 From: krapnik Date: Fri, 11 Jul 2025 14:35:13 +0800 Subject: [PATCH 2/4] fix typo --- src/textures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textures.js b/src/textures.js index 8321d2f7..d765f3c7 100644 --- a/src/textures.js +++ b/src/textures.js @@ -1714,7 +1714,7 @@ function setTextureFromArray(gl, tex, src, options) { } else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, src); } else { - if( compressed ){ + if ( compressed ){ gl.compressedTexImage2D(target, level, internalFormat, width, height, 0, src); } else { gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, src); From e5bf81d26c1af59007a1630156dc259d4d6c648f Mon Sep 17 00:00:00 2001 From: krapnik Date: Sat, 12 Jul 2025 04:24:03 +0800 Subject: [PATCH 3/4] - update webgl constants - Optimize the calling parameters of `compressedTexImage2D` - add `test compressed texture format` --- src/textures.js | 75 +++++++++++++++++++++++++++++-------- test/tests/texture-tests.js | 62 ++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 16 deletions(-) diff --git a/src/textures.js b/src/textures.js index d765f3c7..63d3a5b1 100644 --- a/src/textures.js +++ b/src/textures.js @@ -218,10 +218,10 @@ const RGBA_INTEGER = 0x8D99; /* Compressed Texture Formats */ // s3tc -const COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F1; -const COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F2; -const COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F3; -const COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F4; +const COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; +const COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; +const COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; +const COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; // s3tc_srgb const COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C; const COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D; @@ -398,17 +398,17 @@ function getTextureInternalFormatInfo(internalFormat) { t[COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT] = { textureFormat : COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; t[COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT] = { textureFormat : COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; // https://registry.khronos.org/OpenGL/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt - t[COMPRESSED_RGB_ETC1_WEBGL] = { textureFormat : COMPRESSED_RGB_ETC1_WEBGL, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; - t[COMPRESSED_R11_EAC] = { textureFormat : COMPRESSED_R11_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; - t[COMPRESSED_SIGNED_R11_EAC] = { textureFormat : COMPRESSED_SIGNED_R11_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [BYTE], }; - t[COMPRESSED_RG11_EAC] = { textureFormat : COMPRESSED_RG11_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; - t[COMPRESSED_SIGNED_RG11_EAC] = { textureFormat : COMPRESSED_SIGNED_RG11_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [SHORT], }; - t[COMPRESSED_RGB8_ETC2] = { textureFormat : COMPRESSED_RGB8_ETC2, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; - t[COMPRESSED_SRGB8_ETC2] = { textureFormat : COMPRESSED_SRGB8_ETC2, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; - t[COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2] = { textureFormat : COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; - t[COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2] = { textureFormat: COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, colorRenderable : false, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; - t[COMPRESSED_RGBA8_ETC2_EAC] = { textureFormat : COMPRESSED_RGBA8_ETC2_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; - t[COMPRESSED_SRGB8_ALPHA8_ETC2_EAC] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, colorRenderable : false, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGB_ETC1_WEBGL] = { textureFormat : COMPRESSED_RGB_ETC1_WEBGL, colorRenderable : true, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_R11_EAC] = { textureFormat : COMPRESSED_R11_EAC, colorRenderable : true, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SIGNED_R11_EAC] = { textureFormat : COMPRESSED_SIGNED_R11_EAC, colorRenderable : true, textureFilterable : false, bytesPerElement : [8], type : [BYTE], }; + t[COMPRESSED_RG11_EAC] = { textureFormat : COMPRESSED_RG11_EAC, colorRenderable : true, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SIGNED_RG11_EAC] = { textureFormat : COMPRESSED_SIGNED_RG11_EAC, colorRenderable : true, textureFilterable : false, bytesPerElement : [16], type : [SHORT], }; + t[COMPRESSED_RGB8_ETC2] = { textureFormat : COMPRESSED_RGB8_ETC2, colorRenderable : true, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ETC2] = { textureFormat : COMPRESSED_SRGB8_ETC2, colorRenderable : true, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2] = { textureFormat : COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, colorRenderable : true, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2] = { textureFormat: COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, colorRenderable : true, textureFilterable : false, bytesPerElement : [8], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_RGBA8_ETC2_EAC] = { textureFormat : COMPRESSED_RGBA8_ETC2_EAC, colorRenderable : true, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; + t[COMPRESSED_SRGB8_ALPHA8_ETC2_EAC] = { textureFormat : COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, colorRenderable : true, textureFilterable : false, bytesPerElement : [16], type : [UNSIGNED_BYTE], }; // https://registry.khronos.org/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt t[COMPRESSED_RGB_PVRTC_4BPPV1_IMG] = { textureFormat : COMPRESSED_RGB_PVRTC_4BPPV1_IMG, colorRenderable : false, textureFilterable : false, bytesPerElement : [4], type : [UNSIGNED_BYTE], }; t[COMPRESSED_RGB_PVRTC_2BPPV1_IMG] = { textureFormat : COMPRESSED_RGB_PVRTC_2BPPV1_IMG, colorRenderable : false, textureFilterable : false, bytesPerElement : [2], type : [UNSIGNED_BYTE], }; @@ -1671,6 +1671,9 @@ function setTextureFromArray(gl, tex, src, options) { if (numElements % 1) { throw "length wrong size for format: " + utils.glEnumToString(gl, format); } + if (compressed && !width && !height) { + throw "compressed texture needs to set width and height!"; + } let dimensions; if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { if (!width && !height && !depth) { @@ -1715,7 +1718,24 @@ function setTextureFromArray(gl, tex, src, options) { gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, src); } else { if ( compressed ){ - gl.compressedTexImage2D(target, level, internalFormat, width, height, 0, src); + if ( level === 0 ){ + gl.compressedTexImage2D(target, level, internalFormat, width, height, 0, src); + } else { + let offset = false; + for (let i = 0; i < level; ++i) { + // Determine how big this level of compressed texture data is in bytes. + const levelSize = textureLevelSize(internalFormat, width, height); + // Get a view of the bytes for this level of DXT data. + const dxtLevel = new Uint8Array(src.buffer, src.byteOffset + offset, levelSize); + // Upload! + gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, dxtLevel); + // The next mip level will be half the height and width of this one. + width = width >> 1; + height = height >> 1; + // Advance the offset into the compressed texture data past the current mip level's data. + offset += levelSize; + } + } } else { gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, src); } @@ -2091,6 +2111,29 @@ function createTexturesAsync(gl, options) { }); } +function textureLevelSize(format, width, height) { + switch (format) { + case COMPRESSED_RGB_S3TC_DXT1_EXT: + case COMPRESSED_RGB_ETC1_WEBGL: + return ((width + 3) >> 2) * ((height + 3) >> 2) * 8; + + case COMPRESSED_RGBA_S3TC_DXT3_EXT: + case COMPRESSED_RGBA_S3TC_DXT5_EXT: + return ((width + 3) >> 2) * ((height + 3) >> 2) * 16; + + case COMPRESSED_RGB_PVRTC_4BPPV1_IMG: + case COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: + return Math.floor((Math.max(width, 8) * Math.max(height, 8) * 4 + 7) / 8); + + case COMPRESSED_RGB_PVRTC_2BPPV1_IMG: + case COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: + return Math.floor((Math.max(width, 16) * Math.max(height, 8) * 2 + 7) / 8); + + default: + return 0; + } +} + export { setDefaults as setTextureDefaults_, diff --git a/test/tests/texture-tests.js b/test/tests/texture-tests.js index 4e790bd8..0262ade9 100644 --- a/test/tests/texture-tests.js +++ b/test/tests/texture-tests.js @@ -8,7 +8,10 @@ import {describe} from '../mocha-support.js'; import { assertNoWebGLError, createContext, + createContext2, itWebGL, + itWebGL2, + checkColor } from '../webgl.js'; function assertPixelFromTexture(gl, texture, expected) { @@ -72,4 +75,63 @@ describe('texture tests', () => { assertNoWebGLError(gl); }); + itWebGL2(`test compressed texture format`, async() => { + const {gl} = createContext2(); + const vs = `#version 300 es + in vec2 position; + in vec2 texcoord; + out vec2 v_texcoord; + void main() { + gl_Position = vec4(position, 0, 1); + v_texcoord = texcoord; + }`; + + const fs = `#version 300 es + precision mediump float; + in vec2 v_texcoord; + uniform sampler2D u_texture; + out vec4 fragColor; + void main() { + fragColor = texture(u_texture, v_texcoord); + } + `; + const programInfo = twgl.createProgramInfo(gl, [vs, fs]); + + const arrays = { + position: [1, 1, 0.0, 1, -1, 0.0, -1, -1, 0.0, -1, 1, 0.0], + texcoord: [1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0], + indices: [0, 1, 3, 1, 2, 3], + }; + + const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays); + twgl.addExtensionsToContext(gl); + const green = [2, 255, 2, 255]; + // KTX2:BASIS_FORMAT.cTFBC7 + const green_4x4 = new Uint8Array([32, 128, 193, 255, 15, 24, 252, 255, 175, 170, 170, 170, 0, 0, 0, 0]); + const width = 4; + const height = 4; + let internalFormat = 0x8E8C; //COMPRESSED_RGBA_BPTC_UNORM + gl.canvas.width = width; + gl.canvas.height = height; + let texture = twgl.createTexture(gl, { src: green_4x4, width, height, internalFormat, compressed: true }); + gl.useProgram(programInfo.program); + twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); + twgl.setUniforms(programInfo, { u_texture: texture }); + twgl.drawBufferInfo(gl, bufferInfo, gl.TRIANGLES); + checkColor(gl, green); + + const red = [255, 0, 0, 255]; + // DDS:COMPRESSED_RGB_S3TC_DXT1_EXT + const red_4x4 = new Uint8Array([0, 248, 0, 248, 0, 0, 0, 0, 0, 248, 0, 248, 0, 0, 0, 0, 0, 248, 0, 248, 0, 0, 0, 0]); + internalFormat = 0x83F0; //COMPRESSED_RGB_S3TC_DXT1_EXT + texture = twgl.createTexture(gl, { src: red_4x4, width, height, internalFormat, compressed: true, level:3 }); + gl.useProgram(programInfo.program); + twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); + twgl.setUniforms(programInfo, { u_texture: texture }); + twgl.drawBufferInfo(gl, bufferInfo, gl.TRIANGLES); + checkColor(gl, red); + + assertNoWebGLError(gl); + }); + }); \ No newline at end of file From 3a4c8809fbe351bb786fc828c1fe810d282f0079 Mon Sep 17 00:00:00 2001 From: krapnik Date: Sat, 12 Jul 2025 04:30:35 +0800 Subject: [PATCH 4/4] -add ext `WEBGL_compressed_texture_etc` --- src/twgl.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/twgl.js b/src/twgl.js index 49a3aab0..86de94ca 100644 --- a/src/twgl.js +++ b/src/twgl.js @@ -217,6 +217,7 @@ const supportedExtensions = [ 'WEBGL_color_buffer_float', 'WEBGL_compressed_texture_atc', 'WEBGL_compressed_texture_etc1', + 'WEBGL_compressed_texture_etc', 'WEBGL_compressed_texture_pvrtc', 'WEBGL_compressed_texture_s3tc', 'WEBGL_compressed_texture_s3tc_srgb',