creator贴图纹理压缩(creator2.4.x 实现ETC2和ASTC)

article/2025/11/10 6:37:06

目录

1. creator游戏开发之纹理压缩

2. 常用的压缩纹理格式

3. 测试

4. ETC2 格式测试

6. ASTC格式测试

送上下载链接 不修改引擎的实现ASTC格式加载.zip-cocos2D文档类资源-CSDN下载

7.  最后附上插件代码


1. creator游戏开发之纹理压缩

目的:减少运行内存,减少耗电量发热

2. 常用的压缩纹理格式

  1. 常用压缩格式有 ETC1、ETC2、PVRTC、ASTC
  1. 分析对比:

兼容 、内存、 效果 各方面分析对比

ETC1的问题是不支持透明通道;而PVRTC的问题是透明图片质量太差,且图片大小必须是2的幂和正方形。ETC2的出现正好弥补了这两个格式的不足。

ETC2 需要 OpenGL ES 3.0 支撑。

OpenGL ES 3.0的兼容情况,大概是这样的:

  • 软件:
  • android 4.3以上支持ES3.0
  • android  minSdkVersion版本18
  • IOS 7以上支持ES3.0
  • 硬件:
  • Adreno 300 and 400 series (Android, BlackBerry 10, Windows Phone 8, Windows RT)
  • Mali T600 series onwards (Android, Linux, Windows 7)
  • PowerVR Series6 (iOS, Linux)
  • Vivante (Android, OS X 10.8.3, Windows 7)
  • Nvidia (Android, Linux, Windows 7)
  • Intel (Linux)
  • 苹果设备从A7开始支持ES3.0,最低要求的设备是:
  • iPhone 5S
  • iPad Air
  • iPad mini with Retina display

  

ASTC 6x6的内存容量小于ETC2 4 bits,压缩质量高于ETC2 4 bits。creator3.0以后支持ASTC ,受版本限制creator2.4需要修改c++底层逻辑来加载ASTC

3. 测试

测试的纹理图片1.jpg (720*1600) 位深度24、2.png( 614*902) 位深度8、3.png( 462*689)位深度8

补充知识:

RGBA4444 :每个通道占4位(bit)4*4 = 16位

RGBA8888 :每个通道占8位(bit)32位

RGB565 :占用的存储大小 5+6+5 = 16 位 = 2Byte

RGB888 :每个通道占8位(bit)24位 = 3Byte

纹理内存计算:(12* 720*1600 + 4* 614*902 + 4* 462*689)/(1024*1024) = 16.5M

测试工程:不加载任何纹理 (空包内存):50.6M

显示那3张图片内存:67.3M

4. ETC2 格式测试

  • 准备工作

需要android  minSdkVersion版本18

使用的 ext.COMPRESSED_RGBA8_ETC2_EAC

引擎目录jsb-adapter/bin/jsb-builtin.js可以看到支持的压缩格式

压缩工具在引擎目录:\2.4.3\resources\static\tools\texture-compress\mali\Windows_64\

etcpack.exe

相关工具ImageMagick 中文站

构建后需要关注的变化

查阅 CCMacro.js的 SUPPORT_TEXTURE_FORMATS和 resources\engine\cocos2d\renderer\gfx\ enums.js 的enums

回到构建后的assets目录下资源,压缩需要处理的贴图得到pkm

 0 换成 6@29

  • IOS 需要改用ES3.0的EGLContext 参考 PR #1685

creator2.4支持

creator2.4.3 提示只支持导出,但实际测试是已经支持加载了。可能编辑器没同步更新。

  • 结果

ETC2 fast 内存:减少%40以上

一共花了约2分钟

ETC2 slow 压缩一张图片要3-5分钟,实在太慢了。好在有缓存不用重复压缩,和fast模式差别不大,可能图片质量高一些,看不太出来。

问题

  1. 透明渐变区域有波纹(透明通道)
  1. mac平台纹理压缩 后透明度不对, etc2存在alpha精度丢失

