板子效能評估 · 2026-08-06

板子效能評估

目標是板子上穩定 60fps,現在是 38–42fps。瓶頸已完全定位在 CPU 的程式運算,不是顯示晶片。已完成的優化拿到 3.5 毫秒,還差 7.2–9.6 毫秒 —— 剩下兩條路樂觀估計剛好夠,但都需要投入開發時間。
前三節(目前的狀況 → 結論 → 做了什麼還沒做什麼)看完即可掌握全貌;技術細節與量測資料集中在後段。

rk3576_u · Mali-G52 · 1920×1080 · WebView 116 兩種場面:量測場面(26 隻釘住)與實戰(正式版打包) 2026-08-06 · 約 30 輪板子實測

目前的狀況

現在跑多快,離目標差多少

下面兩根是同一把尺。上面是 60fps 的預算,下面是現在實際跑出來的。

60fps 的預算 16.67 ms
現在的一幀
超出 7.2–9.6 ms
0 ms16.6726.3 ms
目標
60fps
每幀 16.67 ms
現在
38–42fps
每幀 23.9–26.3 ms
還差
7.2–9.6ms
要再砍掉約三成半
戰鬥中頓挫
5
每次 70–274 ms

時間花在哪裡

這張圖回答「一幀的時間被誰用掉」。看比例就好(它在固定場面上量的,總長比實戰短)。

A
B 算角色骨頭在哪 7.15
C 把畫面畫出來 6.15
D 其餘 5.35
0 ms10 ms20.25 ms
A 遊戲邏輯 0.85 B 把遊戲狀態算成畫面 7.15 C 繪圖 6.15 D 其餘 5.35 等顯示晶片 0.75
這張圖只要看一件事

最右邊「等顯示晶片」只有 0.75 毫秒,細到幾乎看不見 —— 佔 4%。

顯示晶片一直在等我們,不是我們在等它。時間全部花在 CPU 跑程式。換更好的顯示晶片不會有幫助。

結論

三句話

① 瓶頸已經完全定位 —— 在 CPU 的程式運算,不是硬體。每一毫秒都指認到具體的程式段落,沒有未知的黑洞

② 60fps 有機會但不保證 —— 剩兩條路(分工到第二條執行緒、用更快的語言重寫核心計算),樂觀估計剛好補得上缺口,但都要投入數週,而且各有一個還沒被證明的前提

③ 建議先花兩天各做一個小驗證,量出「真的拿得到多少」再決定要不要投 —— 可以避免投錯一到兩週。

⚠️ 如果最後到不了 60fps,還有一個退路

改成鎖定穩定 30fps。30fps 的預算是 33.3 毫秒,而我們現在是 23.9–26.3 —— 已經在裡面了。

那樣問題會從「要快一倍」變成「消除偶爾的頓挫」,而頓挫的成因已經全部查明(下一節第 ② 項),是可以在數天內解掉的東西。

⚠️ 這不是技術判斷,是產品決定 —— 60fps 與 30fps 的手感差異要由產品端評估。

做了什麼、還沒做什麼

進度:距離 60fps 的缺口走到哪

優化開始前,這個缺口是 10.7–13.1 毫秒;現在是 7.2–9.6

已消除 3.5 ms
還差 7.2–9.6 ms
0 ms起初的缺口約 11.9 ms
已完成的優化拿回來的(約三成) 還沒解決的(約七成)

已經做到的

成果數字玩家感受
渲染優化(批次化、描邊合併、動畫相位共用等五項)−3.5 ms幀率從約 32–37 提升到 38–42 fps
載入流程修正−1.47 秒每一局都少等 1.5 秒
顯示記憶體−144 MB降低記憶體壓力的風險

還沒解決的兩件事

① 幀率還差 7.2–9.6 ms
要靠下面的兩條路。這是「順不順」的問題
② 戰鬥中會頓 5 次
每次 70–274 毫秒,成因已完整查明(怪物貼圖第一次載入顯示晶片)。這是「有沒有卡一下」的問題 —— 玩家最直接感覺得到

要決定的四件事

項目投入拿到什麼風險
① 併入載入修正 已完成
只差核可
每局少等 1.5 秒
記憶體省 144MB
 畫面已驗過無差異
