การเร่งฮาร์ดแวร์

ไปป์ไลน์การแสดงผล 2 มิติของ Android รองรับการเร่งฮาร์ดแวร์ ซึ่งหมายความว่า การดำเนินการวาดทั้งหมดที่ทำใน Canvas จะใช้ GPU เนื่องจากต้องใช้ทรัพยากรมากขึ้นเพื่อเปิดใช้การเร่งฮาร์ดแวร์ แอปของคุณจึงใช้ RAM มากขึ้น

ระบบจะเปิดใช้การเร่งฮาร์ดแวร์โดยค่าเริ่มต้น หากแอปพลิเคชันใช้เฉพาะ Composable มาตรฐาน การเปิดใช้ทั่วโลกไม่น่าจะทำให้เกิดเอฟเฟกต์การวาดภาพ ที่ไม่พึงประสงค์ อย่างไรก็ตาม เนื่องจากระบบไม่รองรับการเร่งด้วยฮาร์ดแวร์สำหรับการดำเนินการวาดภาพ 2 มิติทั้งหมด การเปิดใช้จึงอาจส่งผลต่อการเรียกใช้การวาดภาพที่กำหนดเองบางรายการ โดยปกติแล้วปัญหาจะแสดงในรูปแบบขององค์ประกอบที่มองไม่เห็น ข้อยกเว้น หรือพิกเซลที่แสดงผลอย่างไม่ถูกต้อง Android จึงมีตัวเลือกให้คุณ เปิดหรือปิดใช้การเร่งฮาร์ดแวร์ได้หลายระดับ ดูควบคุม การเร่งฮาร์ดแวร์

หากแอปพลิเคชันของคุณทำการวาดที่กำหนดเอง ให้ทดสอบแอปพลิเคชันในอุปกรณ์ฮาร์ดแวร์จริงโดยเปิดใช้การเร่งฮาร์ดแวร์เพื่อค้นหาปัญหา ส่วนการรองรับการดำเนินการวาดภาพจะอธิบายปัญหาที่ทราบเกี่ยวกับฮาร์ดแวร์ เร่งความเร็วและวิธีแก้ไขปัญหา

ดูOpenGL ด้วย Framework API ด้วย

ควบคุมการเร่งฮาร์ดแวร์

คุณควบคุมการเร่งฮาร์ดแวร์ได้ในระดับต่อไปนี้

  • แอปพลิเคชัน
  • กิจกรรม
  • หน้าต่าง
  • Composable

ระดับแอปพลิเคชัน

ในไฟล์ Manifest ของ Android ให้เพิ่มแอตทริบิวต์ต่อไปนี้ลงในแท็ก <application> เพื่อเปิดใช้การเร่งด้วยฮาร์ดแวร์สำหรับทั้งแอปพลิเคชัน

<application android:hardwareAccelerated="true" ...>

ระดับกิจกรรม

หากแอปพลิเคชันทำงานไม่ถูกต้องเมื่อเปิดการเร่งด้วยฮาร์ดแวร์ ทั่วโลก คุณสามารถควบคุมการเร่งด้วยฮาร์ดแวร์สำหรับกิจกรรมแต่ละอย่างได้เช่นกัน หากต้องการเปิดหรือปิดใช้การเร่งฮาร์ดแวร์ที่ระดับกิจกรรม คุณสามารถใช้แอตทริบิวต์ android:hardwareAccelerated สำหรับองค์ประกอบ <activity> ตัวอย่างต่อไปนี้จะเปิดใช้การเร่งฮาร์ดแวร์สำหรับทั้งแอปพลิเคชัน แต่ ปิดใช้สำหรับกิจกรรมหนึ่ง

<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

ระดับหน้าต่าง

หากต้องการควบคุมที่ละเอียดยิ่งขึ้น คุณสามารถเปิดใช้การเร่งด้วยฮาร์ดแวร์ สำหรับหน้าต่างที่ต้องการได้ด้วยโค้ดต่อไปนี้

