วิธีที่เร็วที่สุดในการเรนเดอร์ด้วย AA ความหนาต่างกันใน DirectX


14

ดังนั้นฉันจึงทำการพัฒนา DirectX บางอย่างโดยใช้ SharpDX ภายใต้. NET เพื่อให้แน่นอน (แต่โซลูชัน DirectX / C ++ API สามารถใช้ได้) ฉันกำลังมองหาวิธีที่เร็วที่สุดในการเรนเดอร์เส้นในการฉายฉากมุมฉาก (เช่นการจำลองการวาดเส้นสองมิติสำหรับแอปเชิงวิทยาศาสตร์) โดยใช้ DirectX

สกรีนช็อตของแปลงต่าง ๆ ที่ฉันพยายามจะทำดังนี้ ป้อนคำอธิบายรูปภาพที่นี่

มันไม่ใช่เรื่องแปลกสำหรับการแปลงประเภทนี้มีเส้นที่มีหลายล้านส่วนของความหนาของตัวแปรที่มีหรือไม่มีการลดรอยหยักต่อบรรทัด (หรือเปิด / ปิด AA แบบเต็มหน้าจอ) ฉันจำเป็นต้องอัปเดตจุดยอดสำหรับสายบ่อยมาก (เช่น 20 ครั้ง / วินาที) และถ่ายให้ GPU มากที่สุดเท่าที่จะทำได้

จนถึงตอนนี้ฉันได้ลอง:

  1. การแสดงผลซอฟต์แวร์เช่น GDI + จริง ๆ แล้วประสิทธิภาพไม่ดี แต่เห็นได้ชัดว่าหนักบน CPU
  2. Direct2D API - ช้ากว่า GDI โดยเฉพาะเมื่อเปิดใช้การลดรอยหยัก
  3. Direct3D10 ใช้วิธีนี้เพื่อจำลอง AA โดยใช้สีจุดยอดและเทสเซลเลชันด้านซีพียู ยังช้า (ฉันทำประวัติและใช้เวลา 80% ในการคำนวณตำแหน่งจุดสุดยอด)

สำหรับวิธีที่ 3 ฉันใช้ Vertex Buffers เพื่อส่งแถบสามเหลี่ยมไปยัง GPU และอัปเดตทุก ๆ 200 มิลลิวินาทีด้วยจุดยอดใหม่ ฉันได้รับอัตราการรีเฟรชประมาณ 5FPS สำหรับ 100,000 ส่วนของเส้น ฉันต้องการคนเป็นล้าน!

ตอนนี้ฉันคิดว่าวิธีที่เร็วที่สุดคือการทำ tessellation บน GPU เช่นใน Geometry Shader ฉันสามารถส่งจุดยอดเป็นรายการหรือแพ็คในพื้นผิวและแกะใน Geometry Shader เพื่อสร้างล่าม หรือเพียงแค่ส่งคะแนนดิบไปที่พิกเซลเชดเดอร์แล้วนำภาพวาดเส้น Bresenham Line มาใช้ในการสร้างพิกเซล HLSL ของฉันเป็นสนิม, shader รุ่นที่ 2 จากปี 2006 ดังนั้นฉันไม่รู้เกี่ยวกับ GPU ที่ทันสมัยอย่างบ้าคลั่งที่สามารถทำได้

ดังนั้นคำถามคือ: - เคยมีใครทำมาก่อนและคุณมีข้อเสนอแนะให้ลอง? - คุณมีข้อเสนอแนะเพื่อปรับปรุงประสิทธิภาพด้วยการอัพเดทรูปทรงเรขาคณิตอย่างรวดเร็วหรือไม่ (เช่นรายการจุดสุดยอดใหม่ทุก ๆ 20ms)

อัพเดท 21 มกราคม