② 貼圖預先載入 約 2–3 天 戰鬥中 5 次頓挫消失  載入多 0.8 秒、記憶體多 58MB
③ 改用第二條執行緒 數週
需重做資料流
最多 6.15 ms  雛形跑得比原本慢,要重新設計才拿得到
④ WASM 重寫核心計算 1–2 週 約 3 ms  倍率是業界經驗值,我們沒量過

建議的順序

先做確定的,再花小錢買資訊,最後才投大的

第一步(低風險、玩家馬上有感):核可 ①、執行 ②。合計約 2–3 天,玩家會感覺「載得快了、打起來不卡了」。⚠️ 但幀率不會變。

第二步(各花一天買一個數字):③④ 各做一個小規模驗證,量出「真的拿得到多少」。兩天,可以避免投錯一到兩週。

第三步:拿到那兩個數字之後,再決定要不要投 ③④ 的完整版。

這份評估的可信度

量測方式在實際的 Android 板子上量,正式打包版本,真的玩一局
宣稱門檻由量測誤差反推:差距小於 0.6 毫秒的一律不宣稱;0.6–1.5 毫秒要交錯對照至少三對
覆蓋範圍每幀 19.4 毫秒全部拆解到具體程式段落,沒有無法解釋的部分
自我修正過程中有 7 個結論被後續量測推翻並更正,清單附在文末。這是刻意保留的 —— 避免有人引用已作廢的數字
以下為技術細節

資料、方法與逐項說明

上面是結論,下面是支撐它的量測與推導。不需要逐段讀 —— 需要查證某一個數字的時候再翻到對應那節。

技術一、現在幾毫秒

現在 23.9–26.3,60fps 要 16.67,差 7.2–9.6

下面兩根是同一個尺。上面是預算,下面是現在。(正式版打包、真的玩一局量到的。)

60fps 的預算 16.67 ms
現在的一幀
超出 7.2–9.6 ms
0 ms16.6726.3 ms
現在(實戰)
23.9–26.3ms
約 38–42 fps
目標
16.67ms
60 fps
還差
7.2–9.6ms
要再砍掉約三成半
我們的優化已拿到
3.5ms
整套 vs main

60fps = 1000÷60 = 16.67 毫秒,而這塊板子確實跑得到 60Hz(空白頁實測兩輪:59.95/60.03 fps,p50 16.7、p99 16.8–16.9,連尾巴都貼著 16.7)⇒ 16.67 就是正確的目標
(更早的報告採用 17.24=58fps,理由是「板子天花板只有 57–59fps」—— 那個說法實測不成立。⚠️ 唯一沒驗的是「有 WebGL context 時會不會降頻」,那個中間狀態沒量過。)

一句話

超出去的那 7.2–9.6 毫秒,幾乎全在 CPU 上的 JavaScript —— 等 GPU 只佔 4%。

換更好的顯示晶片不會有幫助,問題不在那裡。能做的只有兩種:讓那些計算變快,或把一部分搬到第二條執行緒同時做。

技術二、錢花在哪

一幀切成四塊

這張圖回答的是「時間花在哪一塊」,不是「現在幾毫秒」。
它在量測場面上量的(怪固定 26 隻釘死不動、開發版打包)—— 場面釘住才比得出「某個改動值多少」,代價是它比實戰輕,所以總長只有 20.25 而不是 23.9–26.3。看比例,不要看絕對值。

A
B 算骨頭在哪 7.15
C 畫出來 6.15
D 其餘 5.35
0 ms10 ms20.25 ms(量測場面)
A 遊戲邏輯 0.85 B 把遊戲狀態算成畫面 7.15 C three.js 畫出來 6.15 D 其餘 5.35 等 GPU 0.75
這張圖要看兩件事

① 最右邊那條「等 GPU」只有 0.75 毫秒,細到幾乎看不見。顯示晶片一直在等我們,不是我們在等它。

② B 和 C 是最重的兩塊(合計 66%)—— 而剩下能做的兩條路,正好一條打 B、一條打 C。

技術三、優化前後

優化了 3.5ms,為什麼還差 7.2–9.6?