解决方式(Mac 升级工具链

6. ASTC格式测试

ASTC 6x6的内存容量小于ETC2 4 bits,压缩质量高于ETC2 4 bits。

需要参考creator3.x的实现方式自行修改 参考这里

送上下载链接 不修改引擎的实现ASTC格式加载.zip-cocos2D文档类资源-CSDN下载

7.  最后附上插件代码

插件说明:插件实现在构建的时候自动将项目贴图资源压缩,无感操做

完整构建插件:packages\pack-render

核心代码文件(main.js):

/*main.js*/
/* eslint-disable no-undef */
const Fs = require('fs');
const Path = require('path');
const FsExtra = require('fs-extra');
const { resolve } = require('path');const settingPath = "packages://pack-render/config/setting.json"
const astc = "packages://pack-render/astcenc/astcenc-avx2"
var isCompress = true;
var platform = "android"// 开始构建
function onBeforeBuildStart (options, callback) {isCompress = options.isCompressplatform = options.platformEditor.log("pack-render:start::", isCompress);if (!isCompress) {callback();return;}Editor.Ipc.sendToMain("pack-render:doWork", "start", function (event, args) {Editor.log("doWork  start >>> Complete");callback();});
}
// 构建完成
function onBeforeBuildFinish (options, callback) {callback();
}function onBuildFinish (options, callback) {isCompress = options.isCompressplatform = options.platformEditor.log("pack-render:finish::", isCompress);if (!isCompress) {callback();return;}Editor.Ipc.sendToMain("pack-render:setBuildResults", options.bundles);Editor.Ipc.sendToMain("pack-render:doWork", "finish", function (event, args) {Editor.log("doWork finish >>> Complete");callback();});
}const Types = {NONE: "none",ETC2_RGBA: "etc2",ASTC_6_6: "astc_6_6",ASTC_8_8: "astc_8_8"
}function autoSetTypeSelectWithPlatform (setting, p) {if (p === 'ios') {setting.typeSelect = Types.ASTC_6_6} else if (p === 'android') {setting.typeSelect = Types.ETC2_RGBA}
}module.exports = {data: {setting: {},platformSettings: {},cmd: "start",buildBundles: null},messages: {'open' () {Editor.Panel.open('pack-render');// 面板加载时,直接显示缓存数据.setTimeout(() => {Editor.Ipc.sendToPanel("pack-render", "loadConfigs", "arg1", "arg2");}, 2000);},'onStart' (event, arg1, arg2) {Editor.log("onStart", arg1, arg2);},'setBuildResults' (event, bundles) {// Editor.log("setBuildResults:", bundles);this._setBuildResults(bundles);},async 'doWork' (event, arg1) {// this.encNativeTexture("D:/build/jsb-default/assets/testui/native/2e/2ef15057-867c-45ec-a6b1-1dabeaeec6bc.c4c12.png");// return;Editor.log("doWork:", arg1);await this.updateSetting()this.cmd = arg1let isAstc = this.isASTC()if (this.cmd == "start") {if (this.setting.include && this.setting.include.length > 0) {// if (isAstc) {//   setTimeout(() => {//     if (event.reply) {//       event.reply(null, 'Fine, thank you!');//     }//   }, 1000);// } else {this.findPaths(this.setting.include, event)// }}} else if (this.cmd == "finish") {if (this.setting.include && this.setting.include.length > 0) {if (isAstc) {this.findImports(this.setting.include, event)} else {this.findPaths(this.setting.include, event)}}}}},load () {this.updateSetting();Editor.Builder.on('build-start', onBeforeBuildStart); // 构建开始时触发。// 在构建结束 之前 触发,此时除了计算文件 MD5、生成 settings.js、原生平台的加密脚本以外,大部分构建操作都已执行完毕。我们通常会在这个事件中对已经构建好的文件做进一步处理。Editor.Builder.on('before-change-files', onBeforeBuildFinish);Editor.Builder.on('build-finished', onBuildFinish); // 构建完全结束时触发。},unload () {Editor.Builder.removeListener('build-start', onBeforeBuildStart);Editor.Builder.removeListener('before-change-files', onBeforeBuildFinish);Editor.Builder.removeListener('build-finished', onBuildFinish);},addLog (...str) {Editor.Ipc.sendToPanel("pack-render", "addLog", str.join(':'));Editor.log(str.join(':'))},// 同步配置updateSetting () {return new Promise(resolve => {// 检查配置文件Fs.access(Editor.url(settingPath), Fs.constants.R_OK | Fs.constants.W_OK, (err) => {if (err) {autoSetTypeSelectWithPlatform(this.setting, platform)Fs.writeFile(Editor.url(settingPath), JSON.stringify({}), 'utf-8');return resolve(this.setting)} else {// Editor.log(`${settingPath} exists, and it is read-writable`);Fs.readFile(Editor.url(settingPath), 'utf-8', (err, res) => {if (err) {return resolve(this.setting);}const settingObj = res ? JSON.parse(res) : {};autoSetTypeSelectWithPlatform(settingObj, platform)Editor.log("update setting", JSON.stringify(settingObj));this.setting = settingObjreturn resolve(this.setting)});}})});},isASTC () {return this.setting.typeSelect == Types.ASTC_6_6 || this.setting.typeSelect == Types.ASTC_8_8},_setBuildResults (bundles) {this.buildBundles = bundles},// 查询资源async findPaths (include, event) {//for (let index = 0; index < include.length; index++) {const config = include[index];let path = "db://assets/" + config.path + "/**/*"// "db://assets/testui/**/*"let results = await this.queryAssets(path)if (!results) {continue;}for (let itemidx = 0; itemidx < results.length; itemidx++) {const item = results[itemidx];if (this.isExcludePath(item.url)) {this.addLog("排除:", item.url);continue}// Editor.log("绝对路径:", item.path);let uuid = item.uuid;this.addLog("设置纹理格式:", item.url);await this.changeMeta(item.uuid, item.url)}if (index == include.length - 1) {this.addLog(">>> Complete");if (event.reply) {event.reply(null, 'Fine, thank you!');}}}},isExcludePath (url) { // 排除for (let index = 0; index < this.setting.exclude.length; index++) {const path = this.setting.exclude[index].path;if (url.indexOf(path) >= 0) {return true;}}return false;},queryAssets (urlPath) {Editor.log("查询目录:", urlPath);return new Promise(resolve => {Editor.assetdb.queryAssets(urlPath, "texture", (err, results) => {if (err) {Editor.log("error:", err);return resolve(null)}Editor.log("找到文件个数:", results.length);return resolve(results)});});},// 修改metachangeMeta (uuid, url) {return new Promise(resolve => {let type = this.setting.typeSelectthis.platformSettings = {android: {formats: []}}if (this.cmd == "finish") {this.platformSettings = {};} else {if (type == Types.ETC2_RGBA) {this.platformSettings.android.formats.push({ name: "etc2", quality: "fast" })} else if (type == Types.ETC2_RGB) {this.platformSettings.android.formats.push({ name: "etc2_rgb", quality: "fast" })} else {this.platformSettings = {};}}var platformSettingsTemp = this.platformSettings// Editor.log("修改meta:", JSON.stringify(this.platformSettings));// Editor.log("修改meta:", uuid);Editor.Ipc.sendToMain("asset-db:query-meta-info-by-uuid", uuid, function (err, info) {let metaInfo = JSON.parse(info.json)// Editor.log("修改前meta:", platformSettingsTemp);metaInfo.platformSettings = platformSettingsTemp// Editor.log("修改后meta:", JSON.stringify(metaInfo))Editor.assetdb.saveMeta(metaInfo.uuid, JSON.stringify(metaInfo, null, 2), function (err, info) {resolve()});})});},// 查询importasync findImports (include, event) {if (!this.isASTC()) {return;}for (let index = 0; index < this.buildBundles.length; index++) {const bundle = this.buildBundles[index];// Editor.log("bundle:", bundle)if (bundle.name == "internal" || !bundle.root) {continue}var buildResults = bundle.buildResults;//let querypath = bundle.root + "/**/*"let results = await this.queryImports(querypath)if (!results) {continue;}for (let j = 0; j < results.length; ++j) {let item = results[j];if (this.isExcludePath(item.url)) {this.addLog("排除:", item.url);continue}Editor.log("ready astcEnc :", item.url)let uuid = item.uuidvar nativePath = buildResults.getNativeAssetPath(uuid);let importUrl;if (buildResults._md5Map) {let import_md5 = buildResults._md5Map[uuid];importUrl = bundle.dest + "/import/" + uuid.slice(0, 2) + "/" + uuid + "." + import_md5 + ".json"} else {importUrl = bundle.dest + "/import/" + uuid.slice(0, 2) + "/" + uuid + ".json"}await this.astcEnc(importUrl, nativePath);}}if (event.reply) {event.reply(null, 'Fine, thank you!');}},queryImports (urlPath) {Editor.log("查询目录:", urlPath);return new Promise(resolve => {Editor.assetdb.queryAssets(urlPath, "texture", (err, results) => {if (err) {Editor.log("error:", err);return resolve(null)}return resolve(results)});});},async astcEnc (importUrl, nativeUrl) {Editor.log("importUrl:", importUrl);await this.changeImportJson(importUrl)Editor.log("nativeUrl:", nativeUrl);await this.encNativeTexture(nativeUrl)return new Promise(resolve => {resolve();});},// 废弃getAssetPath (uuid, bundelName) {for (let index = 0; index < this.buildBundles.length; index++) {const bundle = this.buildBundles[index];// if (bundle.name != bundelName) {//   continue// }var buildResults = bundle.buildResults;// Editor.log("bundle:", JSON.stringify(bundle));// Editor.log("buildResults:", buildResults);// 获得指定资源依赖的所有资源// var depends = buildResults.getDependencies(uuid);// Editor.log("depends:", depends);// 获得构建后的原生资源路径(原生资源有图片、音频等,如果不是原生资源将返回空)var nativePath = buildResults.getNativeAssetPath(uuid);let importUrl;if (buildResults._md5Map) {let import_md5 = buildResults._md5Map[uuid];importUrl = bundle.dest + "/import/" + uuid.slice(0, 2) + "/" + uuid + "." + import_md5 + ".json"} else {importUrl = bundle.dest + "/import/" + uuid.slice(0, 2) + "/" + uuid + ".json"}// Editor.log("importPath:", importUrl);// 获取资源类型// var type = buildResults.getAssetType(uuid);// Editor.log("type:", type);return { nativeUrl: nativePath, importUrl: importUrl }}},changeImportJson (importUrl) {return new Promise(resolve => {Fs.readFile(importUrl, 'utf-8', (err, res) => {if (err) {return resolve();}let obj = JSON.parse(res)if (this.setting.typeSelect == Types.ASTC_6_6) {obj[5][0] = "7@34,9729,9729,33071,33071,0,0,1"} else if (this.setting.typeSelect == Types.ASTC_8_8) {obj[5][0] = "7@37,9729,9729,33071,33071,0,0,1"}// Editor.log("changeImportJson:", JSON.stringify(obj));Fs.writeFileSync(importUrl, JSON.stringify(obj), 'utf-8');return resolve(obj)});});},encNativeTexture (nativeUrl) {return new Promise(resolve => {// var cmd = Editor.url('packages://pack-render/astcenc/a.bat') + " " + nativeUrl;// var cmd = Editor.url('packages://pack-render/astcenc/do.bat') + " " + nativeUrl;let outfile = nativeUrl.replace(/\.\w+$/, ".astc");let sel = "6x6"if (this.setting.typeSelect == Types.ASTC_6_6) {sel = "6x6"} else if (this.setting.typeSelect == Types.ASTC_8_8) {sel = "8x8"}var cmd = "python " + Editor.url('packages://pack-render/astcenc/runner.py') + ` -cl ${nativeUrl} ${outfile} ${sel} -medium`;this.exec(cmd, () => {Fs.unlink(nativeUrl, (err) => {if (err) {Editor.error(err)}Editor.log('>>> encNativeTexture complete');return resolve();});})// let pythonFile = Editor.url('packages://pack-render/') + "test.py"// this.python(pythonFile, [nativeUrl, Editor.url('packages://pack-render/')], () => {//   Editor.log('>>> encNativeTexture complete');// })});},exec (cmd, callback) {var exec = require('child_process').exec;Editor.log('exec::' + cmd);exec(cmd, function (err, stdout, stderr) {if (err) {Editor.log('execall:: error:' + stderr);} else {// var data = JSON.parse(stdout);Editor.log(stdout)callback()}});},shell (cmd, callback) {var callfile = require('child_process');var ip = '1.1.1.1';var username = 'test';var password = 'pwd';var newpassword = 'newpwd';callfile.execFile('xxx.sh', ['-H', ip, '-U', username, '-P', password, '-N', newpassword], null, function (err, stdout, stderr) {// callback(err, stdout, stderr);});},python (pythonUrl, pramas, callback) {var exec = require('child_process').exec;exec('python ' + pythonUrl + ' ' + pramas[0] + ' ' + pramas[1] + ' ', function (error, stdout, stderr) {if (stdout.length > 1) {Editor.log('you offer args:', stdout);} else {Editor.log('you don\'t offer args');}if (error) {Editor.info('python call error::' + stderr);}});}}

参考

1.

Cocos Creator 纹理压缩插件 - Creator - Cocos中文社区

2. etc2

【技术分享之三】cocos实现对ETC2的支持 - Creator - Cocos中文社区

astc

大佬们有没有计划或者方案在2.4.x上使用astc - Creator - Cocos中文社区


http://chatgpt.dhexx.cn/article/qYwGBVgq.shtml

相关文章

Android平台压缩纹理ETC2 VS ASTC

1&#xff09;Android平台压缩纹理ETC2 VS ASTC ​2&#xff09;Unity使用Profiler和UWA内存差异巨大 3&#xff09;Unity 2020 IL2CPP打包异常 4&#xff09;TouchScreenKeyboardWrap在PC模式下打包报错 这是第301篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发…

Unity iOS使用ASTC格式纹理实践

引言 上一篇文章描述了如何在不修改自定义渲染组件的前提下使用 alpha 分离的纹理来提升 iOS 的透明压缩纹理质量&#xff08;见这里&#xff1a;https://indienova.com/indie-game-development/unity-alpha-separate/&#xff09;。 在这个方案投入项目开始使用一段时间之后…

ASTC纹理压缩格式详解

https://zhuanlan.zhihu.com/p/158740249 一、ASTC纹理压缩格式介绍 ASTC是在OpenGL ES3.0出现后在2012年中产生的一种业界领先的纹理压缩格式&#xff0c;它的压缩分块从4x4到12x12最终可以压缩到每个像素占用1bit以下&#xff0c;压缩比例有多种可选。ASTC格式支持RGBA&…

astc纹理压缩格式

astc是当前android和ios平台下压缩最好的纹理格式&#xff0c;并且主流的机器基本都支持&#xff0c; astc:支持非2次幂的&#xff0c;2次幂的&#xff0c;等宽高的 一、ASTC纹理压缩格式介绍 ASTC是在OpenGL ES3.0出现后在2012年中产生的一种业界领先的纹理压缩格式。 它的压…

ASTC textures

ASTC textures 原文&#xff1a;https://arm-software.github.io/opengl-es-sdk-for-android/astc_textures.html This document describes usage of compressed ASTC textures. Introduction This tutorial shows how Adaptive Scalable Texture Compression (ASTC) can be …

[图形学]ASTC纹理压缩格式

纹理压缩的目的 1&#xff0c;降低内存&#xff0c;特别是移动端应用&#xff0c;内存占用不应过大&#xff0c;否则低端机很容易崩溃 2&#xff0c;降低带宽&#xff0c;手游类应用&#xff0c;在渲染时会有大量贴图传输到GPU&#xff0c;不限制的话不仅会严重影响渲染性能&a…

ASTC纹理压缩格式(Adaptive Scalable Texture Compression)

原文&#xff1a;这是一个pdf的下载链接 介绍 Adaptive Scalable Texture Compression(ASTC)是一种世界领先的新型纹理压缩格式。这种压缩格式已经加入Khronos标准&#xff0c;并已在某些硬件平台中提供。本文介绍了它的工作原理、使用方法和如何最大程度地使用它。更深入的信…

ASTC纹理压缩格式介绍

一、ASTC纹理压缩格式介绍 ASTC是在OpenGL ES 3.0出现后&#xff0c;在2012年中产生的一种业界领先的纹理压缩格式&#xff0c;它的压缩分块从4x4到12x12最终可以压缩到每个像素占用1bit以下&#xff0c;压缩比例有多种可选。ASTC格式支持RGBA&#xff0c;且适用于2的幂次方长宽…

选择软件人力外包公司看这几点没错

近几年&#xff0c;大数据、云计算等各种互联网技术飞速发展&#xff0c;深入到我们工作生活的各个角落。很多企业为了提升竞争力也加快了信息化建设的步伐&#xff0c;而信息化建设的关键就是软件人才&#xff0c;谁能快速构建真正高效的软件开发团队&#xff0c;谁就能先一步…

10 个Web3 设计灵感网站

10 个Web3 设计灵感网站&#xff1a;Cosmos、Axies Infinity、DeSo Foundation、Foundation App、Llama、Snapshot、Juicebox、Alchemy、RabbitHole 正如Twitter前首席执行官Jack Dorsey最近发的一条推文“你不拥有web3&#xff0c;但风险投资家拥有”&#xff0c;而Marc Andre…

web 服务器有哪些

<1>什么是web服务器 "网络服务"&#xff08;Web Service&#xff09;的本质&#xff0c;就是通过网络调用其他网站的资源。 Web Service架构和云 如果一个软件的主要部分采用了"网络服务"&#xff0c;即它把存储或计算环节"外包"给其他…

要不要进外包?

互联网行业的估计都听过这句话:外包&#xff0c;你是外包&#xff0c;麻烦不要偷吃公司零食&#xff0c;注意素质&#xff01; 事情是这样的:她说自己被外派到一家大公司上班&#xff0c;因为那家公司是大公司&#xff0c;在休息的时候还提供零食和下午茶。大家都知道女生爱吃…

为什么程序员做外包会被瞧不起?

二哥&#xff0c;有个事想询问下您的意见&#xff0c;您觉得应届生值得去外包吗&#xff1f;公司虽然挺大的&#xff0c;中xx&#xff0c;但待遇感觉挺低&#xff0c;马上要报到&#xff0c;挺纠结的。 以上是读者小 K 给我发的私信。除此之外&#xff0c;还有个读者 down 也问…

被迫选择了到了外包公司

即使大厂裁员下来的员工愿意被迫选择了到了外包公司&#xff0c;迫不得已做外包的工作&#xff0c;那么&#xff0c;中小型企业那势必也得裁员&#xff0c;为了接收从大厂下来的&#xff0c;有大厂背景的员工&#xff0c;那么就会裁掉自己公司的员工。 这样看来&#xff0c;这…

测试应届生是去自研小公司好还是外包公司好?

我不知道当年怎么想的&#xff0c;能在一个外包公司一干就是3年&#xff0c;后来终于跳出来了&#xff0c;现在的公司虽然不是什么大厂吧&#xff0c;但至少是个正经的互联网企业&#xff0c;待遇也不错。其实很多地方的朋友都有提到外包公司的一些弊端。 外包公司&#xff1a…

网站建设公司该不该把web前端外包出来!精辟

如今的网站建设公司其实过的并不好&#xff0c;一些建站平台、模板建站、仿站等都对网站定制造成了比较大的影响&#xff0c;网站建设公司如何降低用人成本、灵活的整合第三方资源成为度过“特殊时期”的重要手段&#xff0c;迎接下一波春天的到来。 网站建设公司该不该把前端…

外包公司面试门槛高吗?软件测试员进外包公司容易吗?

虽然很多测试人员都抵制外包&#xff0c;但实际情况则是依旧有大量软件测试员&#xff0c;选择加入到外包这个圈子。外包公司面试门槛高吗?外包公司容易进吗?本篇来解答一下这个问题。 外包公司面试门槛高吗&#xff1f; 外包的面试门槛&#xff0c;相对大厂要低很多。尤其…

我的web前端工作日记11------在腾讯外包的这一年

说在前面的话 本文只是大概说一下自己在腾讯做了一年前端外包的收获和一些心得感悟&#xff0c;希望自己能客观的描述&#xff0c;能给一些后来者参考取舍&#xff0c;看是否值得去腾讯做外包。写的没啥逻辑&#xff0c;都是想到啥就写啥&#xff0c;所以大家将就着看看。 一…

进程平均周转时间的计算

题目&#xff1a; 有4个进程A,B,C,D,设它们依次进入就绪队列&#xff0c;因相差时间很短可视为同时到达。4个进程按轮转法分别运行11,7,2,和4个时间单位&#xff0c;设时间片为1。四个进程的平均周转时间为 &#xff08;&#xff09;&#xff1f; 分析 要理解周转时间的含义&am…

操作系统进程完成时间,周转时间,带权周转时间, 平均周转时间, 带权平均周转时间计算

计算规则 周转时间作业完成时刻-作业到达时刻&#xff1b; 带权周转时间周转时间/服务时间&#xff1b; 平均周转时间作业周转总时间/作业个数&#xff1b; 平均带权周转时间带权周转总时间/作业个数&#xff1b;