Vorax Online: รายงานวิเคราะห์ FPS ฉบับเต็ม

ทดสอบบน Windows วันที่ 2 พ.ย. 2026 · Electron 44.4.5, three.js r186 · Ryzen 9 9950X3D, RTX 4090, จอ 2560×1440 279 Hz

สรุป

คอขวดอยู่ที่ เธรดหลักของ JavaScript ไม่ใช่การ์ดจอ เน็ต หรือตัว Electron สาเหตุใหญ่ที่สุดคือเกมมี requestAnimationFrame แยกกันราว 11 loop และ loop เหล่านี้ไม่ได้ฟัง cap FPS หรือโหมดประหยัดพลังงาน ตั้ง cap ไว้ เกมก็ยังทำงานเกือบเต็มคอร์

ตอนเกมอยู่หน้าสุด เครื่องทดสอบได้ FPS กลาง 113 และแทบไม่ต่ำกว่า 60 ส่วน FPS 30 ที่เห็นบ่อยเกิดจาก cap ตอนหน้าต่างไม่อยู่หน้าสุด ไม่ใช่เครื่องไม่ไหว ปัญหาจึงไม่ได้อยู่ที่เกมช้าในฉากปกติ แต่อยู่ที่เธรดหลักแทบไม่เหลือแรงสำรอง พอเจอคนเยอะ บอส หรือเครื่องที่ CPU ช้ากว่า FPS จะร่วงทันที

113FPS กลาง ตอนอยู่หน้าสุด
~94%เธรดหลักไม่ว่าง ตอนอยู่หน้าสุด
~15%GPU 3D
~60%เธรดหลักไม่ว่าง แม้ cap 30 FPS

ทำไม FPS ถึงตก

วิธีทดสอบ

การทดสอบ รายละเอียด
Client จริง 18 นาที อ่าน FPS บนจอและ metric ทุก 2 วินาที รวม 535 ตัวอย่าง และบันทึกสถานะโฟกัสในรอบหลัง
Profile ใน Electron อัด 15 ช่วง ช่วงละ 10 วินาที โดยอัดอัตโนมัติเมื่อ FPS ต่ำกว่า 60
Profile ใน Chrome อัด 2 รอบ รวมราว 67 วินาที และนับคำสั่ง WebGL 45 วินาที
อ่านโค้ด JS ของเกม Setting, loop, การวาด, ตัวละคร, effect, UI, network และหน่วยความจำ
Process จากภายนอก CPU ต่อเธรด, GPU, VRAM, เน็ต, ดิสก์ และ flag ของ Electron

ฉากทดสอบคือการเล่นด้วย AUTO ค่า setting: quality high, cap 120, แสดงผู้เล่น 10 คน, fxOthers ปิด ชื่อใน minified code อ้างตามไฟล์ที่ส่งจริง ชื่อในซอร์สอาจต่างกัน

ผลวัด

FPS และเธรดหลัก (รอบที่บันทึกโฟกัส 8 นาที)

ค่าที่วัด อยู่หน้าสุด (62 ตัวอย่าง) ไม่อยู่หน้าสุด (176 ตัวอย่าง)
FPS กลาง 113 30
FPS ต่ำ (p5) / ต่ำสุด 71 / 30 30 / 15
ตัวอย่างที่ต่ำกว่า 60 FPS 2 175
เฟรมช้าสุด (p95 / สูงสุด) 36 / 46 ms 36 / 286 ms
เธรดหลักไม่ว่าง 93.6% 60.3%
เวลา Script 60.6% 23.9%
Style / Layout 4.2% / 2.2% 6.3% / 3.0%

สิ่งที่บอกว่า loop แยกกันทำงานไม่หยุด

ค่าต่อวินาที (จาก profile) อยู่หน้าสุด ~100 FPS ไม่อยู่หน้าสุด 30 FPS
Callback ของ rAF 1,559 2,567
เวลาใน rAF 677 ms 299 ms
Paint (ครั้ง) 470 936
Layerize (เวลา) 45 ms 78 ms
Style + Layout + Paint + Layerize + Commit (เวลา) ~208 ms ~267 ms