因為「現在 23.9–26.3」已經是含了所有優化之後的數字。沒有那些優化會更慢。三根同一個尺,都是實戰量到的:

沒有我們的優化 27.1 – 31.0 ms
現在 23.9 – 26.3 ms
60fps 的預算 16.67 ms
還差 7.2 – 9.6 ms
0 ms16.6731 ms
照順序讀這三根

第一根:如果我們什麼都沒做,一幀要 27.1–31.0 毫秒(約 32–37 fps)。

第二根:我們做的優化把它拉到 23.9–26.3(約 38–42 fps)。那就是那 3.5 毫秒的位置 —— 它已經被花掉了。

第三根:而 60fps 要的是 16.67。所以還差 7.2–9.6。

「還差 7.2–9.6」是扣掉我們已經做的之後還差的,不是還沒開始做。

那 3.5 毫秒是哪些東西

陀螺批次化、殭屍描邊併材質群組、相位分桶、10 套骨架不掛進場景樹、動畫格號改走 uniform 陣列。
合起來 3.5 毫秒(整合分支 vs main,同一次跑完、逐對 3.6/3.6/3.5,三次幾乎一樣,很穩)。

還能做的兩條路,夠不夠填滿缺口

兩個名詞先講一下,不然下面看不懂

多執行緒 = 現在「算遊戲」跟「畫畫面」是同一個人在做,一件做完才做下一件。多執行緒就是找第二個人專門畫畫面,兩邊同時做

WASM = 網頁的程式是用 JavaScript 寫的,那是為了「好寫」設計的語言,速度有天花板。WASM 是另一種格式,可以把 C 或 Rust 這類比較接近機器的語言編譯成瀏覽器看得懂的東西 —— 同樣的計算通常快 2 到 5 倍完整說明另開一頁

最上面那根是「要填滿的洞」,下面三根是「各能填多少」。看長度夠不夠到最上面那根就好。

要填滿的缺口 7.2
~9.6
多執行緒 6.15
填不滿
WASM 約 3
差得比較多
兩條都做 約 9.15
0 ms510 ms
這兩個數字是怎麼來的(一個是量到的,一個是估的)

多執行緒 6.15 = 量到的。那就是 renderer.render() 每幀的實測值(本頁第四節的 C 段)。把它整段搬走,主線就從 19.4 降到 13.35
⚠️ 但「拿得到」還沒被證明 —— 整幀會變成「主線」與「第二條執行緒」兩者中比較慢的那個,而現在那條執行緒自己要跑 22.7–29.3ms(它把主線做過的工作原封不動再做一次)。要拿到這 6.15,得先把它的資料流重做。

WASM 約 3 = 量到的數字打了兩層折。推導鏈:

B1 算動畫 3.40 + B2 骨頭換算 2.55 = 5.95ms實測,而且兩種完全不同的儀器互相印證:profiler 5.91/直接包住方法 5.95)
→ 扣掉量測工具本身的成本、換算到現行的分桶設定 → 3.3–4.1ms(這是天花板)
→ 再扣 WASM 自己的跨語言成本 → 約 3ms

🔴 最後那一步沒有數字支撐,是我抓的。WASM 不會讓那段歸零(資料要在兩種語言之間進出),但到底要付多少,只有做原型量了才知道 —— 那就是為什麼建議先花一天做原型。

這張圖的結論

單獨做任一條都填不滿。多執行緒 6.15 差一點、WASM 約 3 差比較多。

兩條都做約 9.15,剛好落在缺口的上緣(7.2–9.6) —— 也就是順利的話剛好夠,不順利就還差一點

⚠️ 「兩條都做 9.15」是把兩個數字直接相加的樂觀估計

它們打的是不同的兩段(一段是「算骨頭」、一段是「畫出來」),理論上不重疊,但沒有人實測過兩個一起做會不會真的相加

這三十輪裡已經有好幾個「應該會相加」的東西,實測不會

技術四、再細一層

最重的兩塊裡面是什麼

B(7.15ms)—— 把遊戲狀態算成畫面

算動畫播到第幾格 3.40
骨頭換算位置 2.55
送 GPU
其餘 1.95
算動畫播到第幾格 3.40 把骨頭換算成世界位置 2.55(含轉頭 0.73) 把算好的送給 GPU 0.75 位置顏色 0.20 描邊、死亡狀態、迴圈 1.95