ฉันได้ใช้วิธีตั้งแต่ (3) ข้างต้นโดยใช้ Geometry shaders โดยใช้ LineStrip และ Dynamic Vertex Buffers ตอนนี้ฉันได้รับ 100FPS ที่ 100k คะแนนและ 10FPS ที่ 1,000,000 คะแนน นี่เป็นการปรับปรุงครั้งใหญ่ แต่ตอนนี้ฉันเพิ่มอัตราและคำนวณ จำกัด ดังนั้นฉันจึงคิดเกี่ยวกับเทคนิค / ความคิดอื่น ๆ

  • สิ่งที่เกี่ยวกับฮาร์ดแวร์อินสแตนซ์ของเรขาคณิตส่วนบรรทัด
  • สิ่งที่เกี่ยวกับสไปรท์แบทช์?
  • วิธีการอื่น ๆ (Pixel Shader) เป็นวิธีการที่มุ่งเน้น?
  • ฉันสามารถเลือก GPU หรือซีพียูได้อย่างมีประสิทธิภาพหรือไม่?

ความคิดเห็นและข้อเสนอแนะของคุณชื่นชมมาก!


1
AA ดั้งเดิมใน Direct3D คืออะไร
API-Beast

5
คุณสามารถแสดงตัวอย่างโค้งบางส่วนที่คุณต้องการลงจุดได้หรือไม่? คุณพูดถึงจุดยอดหนึ่งล้านจุด แต่หน้าจอของคุณอาจมีพิกเซลไม่เกินล้านพิกเซลปริมาณนั้นเป็นสิ่งจำเป็นหรือไม่ ดูเหมือนว่าคุณจะไม่ต้องการความหนาแน่นของข้อมูลแบบเต็มทุกที่ คุณเคยพิจารณา LODs หรือยัง?
sam hocevar

1
วิธีที่สองที่คุณเชื่อมโยงดูเหมือนดีคุณใช้บัฟเฟอร์จุดสุดยอดแบบไดนามิกที่คุณอัปเดตหรือคุณสร้างขึ้นใหม่ทุกครั้งหรือไม่

1
คุณควรจะไปกับบัฟเฟอร์จุดสุดยอดแบบไดนามิก (ไม่ทำงานมากแค่บอกบัฟเฟอร์จุดสุดยอดของคุณให้เป็นแบบไดนามิกแมปบัฟเฟอร์คัดลอกข้อมูลของคุณยกเลิกการแมป) แต่ถ้าคอขวดของคุณคือการสร้างจุดสุดยอดในตอนแรก ช่วยไม่ได้มากตอนนี้ อย่าคิดว่ามีอะไรที่คลั่งไคล้การใช้ GS เพื่อแบ่งเบาภาระให้กับซีพียู

1
หากจำนวนจุดยอดที่ใหญ่เกินไปสำหรับ GPU คุณยังสามารถลองสุ่มตัวอย่างอินพุตของคุณ - คุณไม่ต้องการเซ็กเมนต์บรรทัดหลายร้อยล้านเส้นสำหรับเส้นโค้งเส้นเดียวเมื่อหน้าจอของคุณกว้างไม่กี่พันพิกเซล ที่มากที่สุด.

คำตอบ:


16

หากคุณกำลังจะแสดงผลY = f(X)กราฟเท่านั้นฉันขอแนะนำให้ลองวิธีต่อไปนี้

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

เนื้อหาเนื้อองค์ประกอบเดียวอาจมีลักษณะเช่นนี้:

+----+----+----+----+
| 12 | 10 |  5 | .. | values for curve #1
+----+----+----+----+
| 55 | 83 | 87 | .. | values for curve #2
+----+----+----+----+

การทำงานของ pixel shader เป็นดังนี้:

  • ค้นหาพิกัด X ของแฟรกเมนต์ปัจจุบันในพื้นที่ชุดข้อมูล
  • ใช้เช่น จุดข้อมูลที่ใกล้เคียงที่สุดที่มีข้อมูล ตัวอย่างเช่นถ้าค่า X คือ41.3มันจะเลือก40, 41, และ4243
  • สอบถามพื้นผิวสำหรับค่า 4 Y (ตรวจสอบให้แน่ใจว่าตัวอย่างไม่ทำการแก้ไขใด ๆ )
  • แปลงX,Yคู่เป็นพื้นที่หน้าจอ
  • คำนวณระยะทางจากแฟรกเมนต์ปัจจุบันไปยังแต่ละเซ็กเมนต์ทั้งสามและสี่จุด
  • ใช้ระยะทางเป็นค่าอัลฟาสำหรับแฟรกเมนต์ปัจจุบัน