ตอน cap 30 FPS เกมกลับเรียก rAF และ Paint มากขึ้น งาน DOM ก็ไม่ลดลงตาม FPS ค่าหน้าสุดมาจากช่วง profile เดียว ใช้ดูแนวโน้ม

เวลาเธรดหลักตอนอยู่หน้าสุด (ค่าจาก Chrome)

โค้ด three.js รวม39.9%
งาน DOM: style, layout, paint, commit~22%
คำนวณ matrix และ scene graph (อยู่ใน three.js)~17%
วาดข้อความลง canvas~7.6%
สลับ shader และคำนวณ material (อยู่ใน three.js)~3.3%

บางกลุ่มซ้อนกัน จึงรวมกันไม่เท่า 100% ใน Electron ช่วงที่วัดได้ updateMatrixWorld สูง 8.59%, drawOverlay 5.62%, getParameters 3.33%

ภาระการวาด

คำสั่ง ต่อวินาที ต่อเฟรม (ที่ 95 FPS)
Draw call 33,358 ~350
สามเหลี่ยม 75 ล้าน ~790,000
useProgram 15,108 ~160
uniformMatrix4fv 45,244 ~480
bindFramebuffer 2,030 ~21
bufferSubData 2,352 (35.8 MB) ~25 (0.38 MB)

หน่วยความจำ

ค่าที่วัด รอบ 10 นาที รอบ 8 นาที
JS Heap สูงสุด 625 MB 337 MB
DOM node สูงสุด (รวมที่ค้างรอ GC) 91,488 87,761
Event listener สูงสุด 14,548 7,023

Node แกว่งจากราว 9,000 ถึงเกือบ 90,000 ทั้งที่หน้าเกมมีจริงราว 3,500 น่าจะมีการสร้าง DOM แล้วทิ้งเป็นจำนวนมาก ค่าจบรอบกลับลงมาใกล้เดิม จึงยังไม่ใช่หลักฐานของ leak

Electron เทียบ Chrome

ตอนอยู่หน้าสุดค่าหลักเท่ากันในระยะไม่เกินราว 1 จุด (เธรดหลัก 97–98%, GPU ~15%) เปลี่ยน wrapper อย่างเดียวจึงไม่น่าทำให้ FPS ดีขึ้น

สิ่งที่ควรแก้