window.setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)

ระดับที่ประกอบกันได้

ใน Compose จะไม่มีสวิตช์ต่อ Composable เพื่อปิดใช้การเร่งฮาร์ดแวร์

หากต้องการแสดงผล Composable ในเลเยอร์ของตัวเอง ให้ใช้ Modifier.graphicsLayer ซึ่งจะช่วยให้พร็อพเพอร์ตี้การเปลี่ยนรูปแบบ (เช่น alpha, scaleX, scaleY, translationX, translationY, rotationX, rotationY, rotationZ และ transformOrigin) เปลี่ยนแปลงได้โดยไม่ต้องเรียกใช้โค้ดการวาดของ Composable อีกครั้ง เพื่อประสิทธิภาพที่ดีที่สุด ให้ใช้รูปแบบ Lambda ของตัวแก้ไขเพื่อตั้งค่าพร็อพเพอร์ตี้เหล่านี้เสมอ

หากต้องการบังคับให้ใช้บัฟเฟอร์นอกหน้าจออย่างชัดเจนสำหรับการดำเนินการวาดขั้นสูง เช่น การผสมแบบกำหนดเองภายในเลเยอร์ ให้ใช้ CompositingStrategy.Offscreen ดูข้อมูลเพิ่มเติมได้ที่ตัวแก้ไขกราฟิก

หากมีการดำเนินการวาดภาพที่กำหนดเองซึ่งต้องใช้การแสดงผลซอฟต์แวร์อย่างเคร่งครัด คุณสามารถโฮสต์ View เดิมโดยใช้ AndroidView และเรียกใช้ setLayerType(View.LAYER_TYPE_SOFTWARE, null) ใน View นั้น

การรองรับการดำเนินการวาด

เมื่อเร่งด้วยฮาร์ดแวร์ ไปป์ไลน์การแสดงผล 2 มิติจะรองรับการดำเนินการวาดภาพ Canvas ที่ใช้กันมากที่สุด รวมถึงการดำเนินการอื่นๆ ที่ใช้กันน้อยกว่า ระบบรองรับการดำเนินการวาดภาพทั้งหมดที่ใช้ในการแสดงผลแอปพลิเคชันที่มาพร้อมกับ Android, Composable มาตรฐาน และเอฟเฟกต์ภาพขั้นสูงทั่วไป เช่น การสะท้อนและพื้นผิวแบบเรียงต่อกัน

ตารางต่อไปนี้อธิบายระดับการสนับสนุนของการดำเนินการต่างๆ ในระดับ API

ระดับ API ที่รองรับเป็นครั้งแรก
Canvas
drawBitmapMesh() (อาร์เรย์สี) 18
drawPicture() 23
drawPosText() 16
drawTextOnPath() 16
drawVertices() 29
setDrawFilter() 16
clipPath() 18
clipRegion() 18
clipRect(Region.Op.XOR) 18
clipRect(Region.Op.Difference) 18
clipRect(Region.Op.ReverseDifference) 18
clipRect() พร้อมการหมุน/มุมมอง 18
สี
setAntiAlias() (สำหรับข้อความ) 18
setAntiAlias() (สำหรับเส้น) 16
setFilterBitmap() 17
setLinearText()
setMaskFilter()
setPathEffect() (สำหรับเส้น) 28
setShadowLayer() (นอกเหนือจากข้อความ) 28
setStrokeCap() (สำหรับเส้น) 18
setStrokeCap() (สำหรับจุด) 19
setSubpixelText() 28
Xfermode
PorterDuff.Mode.DARKEN (framebuffer) 28
PorterDuff.Mode.LIGHTEN (เฟรมบัฟเฟอร์) 28
PorterDuff.Mode.OVERLAY (เฟรมบัฟเฟอร์) 28
Shader
ComposeShader ภายใน ComposeShader 28
เชดเดอร์ประเภทเดียวกันภายใน ComposeShader 28
เมทริกซ์ในเครื่องบน ComposeShader 18