คุณอาจต้องการทดแทน 4 ด้วยค่าที่มากขึ้นทั้งนี้ขึ้นอยู่กับระดับการซูม

ฉันได้เขียนshader GLSL ที่รวดเร็วและสกปรกมากโดยใช้คุณสมบัตินี้ ฉันอาจเพิ่มรุ่น HLSL ในภายหลัง แต่คุณควรจะสามารถแปลงได้โดยไม่ต้องใช้ความพยายามมากเกินไป ผลที่ได้สามารถดูได้ที่ด้านล่างโดยมีขนาดของเส้นและความหนาแน่นของข้อมูลต่างกัน:

เส้นโค้ง

ข้อดีอย่างหนึ่งที่ชัดเจนคือปริมาณของข้อมูลที่ถ่ายโอนนั้นต่ำมากและจำนวนของ drawcalls เป็นเพียงหนึ่งเดียว


นั่นเป็นเทคนิคที่ค่อนข้างเท่ห์ - ฉันเคยทำ GPGPU มาก่อนดังนั้นการบรรจุพื้นผิวด้วยข้อมูลเป็นสิ่งที่ฉันรู้ แต่ไม่ใช่สิ่งที่ฉันพิจารณา ขนาดที่ใหญ่ที่สุดคืออะไร (เช่นชุดข้อมูลที่ใหญ่ที่สุดที่คุณสามารถอัปโหลดได้) ฉันจะดาวน์โหลดรหัสของคุณถ้าคุณไม่รังเกียจและเล่นกับมัน - ฉันกระตือรือร้นที่จะเห็นประสิทธิภาพ
ดร. ABT

@ Dr.ABT ขนาดชุดข้อมูลจะถูก จำกัด ด้วยขนาดพื้นผิวสูงสุดเท่านั้น ฉันไม่เห็นว่าทำไมคะแนนนับล้านไม่ทำงานเนื่องจากมีเค้าโครงข้อมูลที่เหมาะสม โปรดทราบว่ารหัสของฉันไม่ได้คุณภาพการผลิตอย่างแน่นอน แต่โปรดติดต่อฉันเป็นการส่วนตัวหากมีปัญหาใด ๆ
sam hocevar

Cheers @ SamHocevar - ฉันจะไปทันที เป็นเวลานานแล้วที่ฉันได้รวบรวมใจตัวอย่างของ OpenGL! :-)
ดร. ABT

การทำเครื่องหมายว่าเป็นคำตอบราวกับว่าฉันไม่ได้ใช้การโหลดพื้นผิวคุณได้แสดงตัวอย่าง OpenGL จริงซึ่งสามารถวาดเส้นบนพิกเซล shader และทำให้เราไปในทิศทางที่ถูกต้อง !! :)
ดร. ABT

4

: มีบท GPU อัญมณีในการแสดงผลเส้น antialiased เป็นจาน Prefiltered เส้น แนวคิดพื้นฐานคือการแสดงผลแต่ละส่วนของเส้นเป็นรูปสี่เหลี่ยมและคำนวณในแต่ละพิกเซลเป็นฟังก์ชันเกาส์เซียนของระยะทางของจุดศูนย์กลางพิกเซลจากส่วนของเส้น

นี่หมายถึงการวาดแต่ละส่วนของเส้นในกราฟเป็นรูปสี่เหลี่ยมแยกต่างหาก แต่ใน D3D11 คุณสามารถใช้รูปทรงเรขาคณิตและ / หรือการสร้างกราฟในการลดปริมาณข้อมูลที่จะถ่ายโอนไปยัง GPU เพียงจุดข้อมูล ตัวเอง ฉันอาจตั้งค่าจุดข้อมูลเป็นStructuredBufferเพื่ออ่านโดยจุดสุดยอด / เรขาคณิต shader จากนั้นทำการโทรเพื่อระบุจำนวนของกลุ่มที่จะดึง จะไม่มีบัฟเฟอร์จุดสุดยอดที่แท้จริง จุดสุดยอด shader จะใช้ SV_VertexID หรือ SV_InstanceID เพื่อกำหนดจุดข้อมูลที่จะดู