錢幾乎全花在「算」(前兩塊合計 5.95ms),不是花在「送」(0.95ms)。這就是為什麼 WASM —— 讓計算本身變快 —— 打在對的地方。

那 0.73ms 的「轉頭」值得單獨記著:每隻怪每一幀都重算一次「頭要看哪個陀螺」,而相位分桶碰不到它(分桶只讓「姿勢」能共用)。

C(6.15ms)—— three.js 把畫面畫出來

每次畫之前的準備 52%
排序、建清單、可見性 41%
GL 7%
three 準備材質與 skinning 狀態的 JS 排序/建 render list/可見性(其中 2.3ms 我們拿不到掛點) 真正的繪圖指令

真正「畫」的只有 GL 那 7% 裡的九分之一。其餘 93% 是 three.js 在做準備 —— 走過場景、排序、每次繪製前重設一遍狀態。

D(5.35ms)—— 「其他所有東西」的總和

D 不是一個功能,是「扣掉 A、B、C 之後剩下的一切」。拆開之後發現它不是一塊,是一堆各自 1 毫秒以下的小東西

是什麼毫秒能不能動
其他角色的狀態映射
陀螺、特效、場地 —— B 只涵蓋殭屍
1.0–1.7真實工作,理論上可以像 B 一樣優化,但單項就這個量級
透明畫布疊層
票券數字、空中特效那一層
0.85真實渲染
其他每幀的小工作
七個不在主迴圈裡的回呼,各自 <0.5
1.18各自都是真實工作
瀏覽器自己的排程開銷約 1.0動不了
量測工具本身的成本約 0.7不是真實成本 正式跑的時候沒有這一項

⇒ 扣掉量測工具之後真實約 4.8ms,而最大的單項只有 1.0–1.7ms —— 沒有值得單獨動的東西。

⚠️ 兩件順帶查到的:透明畫布那層在量測場面完全沒在畫(九個 2D 繪圖方法一次都沒被呼叫)—— 那只對這個場面成立,實戰有票券數字時會有。另外,我們一度以為 D 是「主迴圈裡的一整塊」,拆完才發現超過一半根本不在主迴圈裡

技術五、為什麼 three.js 這麼貴

跟遊戲引擎(Cocos)的對照

⚠️ 這一節是讀 Cocos 原始碼得到的,零實測

「它的架構比較適合遊戲」有依據;「它跑我們的內容在這塊板子上能到 60fps」沒有人量過 —— 那是推論不是量測。

核心差別只有一句:遊戲引擎把「每幀重複做的事」設計成只在有變化時才做;繪圖函式庫沒有那個假設。我們做的每一批優化,本質上都是在手工補上引擎本來就內建的東西。

機制Cocos 的做法我們的現況能不能效法
每次繪製怎麼傳參數每個物件有常駐的 GPU 參數區,畫的時候只綁一次每個物件、每幀、每個參數各推一次架構限制 three.js 有自己的一套,繞過要重寫渲染管線
沒變就不做更新前先判斷「這個變了嗎」部分低頻參數每幀仍重寫我們的用法 可以改
合批引擎內建自動合併要手動做 —— 就是我們的「陀螺批次化」我們的用法 已做
骨骼動畫預烘成貼圖,每幀只傳播播放進度每幀每隻怪在 CPU 上重算整棵骨架我們的用法 預烘已做但目前預設關(見第六節)

不換引擎改不動的固定成本:three.js 每次繪製前重設一遍狀態、以及場景樹的走訪 —— 合計約 7–8 毫秒。那就是為什麼「把渲染搬到第二條執行緒」是唯一能繞開它的方向(見第七節)。

🔴 這節有一句常被引用的話,實測不成立

先前的報告寫過「three.js 每幀從頭重建一次繪製清單,那是它跟 Cocos 差最多的地方」。實測:排序每幀只叫 1 次、0.04 毫秒;建清單 0.19 毫秒。

真正的大宗是「每次繪製前準備材質與 skinning 狀態」的那 52% —— 要減它得改材質與批次結構,不是換一個旗標。

