Multithreaded Rendering DX11
ל Directx גירסה 11 נוסף פיצר חדש של Multithreading שמטרתו להפחית את העומס הרב על ה Rendering thread היחיד , על ידי פיזור של חלק מהעבודה שהוא עושה בthreads אחרים או שימוש בbatch ים של פקודות לgpu . בפוסט זה אני אנסה לשפוך אור על הפיצר הזה .
על אף ריבוי המעבדים ב GPU שמגיע לאלפים ,וריבוי ה Cpu ים במערכת. הפקודות ל GPU מה CPU עוברות דרך GPU Commands queue יחיד באופן סינכרוני.
ההזנה שלה Commands queue מתבצעת על ידי Thread יחיד בשם ה Rendering thread .
ניתן לראות ולדבאג את הפקודות הנכנסות ל Command queue באמצעות Pix

.
במצב שבו לא משתמשים ב Directx multitheading כל העבודה של הrendering מתבצעת בthread אחד .מכיוון שהעבודה של ה GPU הינה סנכרונית ל Rendering thread ,נוצרים מצבים שבהם הgpu מחכה בחוסר מעש לפקודות מה cpu.
טעינה אסינכרונית של Resource ים
טעינה של Reource ים כדוגמת Textures , Vertex buffersלוקחת זמן ,ביצוע טעינה ב Rendering thread גורם לעיקוב פעולת הGPU ,בד"כ במערכות מורכבות לא ניתן כמו ב Demos לטעון את כל הReource ים בתחילת התוכנית בשל העובדה שהכמות של הReources ים די גדולה אלא יש לבצע את הטעינה של ה Reources ים On the fly במהלך התוכנית .
Directx 11 מאפשר לנו ליצור את הResources ב Thread נפרד שונה מה Rendering thread

Multithreaded Submission
האפשרות לחלק את העבודה של ה Rendering של ה Device בין מספר threads .כל Thread בונה Display List שבסיום הבניה משוגר ל Rendering thread .


Per-Object Display Lists
יכולת זו מאפשרת להכין בthread שאיינו ה Rendering thread ,אסופה של פקודות Draw שנקראות Display List המרכיבות אוביקט לוגי בSence כדוגמת החייל שמורכב מאוסף הDraws הבאים :




לאחר שיצרנו את הBatch אנו יכולים ב Rendering thread להשתמש בו על ידי קריאה ל Execute של אותו Display List



אחרי שה Display List נוצר הוא Immutable אין אפשרות לשנותו על ידי הוספה או גריעה של פקודות Draw .לדוגמא להוסיף לחייל חרב , לדמות פגיעות וכד .
ניתן לשנות את הResources שמשויכים לאותו DisplayList החל מדברים בסיסים כמו מטריצות העולם שבו החייל נמצא וכלה במטריצות הסיבוב המשויכות לBones של החייל שנועדו ליצור תנועה .
מימוש
עד לגירסה DX11 היה בDirectX רק Interface אחד כדוגמת IDirect3DDevice9 ששימש לפיקוד על ה DX device בDX11 הפיקוד על ה Device מתחלק ל 3 בעלי תפקידם שונים.

ID3D11Device – הinterface הזה יכול להיקרא מכל thread שהוא ולבצע טעינת reosurce וכד במקביל לפעולת הרינדור
Rendering thread ID3D11DeviceContext – הDevice context ששייך ל Renderingthread .אחראי להזרמת הCommands ל Command queue של כרטיס המסך .
Deffered Thread ID3D11DeviceContext – ה context שנוצר בthread שאיינו הrendering thread ויכול לבנות Display List .

בDirectx Sdk האחרון ישנו demo המדגים את הטכנולוגיה הזו .
עבדות הrender כאן יכולה להתחלק למספר חלקים בילתי תלויים:
- ציור ההשתקפות של ה scene במראות
- ציור הצל
הDemo בא להראות איך ניתן להשתמש ב Multithreaded rendering על מנת להגדיל את מספר ה fps .
(ניתן להריץ את הdemo בכמעט כל כרטיס שתומך ב dx11 ולראות את השיפור בביצועים )
הdemo מדגים 5 מצבים של Multithreaded rendering .
Immediate
מבצע רגיל בו thead אחד מבצע את כל העבודה משמש referance
ST Def/scene
בmode זה סתם מדגים את השימוש בDeferrered context ללא כל יתרון של ביצועים
MT Def/scene
בMode זה חלוקה של עבודת הציור לפי נושאים כדוגמת רינודר הצל המראות כל אחד ל thread נפרד .
ST Def/chunk
בmode זה עבודת הציור מחולקת לchunks אשר כולם מבוצעים באופן סינכרוני מיועדלהראות את הapi של חלוקה ל chunks
MT Def/chunk
בmode זה עבודת הציור מחולקת לchunks ואז מתרחשת חלוקה של ה chunks לפי מספר המעבדים .
ניתן לראות לפי המונה של Frame per seconds אשר מראה עליה במספר ה frames שמרונדרים על ידי הכרטיס בהתאם למצבי ה Multi threaded rendering השונים בהתאם לכרטיס המסך ולמספר ה cpu ים במערכת .
הרבה פעמים נתקלתי בקוד review שעשיתי בעבודה כבדה של ה CPU כדוגמת חישוב אילו אוביקטים נמצאים ב view frustum או אפילו עבודות AI בRendering thread, הדבר נובע מזה שברוב ה Demos וגם בDXUT הקריאה ל OnFrameMove callback מתבצעת ב rendering thread .דבר זה גורם לפגיע באיכות הrendering ללא קשר ליכולות הgpu .