(function (global) { 'use strict'; /** * 资源面板数据源(仅引擎接口): * assetManager.assets — 全部已加载资源 * assetManager.bundles — 已加载 Bundle(归属用 cache/_map 尽力匹配) */ function fmtType(asset) { var t = (asset && asset.constructor && asset.constructor.name) || 'Other'; return String(t).replace(/^cc\./, ''); } function memMB(bytes) { return (bytes || 0) / (1024 * 1024); } /** GPU 估算:纹理去重只计一次 */ function texMemMB(tex) { if (!tex) return 0; var w = tex.width || 0; var h = tex.height || 0; if (!w || !h) { try { if (tex.image && tex.image.width) { w = tex.image.width; h = tex.image.height; } } catch (e) { /* */ } } return w && h ? memMB(w * h * 4 * 1.33) : 0; } function assetMem(asset, type, seenTex) { if (type === 'Texture2D' || type === 'Texture') { var key = String(asset._uuid || asset.uuid || ''); if (key && seenTex[key]) return 0; if (key) seenTex[key] = 1; return texMemMB(asset); } // SpriteFrame / Image 挂在纹理上,不进总量 if (type === 'SpriteFrame' || type === 'ImageAsset') return 0; if (type === 'AudioClip' || /Audio/.test(type)) { try { var d = asset.duration || (asset._nativeAsset && asset._nativeAsset.duration) || 0; return d ? memMB(d * 44100 * 2 * 2) : 0.2; } catch (e) { return 0.2; } } try { if (asset._nativeAsset && asset._nativeAsset.byteLength) { return memMB(asset._nativeAsset.byteLength); } if (asset.data && asset.data.byteLength) { return memMB(asset.data.byteLength); } } catch (e) { /* */ } return 0; } function frameSize(asset) { var rect = asset.rect || asset._rect; if (rect && rect.width) return { w: rect.width | 0, h: rect.height | 0 }; return { w: asset.width | 0, h: asset.height | 0 }; } function gfxTotals() { var cc = global.cc; var tex = 0; var buf = 0; var heap = 0; try { var st = cc && cc.profiler && cc.profiler.stats; if (st) { if (st.textureMemory && st.textureMemory.counter) tex = st.textureMemory.counter.value || 0; if (st.bufferMemory && st.bufferMemory.counter) buf = st.bufferMemory.counter.value || 0; } } catch (e) { /* */ } try { if (performance.memory && performance.memory.usedJSHeapSize) { heap = performance.memory.usedJSHeapSize / (1024 * 1024); } } catch (e2) { /* */ } return { gfxTextureMB: tex, gfxBufferMB: buf, jsHeapMB: heap }; } /** * 按构建「依赖打入分包」扩展归属: * 包内资源引用到的、且尚未被其它包显式声明的资源,打入同一包。 * 使用 assetManager.dependUtil.getDependsRecursively。 */ function expandOwnershipByDeps(cc, bi) { var depUtil = cc.assetManager && cc.assetManager.dependUtil; if (!depUtil || typeof depUtil.getDependsRecursively !== 'function') return; var uuidMap = bi.uuidMap || {}; var depPacked = bi.depPacked || (bi.depPacked = {}); var roots = []; for (var u in uuidMap) { if (Object.prototype.hasOwnProperty.call(uuidMap, u)) roots.push(u); } for (var i = 0; i < roots.length; i++) { var root = roots[i]; var bname = uuidMap[root]; if (!bname) continue; var deps = null; try { deps = depUtil.getDependsRecursively(root); } catch (e) { try { deps = depUtil.getDeps(root); } catch (e2) { deps = null; } } if (!deps || !deps.length) continue; for (var j = 0; j < deps.length; j++) { var d = String(deps[j]); if (!d || d === root) continue; // 已被其它包显式占有则不抢(跨包引用) if (uuidMap[d] && uuidMap[d] !== bname) continue; if (!uuidMap[d]) { uuidMap[d] = bname; depPacked[d] = true; } } } } function resolveBundleName(uuid, asset, bi) { try { if (asset) { if (asset.bundle && asset.bundle.name) return asset.bundle.name; if (asset._bundle && asset._bundle.name) return asset._bundle.name; if (asset.__bundleName) return asset.__bundleName; } } catch (e) { /* */ } if (bi && bi.uuidMap && uuid && bi.uuidMap[uuid]) return bi.uuidMap[uuid]; var url = (asset && (asset.nativeUrl || asset._nativeUrl)) || ''; var m = String(url).match(/\/([A-Za-z0-9_\-]+)\/(?:import|native|js|ohm|chunks)\//i); if (m && m[1]) return m[1]; var objs = (bi && bi.bundleObjs) || []; for (var i = 0; i < objs.length; i++) { var b = objs[i]; if (!b) continue; var bname = b.__pwName || b.name || ''; try { if (typeof b.getAssetInfo === 'function' && uuid) { var info = b.getAssetInfo(uuid); if (info && (info.uuid || info.redirect || info.path != null || info.ext)) return bname; } } catch (e2) { /* */ } try { if (b.cache && b.cache.has && uuid && b.cache.has(uuid)) return bname; } catch (e3) { /* */ } } return ''; } /** * 构建索引:uuid→bundle、uuid→path、pack(自动图集)成员。 * 数据来自 bundle._config(paths / packs)与 cache。 */ function indexBundles(cc) { var list = []; var uuidMap = {}; var pathMap = {}; var packOf = {}; // uuid -> packId var packMembers = {}; // packId -> [uuid] var bundleObjs = []; var addU = function (u, bname) { if (u != null && bname) uuidMap[String(u)] = bname; }; try { cc.assetManager.bundles.forEach(function (bundle, name) { var n = name || (bundle && bundle.name) || ''; if (!n || !bundle) return; bundle.__pwName = n; bundleObjs.push(bundle); list.push({ name: n, assetCount: 0, memMB: 0, texMB: 0, audioMB: 0, otherMB: 0, status: 'loaded', packCount: 0, }); var cfg = bundle._config; // 1) paths: 路径 → [uuid, type, ...] 或 {uuid} try { var paths = cfg && (cfg.paths || cfg._paths); if (paths && typeof paths === 'object') { for (var p in paths) { if (!Object.prototype.hasOwnProperty.call(paths, p)) continue; var val = paths[p]; if (Array.isArray(val)) { for (var i = 0; i < val.length; i++) { var u1 = val[i]; if (typeof u1 === 'string' && /^[0-9a-fA-F-]{8,}/.test(u1)) { addU(u1, n); if (!pathMap[u1]) pathMap[u1] = p; } else if (u1 && u1.uuid) { addU(u1.uuid, n); if (!pathMap[u1.uuid]) pathMap[u1.uuid] = p; } } } else if (val && val.uuid) { addU(val.uuid, n); if (!pathMap[val.uuid]) pathMap[val.uuid] = p; } } } } catch (e) { /* */ } // 2) packs: 自动图集 / 合包 try { var packs = cfg && (cfg.packs || cfg._packs); if (packs && typeof packs === 'object') { var packCount = 0; for (var packId in packs) { if (!Object.prototype.hasOwnProperty.call(packs, packId)) continue; var members = packs[packId]; if (!members) continue; packCount += 1; packMembers[packId] = packMembers[packId] || []; var arr = Array.isArray(members) ? members : [members]; for (var j = 0; j < arr.length; j++) { var um = arr[j]; if (um && typeof um === 'object') um = um.uuid; if (!um) continue; addU(um, n); packOf[String(um)] = packId; packMembers[packId].push(String(um)); if (!pathMap[String(um)]) pathMap[String(um)] = packId; } } for (var q = 0; q < list.length; q++) { if (list[q].name === n) list[q].packCount = packCount; } } } catch (e2) { /* */ } // 3) cache try { var cache = bundle.cache || bundle._cache; if (cache && typeof cache.forEach === 'function') { cache.forEach(function (asset, uuid) { addU(uuid, n); if (asset) addU(asset._uuid, n); }); } if (cache && cache._map) { for (var k in cache._map) { if (Object.prototype.hasOwnProperty.call(cache._map, k)) addU(k, n); } } } catch (e3) { /* */ } // 4) getAssetInfo 扫一遍 config 里的 uuid try { if (typeof bundle.getAssetInfo === 'function') { var keys = Object.keys(uuidMap); for (var s = 0; s < keys.length; s++) { if (uuidMap[keys[s]] === n) continue; try { var inf = bundle.getAssetInfo(keys[s]); if (inf && (inf.path != null || inf.redirect)) { addU(keys[s], n); if (!pathMap[keys[s]] && inf.path) pathMap[keys[s]] = inf.path; } } catch (e4) { /* */ } } } } catch (e5) { /* */ } }); } catch (e6) { /* */ } return { list: list, uuidMap: uuidMap, pathMap: pathMap, packOf: packOf, packMembers: packMembers, bundleObjs: bundleObjs, }; } /** * @returns {{bundles:Array, rows:Array, totals:Object}} */ function collectAssets() { var cc = global.cc; if (!cc || !cc.assetManager || !cc.assetManager.assets) { return { bundles: [], rows: [], totals: gfxTotals() }; } var bi = indexBundles(cc); var bundleByName = {}; for (var i = 0; i < bi.list.length; i++) { bundleByName[bi.list[i].name] = bi.list[i]; } var seenTex = {}; var rows = []; // —— 全部已加载资源:assetManager.assets —— try { cc.assetManager.assets.forEach(function (asset, uuid) { if (!asset) return; var id = String(uuid || asset._uuid || ''); var type = fmtType(asset); var mem = assetMem(asset, type, seenTex); var bname = resolveBundleName(id, asset, bi); if (!bname) bname = 'internal'; var agg = bundleByName[bname]; if (!agg) { agg = { name: bname, assetCount: 0, memMB: 0, texMB: 0, audioMB: 0, otherMB: 0, status: 'loaded', packCount: 0, }; bundleByName[bname] = agg; bi.list.push(agg); } var kind = 'other'; if (type === 'Texture2D' || type === 'Texture') kind = 'tex'; else if (type === 'AudioClip' || /Audio/.test(type)) kind = 'audio'; var w = 0; var h = 0; if (type === 'SpriteFrame') { var sz = frameSize(asset); w = sz.w; h = sz.h; } else { w = asset.width | 0; h = asset.height | 0; } // 自动图集 / 合包:packId;SpriteFrame 挂到纹理 uuid var packId = bi.packOf[id] || ''; var texUuid = ''; if (type === 'SpriteFrame') { var tex = asset.texture || (asset.getTexture && asset.getTexture()); texUuid = String((tex && (tex._uuid || tex.uuid)) || ''); } else if (type === 'Texture2D' || type === 'Texture') { texUuid = id; } var display = asset.name || ''; var pathStr = bi.pathMap[id] || ''; if (!display || /^[0-9a-f-]{8,}$/i.test(display)) { if (pathStr) { var segs = String(pathStr).split('/'); display = segs[segs.length - 1] || pathStr; } if (!display) { try { for (var bi2 = 0; bi2 < bi.bundleObjs.length; bi2++) { var bb = bi.bundleObjs[bi2]; if (bb && typeof bb.getAssetInfo === 'function') { var inf = bb.getAssetInfo(id); if (inf && inf.path) { var ps = String(inf.path).split('/'); display = ps[ps.length - 1] || inf.path; break; } } } } catch (eName) { /* */ } } if (!display) display = id.slice(0, 8) + '…'; } rows.push({ uuid: id, name: display, type: type, bundle: bname, mem: mem, ref: asset.refCount != null ? asset.refCount : 0, w: w, h: h, kind: kind, pack: packId, texUuid: texUuid, path: pathStr, }); agg.assetCount += 1; agg.memMB += mem; if (kind === 'tex') agg.texMB += mem; else if (kind === 'audio') agg.audioMB += mem; else agg.otherMB += mem; }); } catch (e) { console.warn('[Agent] assets.scan', e); } var bundles = bi.list; for (var j = 0; j < bundles.length; j++) { if (bundles[j].assetCount === 0 && bundles[j].status === 'loaded') { // cache 里没建到索引,但 assets 已扫到就保持 loaded;全 0 才标空 if (bundles[j].memMB === 0 && bundles[j].assetCount === 0) { bundles[j].status = 'loaded-empty'; } } } bundles.sort(function (a, b) { return (b.memMB - a.memMB) || (b.assetCount - a.assetCount) || String(a.name).localeCompare(String(b.name)); }); var totals = gfxTotals(); totals.estimateMB = 0; totals.texMB = 0; totals.audioMB = 0; totals.otherMB = 0; for (var r = 0; r < rows.length; r++) { totals.estimateMB += rows[r].mem; if (rows[r].kind === 'tex') totals.texMB += rows[r].mem; else if (rows[r].kind === 'audio') totals.audioMB += rows[r].mem; else totals.otherMB += rows[r].mem; } totals.assetCount = rows.length; totals.bundleCount = bundles.length; totals.packCount = Object.keys(bi.packMembers || {}).length; return { bundles: bundles, rows: rows, totals: totals, packs: bi.packMembers || {}, packOf: bi.packOf || {}, }; } function buildPreview(asset, type) { try { if (type === 'SpriteFrame') { var tex = asset.texture || (asset.getTexture && asset.getTexture()); var img = tex && tex.image; var data = img && (img._data || img.data); var rect = asset.rect || asset._rect || { x: 0, y: 0, width: asset.width, height: asset.height }; if (data && typeof HTMLImageElement !== 'undefined' && data instanceof HTMLImageElement) { var ih = data.height || img.height || 0; var sw = Math.max(1, rect.width | 0); var sh = Math.max(1, rect.height | 0); var sx = rect.x | 0; var sy = Math.max(0, ih - (rect.y | 0) - sh) | 0; var cv = document.createElement('canvas'); cv.width = sw; cv.height = sh; cv.getContext('2d').drawImage(data, sx, sy, sw, sh, 0, 0, sw, sh); return { kind: 'image', w: sw, h: sh, url: cv.toDataURL('image/png') }; } if (img && img.nativeUrl) { return { kind: 'url', url: img.nativeUrl, w: (rect.width | 0), h: (rect.height | 0) }; } return { kind: 'none' }; } var im = type === 'ImageAsset' ? asset : (asset.image || asset._image); var d2 = im && (im._data || im.data); if (d2 && typeof HTMLImageElement !== 'undefined' && d2 instanceof HTMLImageElement) { return { kind: 'image', w: d2.width, h: d2.height, url: d2.src || '' }; } if (d2 && d2.data && d2.width && typeof ImageData !== 'undefined') { var c2 = document.createElement('canvas'); c2.width = d2.width; c2.height = d2.height; c2.getContext('2d').putImageData(new ImageData(d2.data, d2.width, d2.height), 0, 0); return { kind: 'image', w: d2.width, h: d2.height, url: c2.toDataURL('image/png') }; } if (im && im.nativeUrl) return { kind: 'url', url: im.nativeUrl, w: im.width | 0, h: im.height | 0 }; if (asset.nativeUrl) return { kind: 'url', url: asset.nativeUrl, w: asset.width | 0, h: asset.height | 0 }; } catch (e) { /* */ } return { kind: 'none' }; } function previewAsset(uuid) { var cc = global.cc; var asset = null; try { asset = cc.assetManager.assets.get(uuid); } catch (e) { asset = null; } if (!asset) return { kind: 'none' }; return buildPreview(asset, fmtType(asset)); } if (global.PWAgent) { global.PWAgent.prototype._listAssets = function () { return collectAssets(); }; global.PWAgent.prototype._listAssetsAsync = function (done) { try { done(collectAssets()); } catch (e) { done({ bundles: [], rows: [], totals: gfxTotals() }); } }; global.PWAgent.prototype._previewAsset = function (uuid) { return previewAsset(uuid); }; } global.PWAssetsUtil = { collectAssets: collectAssets, previewAsset: previewAsset, buildPreview: buildPreview, }; })(window);