技術六、已完成

問題是什麼、怎麼做的、實測省多少

① 陀螺貼圖的解析路徑修好

問題六張玩家陀螺皮膚 + 三張,在正式版一直以 2048² PNG 載入。原因是 PropAdapter.ts:320 把「含副檔名的檔名」直接串成路徑,繞過了會選 WebP 的解析函式(其他貼圖早就是 1024² WebP)
做法改走帶 fallback 的解析函式 + 補出缺的 9 張 1024² WebP
省下載入時間 −1.47 秒(−5.8%)、GPU 常駐記憶體估 −144MB。八張貼圖 29.5MB → 0.90MB
戰鬥中零效果 —— 那些貼圖在按下開始鍵的當下就上傳完了,比進戰鬥早 25 秒
視覺零代價。先造「已知變糊」與「已知壓縮瑕疵」兩個對照證明指標抓得到,出貨版兩項都沒落在退化那側

待決 改動還沒 commit(已另外備份到專案內,資料不會丟)。

② 動畫格號改用 uniform 陣列傳

問題預烘骨骼要告訴 GPU「這隻怪播到第幾格」,而套件的通用機制是把值排成一張貼圖每幀上傳。每多開一種預烘怪,每幀就多一次貼圖上傳
做法改成 uniform float uBakedFrame[200],shader 用 instance 編號查。完全不用上傳
結果上傳的劑量曲線整條消失(四種劑量全是 18 次);中位數收益從 1.0–2.7 變成 2.0–2.4ms(更大也更穩)
卡頓沒有跟著好 ⇒ 證明先前「上傳次數 ↔ 卡頓」那條曲線是混淆不是因果

③ 其他已在整合分支上的

陀螺批次化
24 個各自帶材質的物件 → 6 個 instanced batch。同一組態下兩份三角形數完全相同(111,402),而 draw call main 59 → 我們 22
殭屍描邊併材質群組
描邊從獨立物件變成同物件的第二個材質
相位分桶(預設 6)
同型別的怪收斂成 6 種相位,姿勢算一次共用。代價是同組的怪同手同腳
10 套骨架不掛進 scene
場景節點從 763 降到 123

整套優化 vs origin/main(同一 session、同一組態):−3.5 ms(逐對 3.6/3.6/3.5,全距 0.1ms)。

技術七、評估後放棄

不要再提,這些都有隔離證據

項目為什麼放棄
預烘骨骼(維持預設關)收益隨「有幾種怪」放大,代價隨「有幾隻怪」放大。三種全留:平均收益撐不住宣稱、長幀 4–6 倍;只留一種:+1.4ms、長幀 2–3 倍。而 1.4ms 不會讓任何一幀進得了 vsync,卡頓卻看得見。程式留著,缺口縮小後再回來評估
動畫時間量化整條只值 0.2 次上傳。三種怪是三張獨立貼圖,分桶只在同型別內收斂 ⇒ 不需要蔡做那個視覺決定
把姿勢計算搬到第二條 Worker要接受「動畫慢一幀」,蔡否決。且 14 種動作有 8 種的時間來自遊戲計時器不是牆上時鐘,GPU 算不出來
特效圖集降尺寸按需載入,完整一局 45 秒零請求 ⇒ 不是尖峰來源。但 63 張合計 527MB 是未爆彈
靜態節點 matrixAutoUpdate=false機制上完全無效 —— 那些呼叫全部帶 force=true,three 在 force 為真時不看那個旗標
怪物貼圖降到 512²最差幀 497→160ms、視覺零代價,但中位數一格沒動(降 16 倍像素)⇒ 貼圖頻寬不是瓶頸。正式版起點已是 1024²,再降的空間比看起來小
dirty flag 類做法角色幾乎每幀都變、命中率趨零;而且那些呼叫本來就帶 force=true,是「明知要重算」才叫的
draw call 清理(47→30)0ms。空的粒子發射器每幀什麼都不做,拿掉等於沒差
VAT(全頂點烘成貼圖)需 121.6MB,板子吃不下;4 種怪頂點數超過貼圖上限
技術八、還可以做的

每一項在解什麼問題

要你決定 一、多執行緒重開 —— 最大的一筆