เฮ้ขอบคุณ - ใช่ฉันรู้แล้วว่าไม่มีซอร์สโค้ด แต่น่าเสียดายที่ :-( สถานที่ตั้งของ GeoShader + FragShader ดูเหมือนจะเป็นผู้ชนะสำหรับงานประเภทนี้การได้รับข้อมูลไปยัง GPU อย่างมีประสิทธิภาพนั้นจะเป็น ส่วนที่ยุ่งยาก!
ดร. ABT

เฮ้ @NathanReed กลับไปที่สิ่งนี้ - ถ้าฉันใช้ Geometry Shader หรืออินสแตนซ์ในการวาดล่ามดังนั้น Point1 / Point2 นั้นอยู่ตรงข้ามกับจุดยอดของรูปสี่เหลี่ยมฉันจะคำนวณระยะห่างของเส้นแบ่งระหว่าง Pt1 และ Pt2 ได้อย่างไร ตัวแบ่งพิกเซลมีความรู้เรื่องเรขาคณิตอินพุตหรือไม่?
ดร. ABT

@ Dr.ABT พารามิเตอร์ของบรรทัดทางคณิตศาสตร์ต้องถูกส่งลงไปที่พิกเซล shader ผ่านตัวแทรก ตัวแบ่งพิกเซลไม่สามารถเข้าถึงเรขาคณิตได้โดยตรง
นาธานรีด

ฉันเห็นสิ่งนี้สามารถทำได้โดยการส่งออกจาก GeometryShader หรือ instancing? สำหรับเครดิตพิเศษ (ถ้าคุณสนใจ) ฉันได้เพิ่ม Q ที่นี่: gamedev.stackexchange.com/questions/47831/…
ดร. ABT

@ Dr.ABT มันเป็นแบบเดียวกับที่พิกัดพื้นผิวเวกเตอร์ปกติและสิ่งของทุกประเภทนั้นถูกส่งไปยังพิกเซลแชดเดอร์ - โดยการเขียนเอาต์พุตจากจุดยอด / รูปทรงเรขาคณิตที่มีการแก้ไขและป้อนพิกเซลที่มีส่วนร่วม
นาธานรีด

0

@ Dr.ABT - คำขอโทษสำหรับคำถามนี้ไม่ใช่คำตอบ ฉันไม่พบวิธีที่จะถามภายในคำตอบของ Sam Hocevar ด้านบน

คุณสามารถแบ่งปันรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการใช้งานเส้นสำหรับแผนภูมิในที่สุด ฉันมีความต้องการเหมือนกันสำหรับแอป WPF ขอบคุณล่วงหน้าสำหรับรายละเอียดหรือรหัสใด ๆ ที่คุณสามารถแบ่งปันได้


ตามที่คุณรู้ว่าไม่ใช่คำตอบโปรดแก้ไขและใส่เป็นความคิดเห็น
MephistonX

ฉันพยายามทำเช่นนั้น แต่ไม่มีปุ่ม "เพิ่มความคิดเห็น" ในโพสต์ดั้งเดิม
ErcGeek

สวัสดี ErcGeek ขอโทษที่ฉันไม่สามารถแบ่งปันทางออกสุดท้ายเนื่องจากข้อมูลนี้เป็นกรรมสิทธิ์ของ บริษัท ของฉัน แม้ว่าตัวอย่างและคำแนะนำข้างต้นทำให้เราอยู่ในเส้นทางที่ถูกต้อง ดีที่สุด!
ดร. ABT

คุณไม่สามารถโพสต์ความคิดเห็นก่อนที่จะถึงชื่อเสียงจำนวนหนึ่ง และ downvotes ทั้งหมดเหล่านี้แน่นอนว่าจะไม่เปิดโอกาสให้คุณ
Mathias Lykkegaard Lorenzen

คนที่แต่งตัวประหลาดไม่ควรถูกลงโทษเพราะต้องการทราบผลสุดท้ายและขอให้เขาทำในวิธีเดียวที่มีให้เขา โหวตคำตอบ
David Ching
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.