การปรับขนาด Canvas

เราสร้างไปป์ไลน์การแสดงผล 2 มิติที่เร่งด้วยฮาร์ดแวร์ขึ้นมาเป็นอันดับแรกเพื่อรองรับ การวาดที่ไม่ปรับขนาด โดยการดำเนินการวาดบางอย่างจะลดคุณภาพลงอย่างมาก เมื่อค่าการปรับขนาดสูงขึ้น การดำเนินการเหล่านี้จะใช้เป็นพื้นผิวที่วาดที่ สเกล 1.0 ซึ่ง GPU จะแปลง ตั้งแต่ระดับ API 28 เป็นต้นไป การวาดทั้งหมดจะปรับขนาดได้โดยไม่มีปัญหา

ตารางต่อไปนี้แสดงเวลาที่มีการเปลี่ยนแปลงการติดตั้งใช้งานเพื่อให้จัดการ ขนาดใหญ่ได้อย่างถูกต้อง

การดำเนินการวาดที่จะปรับขนาด ระดับ API ที่รองรับเป็นครั้งแรก
drawText() 18
drawPosText() 28
drawTextOnPath() 28
รูปร่างง่ายๆ 17
รูปร่างที่ซับซ้อน 28
drawPath() 28
เลเยอร์เงา 28

หากการดำเนินการวาดภาพที่คุณใช้ไม่ได้เร่งด้วยฮาร์ดแวร์ ให้แสดงผล การวาดภาพที่ได้รับผลกระทบในซอฟต์แวร์นอกหน้าจอ Bitmap (หรือ ImageBitmap) แล้ว วาดผลลัพธ์ ส่วน UI ที่เหลือจะยังคงใช้เส้นทางที่เร่งด้วยฮาร์ดแวร์

กลเม็ดเคล็ดลับ

การเปลี่ยนไปใช้กราฟิก 2 มิติที่เร่งด้วยฮาร์ดแวร์จะช่วยเพิ่มประสิทธิภาพได้ทันที แต่คุณก็ยังควรออกแบบแอปพลิเคชันให้ใช้ GPU อย่างมีประสิทธิภาพโดยทำตามคำแนะนำต่อไปนี้