白話

問題:現在「算遊戲」跟「畫畫面」是同一個人在做,一件做完才做下一件。畫畫面那段要 6.15ms。

做法:找第二個人(瀏覽器的第二條執行緒)專門畫畫面,兩邊同時做。畫面資料每一幀送過去。

拿到:主線從 19.4ms 降到 13.35ms —— 進得了 60fps 的預算。

代價:按鍵到畫面反應會多約 16 毫秒(一幀)。畫面本身是對的,不會錯位,只是整張晚一點出現。你已經接受這個代價。

難在哪不是打開一個開關,是要重做資料流。現在的版本讓第二個人「把第一個人做過的事再做一次」(把資料解開、重建場景),所以它自己要跑 22.7–29.3ms,比主線整幀還多。真正要解的是「兩邊共用同一份資料」而不是各建一份

已經證明的:第 1–4 階段做完並驗過逐像素零差異;第二條執行緒畫同一批東西沒有比較慢(4.94ms vs 主線 6.15ms)。我原本反對的理由(「GPU 只有一顆,搬過去沒用」)被三次量測連續否定

要你決定 二、用 WASM 重寫「算骨頭位置」那段

白話

問題:每一幀要算 26 隻怪的骨頭要擺在哪裡(動畫插值 3.4ms + 骨架傳播 2.55ms)。那是純數學計算,而 JavaScript 算數學不夠快。

做法:那兩段改用 C 或 Rust 寫,編譯成 WASM(見上一節的說明框)。

拿到:最多 3.3–4.1ms,而且還要扣掉 WASM 自己的成本(資料要在兩種語言之間搬來搬去)。實際大概 3ms 上下。

代價一到兩週,而且要引進一整套新的建置工具。等於重寫整個動畫系統。

不確定的地方:「快 2–5 倍」是業界的一般經驗值,我們這個案例能加速多少沒有人量過。⇒ 建議先花一天做一個小原型,只搬一小段、在板子上量出真實倍率,倍率撐得住再做完整版。

已查完 三、D 那 5.35ms —— 沒有機會

白話

本來的疑問:每幀有 5.35ms(27%)我們不知道是什麼,而它比 WASM 能拿的還多。

查完了沒有大塊。扣掉量測工具自己的成本後真實約 4.8ms,而最大的一項只有 1.0–1.7ms —— 其他 adapter 與狀態映射 1.0–1.7、疊層 0.85、其他排程 <0.5 各項、瀏覽器自己的排程約 1.0(動不了)。

結論:這裡沒有 WASM 那個量級的東西。而且整條渲染路徑現在完全解釋得通、沒有藏起來的大塊 —— 再查下去不會再冒出新東西。

其他

比較小、或還卡住的

項目問題是什麼做法能省
轉頭重算每隻怪每一幀都重算一次「頭要看哪裡」,即使最近的陀螺沒換只在「最近的陀螺換了」才重算0.73 ms
未評估
貼圖預載
要你決定
已完整歸屬。進入戰鬥後只會再上傳 9 張怪物貼圖,造成 5 個長幀、合計約 660ms:三張同一幀 264–274ms、Bomb 71ms、木乃伊 72–81ms、狗+盜墓者 119–133ms、Boss+Boss 車 118ms載入畫面就先送進去。那 9 張檔案早就下載完了,缺的只是「建 texture 並上傳 GPU」。陀螺已經證明這個時機做得到 —— 怪物只是沒有人在那時去建它們戰鬥中 5 個頓挫消失
代價:載入 +0.7–0.8 秒、GPU 記憶體 +58.7MB(是「提前」不是「新增」)
特效圖集63 張特效圖合計 527MB,最大單張 47MB。只有玩家真的打出那個特效才載 ⇒ 是顆未爆彈可能降尺寸,但判斷不了 —— 缺「每一格在畫面上多大」的資料未知
技術九、名詞說明

這份報告裡反覆出現的詞