เรียงตามผลต่อ FPS ที่คาดไว้ ระดับผลกระทบประเมินจากโค้ดและ profile ยังไม่ได้แก้จริงแล้ววัด

  1. รวม loop ทั้งหมดเข้า loop หลัก

    ผลสูงยืนยันในโค้ดและวัดแล้วยากปานกลาง
    หลักฐาน
    มี loop ทำงานตลอด 11 ตัว: ao, arenagate k, firenest h, gather dt, gathertools q, islandprops y, lifeskill X, outfitfx l, pets ce, redgate R, gacha kt และ titles เพิ่มอีก 1 ตัวต่อ canvas ไม่มีตัวไหนเช็ก cap หรือ powerSave หลายตัวเรียก proj เขียน style.left/top และ arenagate ตั้ง innerHTML ทุก tick
    วิธีแก้
    เพิ่มรายการ onFrame(fn) แล้วเรียกจาก Ve หลัง Ca ผ่าน เขียน DOM เฉพาะตอนค่าเปลี่ยน
    ข้อดี
    Cap และโหมดประหยัดพลังงานจะได้ผลจริง และลดงาน DOM ตามไปด้วย
    ข้อควรระวัง
    งานที่ต้องวิ่งตอนซ่อนหน้าต่าง เช่น logic ต้องอยู่นอก rAF ตามเดิม
  2. เปลี่ยนค่าเริ่มต้น cap เป็น 60

    ผลสูงยืนยันในโค้ดง่ายมาก
    หลักฐาน
    ค่าเริ่มต้น desktop คือ 120 ตัวเลือกมี auto/30/60/120
    ข้อดี
    เฟรมที่วาดลดลงครึ่งหนึ่ง CPU และความร้อนลดลงตาม ผู้เล่นเลือกกลับเป็น 120 ได้
    ข้อควรระวัง
    ควรทำพร้อมข้อ 1 ไม่งั้น loop อื่นยังวิ่งตามจอเหมือนเดิม
  3. เลิกวาดข้อความสดทุกเฟรมใน drawOverlay

    ผลสูงยืนยันแล้วยากปานกลาง
    หลักฐาน
    drawOverlay 5.62% และ set font สูงสุด 4.73% helper n ตั้ง font แล้ววาด stroke + fill ของทุกตัว, proj.ok เช็กแค่ z ป้ายนอกจอจึงยังวาด, loop NPC ไม่เช็ก ok เลย ตัวเลขดาเมจสร้าง font ใหม่ทุกเฟรมตอนย่อขยาย และ crit ใช้ shadowBlur 14
    วิธีแก้
    แคชป้ายลง offscreen canvas แล้ว drawImage, ทำ digit atlas, เช็กขอบจอแกน x/y, เรียงตาม font เพื่อลดการตั้งค่า
    ข้อดี
    ได้ผลมากตอนตีกันเยอะ ซึ่งเป็นจุดที่ FPS ตก
    ข้อควรระวัง
    ใช้หน่วยความจำเพิ่ม ชื่อไทยมีสระและวรรณยุกต์ซ้อน ต้องแคชทั้งชื่อ และต้องล้างแคชตอนเปลี่ยนขนาดฟอนต์
  4. ลดการคำนวณ matrix

    ผลสูงยืนยันแล้วยากปานกลาง
    หลักฐาน
    ราว 17% ของเธรดหลัก matrixAutoUpdate=false มีจุดเดียวใน earthking.js, fxb.sync เรียก updateMatrixWorld(true) ต่อ effect ต่อเฟรม, pickMonster เรียก proj สองครั้งต่อมอนทุกเฟรม
    วิธีแก้
    แช่ matrix ของของนิ่งในแมพ, ทำ hover pick เฉพาะตอนเมาส์ขยับหรือ 20 Hz, อัปเดต root ของ effect ครั้งเดียว
    ข้อดี
    ลดงานทุกเฟรม เห็นผลชัดในแมพที่มีของเยอะ
    ข้อควรระวัง
    ของที่ขยับต้องเรียกอัปเดตเอง ถ้าลืมวัตถุจะค้างตำแหน่ง
  5. จำกัด animation ของมอนและ NPC

    ผลสูงยืนยันในโค้ดยากต่ำถึงกลาง
    หลักฐาน
    syncEntities อัปเดตมอนบนจอทุกเฟรม, NPC อัปเดตเสมอ, ผู้เล่นไกลอัปเดตทุก 3 เฟรม, animateRig reset ทุกข้อต่อก่อนทุกครั้ง, จำกัดผู้เล่นได้ แต่มอนและ NPC ไม่มีตัวจำกัด
    วิธีแก้
    ตัวไกลอัปเดต 15–20 Hz, ใช้กฎเดียวกันกับมอนและ NPC, เพิ่มเพดานจำนวน rig
    ข้อดี
    ได้ผลตรงกับฉากคนเยอะและมอนเยอะ
    ข้อควรระวัง
    ตัวไกลขยับไม่ลื่นเท่าตัวใกล้ แต่เป็นผลด้านภาพเท่านั้น เพราะอยู่บนเซิร์ฟเวอร์
  6. ลด pass ของ post-processing

    ผลกลางยืนยันในโค้ดง่าย
    หลักฐาน
    มีการสลับ render target 18 ครั้งต่อเฟรม: shadow 1, render 1, bloom 13, tilt 2, output 1 เมื่อตั้ง bloomK=0 pass ยังทำงานต่อ
    วิธีแก้
    ปิด pass เมื่อ strength เป็น 0, ลด bloom เหลือ 3 mip, ถอด tilt ใน preset ต่ำ
    ข้อดี
    ลดงานทั้ง CPU และ GPU โดยเฉพาะการ์ดจอออนบอร์ด
    ข้อควรระวัง
    ภาพเปลี่ยน ควรเก็บ preset สูงไว้ให้คนชอบภาพสวย
  7. ปรับค่า renderer ที่เสียเปล่า

    ผลกลางยืนยันในโค้ดง่าย
    หลักฐาน
    antialias:true ไม่มีผลกับฉาก เพราะ composer target ไม่มี samples, PCFSoftShadowMap ถูกถอดใน r186 จึงเป็น PCF, เงา 2048 วาดใหม่ทุกเฟรมตามกล้อง
    วิธีแก้
    ตั้ง antialias:false หรือเพิ่ม samples ให้ composer และอัปเดตเงาทุก 2–3 เฟรมเมื่อกล้องไม่ขยับมาก
    ข้อดี
    แก้ง่ายและได้แรงคืนทันที
    ข้อควรระวัง
    เงาตัวละครที่เคลื่อนไหวอาจดูหน่วงเล็กน้อย
  8. Pool material ของ effect

    ผลกลางยืนยันในโค้ดยากปานกลาง
    หลักฐาน
    makeFx สร้าง material ใหม่ต่อ effect ทำให้ getParameters และ getProgram โผล่ระหว่างเล่น (3.33% ในช่วงที่วัด) ไม่พบ needsUpdate ทุกเฟรม ส่วนการที่ material outline Re ใช้ร่วมระหว่าง skinned กับ static อาจทำให้สลับ program บ่อย (สันนิษฐาน)
    วิธีแก้
    Pool ตามชนิด effect, แยก Re เป็น 2 ตัว, เพิ่มเงื่อนไขให้ effect เข้า batch ได้มากขึ้น
    ข้อดี
    ลดงานตอนสกิลเยอะ
    ข้อควรระวัง
    ต้อง reset ค่าใน pool ให้สะอาด ไม่งั้น effect จะมีสีหรือค่าค้าง
  9. ลดการอัปโหลด buffer

    ผลกลางที่มายังไม่ครบยากปานกลาง
    หลักฐาน
    วัดได้ 35.8 MB/s และพบการอัปโหลดทั้ง buffer ใน wings.js, wings-cyber.js, fashion.js, pets.js, redgate.js, gacha.js และ portal แต่ขนาดที่อ่านได้น้อยกว่าที่วัด จึงน่าจะมีแหล่งอื่นอีก
    วิธีแก้
    ใช้ addUpdateRange, ข้ามตัวนอกจอ, แชร์ pool ต่อชนิด
    ข้อดี
    ลดงาน GPU process ที่ใช้ CPU สูงอยู่
    ข้อควรระวัง
    ควรหาแหล่งใหญ่ให้เจอก่อน ไม่งั้นอาจแก้แล้วไม่เห็นผล
  10. ลด line of sight และงานใน Jn

    ผลกลางยืนยันในโค้ดยากปานกลาง
    หลักฐาน
    Jn ทำงานทุกเฟรมที่วาด และ We ตรวจ line of sight ซ้ำตามเส้นทาง
    วิธีแก้
    รัน 20–30 Hz แล้ว interpolate ตำแหน่งที่เหลือ
    ข้อดี
    เกมเดินเท่าเดิมทุก FPS และลดงานเธรดหลัก
    ข้อควรระวัง
    ตัวละครตัวเองต้องยังตอบสนองทันที ไม่ควรรู้สึกหน่วง
  11. เปลี่ยนข้อมูล network เป็น binary

    ผลขึ้นกับจำนวนคนยืนยันในโค้ดยากสูง
    หลักฐาน
    ใช้ JSON ผ่าน WebSocket, snapshot ส่งข้อมูลเต็มต่อ entity และใส่ค่าทันทีที่รับ, TICK_RATE 20
    วิธีแก้
    ส่งเฉพาะค่าที่เปลี่ยน, ใช้ binary, parse ใน Worker
    ข้อดี
    ลดเน็ตและ CPU ตอนคนเยอะ ทั้งฝั่ง client และ server
    ข้อควรระวัง
    ต้องแก้ทั้ง client และ server ควรทำเมื่อ profile ฉากคนเยอะยืนยันว่าจำเป็น
  12. เช็ก DOM churn และเฟรมกระตุกหนัก

    สันนิษฐานต้องสืบต่อ
    หลักฐาน
    Node ขึ้นถึง 91,488, heap ถึง 625 MB และพบเฟรม 221–1,025 ms รวม 8 ครั้ง GC ปกติแค่ราว 0.4–0.5% จึงต้องดูว่าการกระตุกเกิดจาก GC ก้อนใหญ่หรืองานอื่น พบ rainObjs ไม่ถูก dispose ตอนถอด และ addEventListener 269 จุด เทียบกับ removeEventListener 22 จุด
    วิธีแก้
    เปิด DevTools Memory เทียบ heap snapshot, ติด longtask observer และ log ฉากที่เกิดเหตุ
    ข้อดี
    สำคัญกับผู้เล่นที่เปิด AUTO ทิ้งไว้นาน
    ข้อควรระวัง
    อย่าตัดสินว่าเป็น leak จาก Node อย่างเดียว เพราะค่าจบรอบกลับมาใกล้เดิม
  13. ลดงานเล็กที่รันทุกเฟรม

    ผลต่ำยืนยันในโค้ดง่าย
    หลักฐาน
    c.update ใช้ querySelector 3 ครั้ง และ JSON.stringify 2 ครั้งทุกเฟรม, buildFrame ห่อ texImage2D ใน production, telemetry ทุก 60 วินาทีเรียก scene.traverse
    วิธีแก้
    จำกัด c.update ที่ 10 Hz, ปิดตัวห่อใน production
    ข้อดี
    แก้เร็ว ความเสี่ยงต่ำ
    ข้อควรระวัง
    HP ตัวเองและคูลดาวน์ต้องยังตอบสนองทันที