ลดความซับซ้อนของเลย์เอาต์และการจัดองค์ประกอบใหม่
ทำให้โครงสร้างเลย์เอาต์ไม่ซับซ้อนและจำกัดจำนวนการจัดองค์ประกอบใหม่ เลื่อนการอ่านสถานะไปยังขอบเขตที่แคบที่สุด เพื่อให้การเปลี่ยนแปลงวาดซ้ำในภูมิภาคที่เล็กที่สุดเท่าที่จะเป็นไปได้ เช่น อ่านสถานะภาพเคลื่อนไหวภายใน Modifier.graphicsLayer { } แทนที่จะอ่านในเนื้อหาของ Composable ดูข้อมูลเพิ่มเติมได้ที่ประสิทธิภาพของ Jetpack Compose
หลีกเลี่ยงการเบิกเงินเกินบัญชี
อย่าวาดเลเยอร์ซ้อนกันมากเกินไป นำองค์ประกอบ UI ที่ถูกองค์ประกอบทึบแสงอื่นๆ ซ้อนทับจนมองไม่เห็นออก หากต้องการวาดเลเยอร์หลายเลเยอร์ที่ผสมกันอยู่ด้านบน ให้พิจารณารวมเลเยอร์เหล่านั้นเป็นเลเยอร์เดียว กฎง่ายๆ สำหรับฮาร์ดแวร์ปัจจุบัน คืออย่าลากพิกเซลบนหน้าจอต่อเฟรมมากกว่า 2.5 เท่า (พิกเซลโปร่งใสในบิตแมปจะนับรวมด้วย)
อย่าสร้างออบเจ็กต์การแสดงผลในเมธอดการวาด
ข้อผิดพลาดที่พบบ่อยคือการสร้าง Paint ใหม่หรือ Path ใหม่ทุกครั้งที่มีการเรียกใช้เมธอดการแสดงผล ซึ่งจะบังคับให้ตัวเก็บขยะทำงานบ่อยขึ้น และยังข้ามแคชและการเพิ่มประสิทธิภาพในไปป์ไลน์ฮาร์ดแวร์ด้วย หากต้องการหลีกเลี่ยงปัญหานี้ ให้นำออบเจ็กต์กลับมาใช้ซ้ำและเปลี่ยนค่าของออบเจ็กต์
  • ใช้วิธีมาตรฐาน: DrawScope วิธีมาตรฐาน (เช่น drawRect และ drawCircle) จะนำออบเจ็กต์ Paint มาใช้ซ้ำภายในอยู่แล้วโดยไม่ต้องมีการจัดสรรจากนักพัฒนาซอฟต์แวร์
  • เปลี่ยนแปลงแทนการจัดสรรใหม่: เมื่อเขียนตรรกะที่กำหนดเอง ให้ใช้ path.rewind เพื่อล้าง Path ที่มีอยู่แทนการสร้างอินสแตนซ์ Path ใหม่
  • คงสถานะอย่างมีประสิทธิภาพ: ภายใน Composable ให้จัดสรรออบเจ็กต์เพียงครั้งเดียวโดยใช้ remember { Path() } หากคุณกำลังสร้างส่วนขยายตัวแก้ไขที่กำหนดเองที่นำกลับมาใช้ใหม่ได้ ให้ใช้ Modifier.Node ที่กำหนดเองโดยใช้ DrawModifierNode เพื่อจัดสรรและนำออบเจ็กต์กลับมาใช้ใหม่โดยไม่ต้องจัดสรรฮีปใหม่
อย่าแก้ไขรูปร่างบ่อยเกินไป
ระบบจะแสดงรูปร่าง เส้นทาง และวงกลมที่ซับซ้อน เช่น โดยใช้มาสก์พื้นผิว ทุกครั้งที่คุณสร้างหรือแก้ไขเส้นทาง ไปป์ไลน์ฮาร์ดแวร์จะสร้างมาสก์ใหม่ ซึ่งอาจมีค่าใช้จ่ายสูง
อย่าแก้ไขบิตแมปบ่อยเกินไป
ทุกครั้งที่คุณเปลี่ยนเนื้อหาของบิตแมป ระบบจะอัปโหลดเนื้อหานั้นอีกครั้งเป็นเท็กซ์เจอร์ GPU ในครั้งถัดไปที่คุณวาด
ใช้เวอร์ชันอัลฟ่าอย่างระมัดระวัง
เมื่อสร้างองค์ประกอบกึ่งโปร่งใสโดยใช้ Modifier.alpha หรือ API ภาพเคลื่อนไหวของ Compose โดยทั่วไปแล้ว ระบบจะแสดงผลในบัฟเฟอร์นอกหน้าจอ ซึ่งจะเพิ่มอัตราการส่งข้อมูลที่จำเป็นเป็น 2 เท่า หากต้องการหลีกเลี่ยงค่าใช้จ่ายในการบัฟเฟอร์นอกหน้าจอสำหรับเนื้อหาที่ทับซ้อนกัน ให้ตั้งค่า CompositingStrategy.ModulateAlpha สำหรับการเรียกใช้การวาดแต่ละรายการ ให้ใช้ค่าอัลฟ่ากับคำสั่งการวาดโดยตรง (เช่นเดียวกับ color = Color.Red.copy(alpha = 0.5f)) โดยไม่ต้องสร้างเลเยอร์

แหล่งข้อมูลเพิ่มเติม

ดูเนื้อหา