白話是什麼
WASM一種可以在瀏覽器裡跑的「接近機器碼」的格式,W3C 標準、業界通用(Figma、Google Earth 網頁版都用它)。同樣的計算通常比 JavaScript 快 2–5 倍,純數字運算差最多。但要用另一種語言重寫那一段,不是加設定
每幀 / 幀時間畫面每更新一次叫一「幀」。要跑 60fps,每一幀只能用 16.7 毫秒(這塊板子實測上限是 17.24)。超過就會掉格
p50 / p95 / p99把所有幀從快到慢排好:p50 是「中間那一幀」(一般狀況)、p95 是「最慢的 5% 從哪開始」、p99 是「最慢的 1%」。p50 代表順不順,p95/p99 代表會不會頓
draw callCPU 對 GPU 說「畫這一批東西」的一次指令。次數越多,CPU 每幀要交代的事越多。批次化就是把很多次併成一次
vsync螢幕的更新節拍。一幀如果沒在節拍內畫完,就得等下一拍 ⇒ 畫面掉格
貼圖上傳把一張圖從記憶體送進 GPU。第一次用到才送,而那一幀就會變得特別長(我們量到 200–490 毫秒)
adapter / mapper遊戲的「狀態」(誰在哪、血量多少)跟畫面的「物件」是分開的。mapper 負責把狀態翻成畫面要的資料,adapter 負責把它套到實際的 3D 物件上
uniform從 CPU 傳給 GPU 著色程式的參數。傳一個數字有很多種方式,方式選錯會變成「每幀傳一整張圖」
instanced batch(批次化)把很多長得一樣的東西(例如六台同型陀螺)合成「一次畫」,而不是各畫各的
Worker / 多執行緒瀏覽器裡的第二條執行緒。可以讓渲染跟遊戲邏輯同時跑,但兩邊不共用記憶體,資料要「送過去」
ASTC一種 GPU 直接看得懂的壓縮圖片格式。好處是不用先解碼、記憶體也小很多。這塊板子支援,但我們的程式沒用到
預烘骨骼把所有動作姿勢「離線先算好」存成一張表,跑的時候不算了、直接查表
相位分桶同一種怪的走路動畫,本來每隻都播在不同的時間點。分桶就是把它們收斂成幾組,同組的姿勢算一次共用 —— 代價是同組的怪會同手同腳
技術十、誠實區

邊界、被推翻的結論、量不到的東西

附註

這份報告改過哪些地方

放在最後,是因為讀報告的人要的是現在的結論,不是它怎麼變成現在這樣。但改過的地方仍然要留痕跡 —— 不然下一個引用舊版的人不會知道那已經不算數。

原本寫的錯在哪現在是
60fps 的預算是 17.24ms17.24 其實是 58fps(1000÷58)。沿用了更早報告的數字,沒問它是怎麼來的;而那份的「板子只有 57–59fps」實測也不成立60fps = 16.67ms(板子實測 59.95/60.03 fps),缺口從 6.7–9.1 改為 7.2–9.6
開頭那張圖標「20.25ms = 現在的一幀那是量測場面(26 隻釘死、開發版打包),與實戰的 23.9–26.3 是兩種條件,並排會讀成矛盾開頭改成實戰數字;拆解圖移到第二節並標明「看比例不看絕對值」
C 的拆解:走場景樹 30%、排序建清單 10%量測的包裝少了一個作用域閘門,把 B 段的骨架運算也算了進來走場景樹每幀只叫 1 次、幾乎不花時間;那 41% 才是第二大的一塊
「靜態節點旗標值得順手做」那些呼叫全部帶 force=true,three 在 force 為真時不看那個旗標 ⇒ 機制上無效從清單移除。另一個相關旗標實測也省 ~0
「多執行緒該收掉,因為 GPU 只有一顆」拿 Worker 端的 23.2ms(含它自己的 JS)跟主線的 6.15ms 直接比,兩個數字內容不同三次量測連續否定:Worker 端 gl.flush 一次都沒被叫、所有 GL 只佔 5–7% ⇒ 這條路重開
「補上缺的 WebP 就能省尖峰」不是 fallback —— PropAdapter 那條路把含副檔名的檔名直接串成路徑,從來沒問過 WebP。而且量錯了時間窗改走正確的解析路徑,價值是載入 −1.47 秒不是戰鬥中的尖峰

六項裡有五項是團隊成員主動推翻的,一項是使用者指出來的。