Setting ในเกม

Setting ตัวเลือก ค่าเริ่มต้น ข้อสังเกต
quality high, mid, low high (มือถือ mid) high: pixel ratio 1.75, เงา 2048, bloom, tilt · low: 0.75, ไม่มีเงา bloom tilt
fpsCap auto, 30, 60, 120 120 auto คือ 60 บน desktop และ 30 บน touch ไม่มีตัวเลือกไม่จำกัด
dynRes เปิด/ปิด ปิด ถูกบังคับปิดตอนโหลด ตัวปรับความละเอียดจึงแทบไม่ทำงาน
showPlayers 0, 5, 10, 20 10 ไม่มีตัวเลือกเดียวกันสำหรับมอนหรือ NPC
fxOthers เปิด/ปิด ปิด ปิดไว้แล้วช่วยได้มากตอนคนเยอะ
powerSave เปิด/ปิด ปิด ซ่อน canvas และรัน logic ใน Worker 50 ms แต่ loop แยกยังทำงาน

ลำดับงานและผลที่คาด

ระยะที่ 1: แก้เร็ว

  • Cap เริ่มต้น 60
  • ตั้ง antialias:false
  • ปิด pass เมื่อ strength เป็น 0
  • c.update 10 Hz
  • เช็กขอบจอให้ป้ายชื่อ

ระยะที่ 2: ผลมากสุด

  • รวม loop เข้า onFrame
  • แคชป้ายชื่อและ digit atlas
  • แช่ matrix ของของนิ่ง
  • จำกัด animation มอนและ NPC

ระยะที่ 3: งานใหญ่

  • Pool material
  • ลด buffer upload
  • Binary network
  • พิจารณา Web Worker

ควรวัดซ้ำในฉากเดิมทุกครั้งหลังแก้ และเปิดให้ผู้เล่นกลุ่มเล็กลองก่อน

งานเสริม

สิ่งที่ไม่ใช่ปัญหา

ข้อจำกัด