עיבוד אות מורכב כדוגמת עיבוד אות וידיאו ב live,או משימות בעלות הרבה מאוד חשובים ניתנות לביצוע היום ב cpu .יתרה מזאת גם במערכות של smart phones ניתן לבצע היום עיבוד וידיאו ב Live לא מזמן בתערוכה של smart phones הוצגה אפליקציה שיודעת לנתח אות וידיאו לטובת זיהוי מכשולים בנהיגה.ניתן למצא הרבה דוגמאות למימוש video processing ב android באמצעות ספריית open cv .
כאשר המשימה מאוד מורכבת וכבדה חישובית כבר לא ניתן לבצע אותה באמצעות ה cpus במחשבים רגילים .דוגמא לאפליקציה כזו הינה NBodyGravityCS11 שב Directx sdk .הדוגמא מדמה תנועת כוכבים בגלקסיה כאשר כל תנועת כל כוכב מושפעת מכוח הכבידה של כל שאר הכוכבים .

כמות החישובים ב Demo הזה הינה עצומה כאשר מנסים לבצע את החישובים הללו באופן הרגיל באמצעות ה Cpu גם במחשב חזק מאוד האפליקציה נתקעת ב 100% Cpu עבור כל ה cpu processors .
אחת הטכנולוגיות החדשות שנוספו ב Directx 11 הינה DirectCompute.טכנולוגיה זו באה לתת כלים למפתח שרוצה להשתמש ביכולות של GPU לביצוע הרבה חישובים מקבילים במהירות .
כאשר מריצים את ה Demo באמצעות DirectCompute ה Demo עובד חלק ללא צריכה של cpu

שימוש ב gpu לצורך ביצוע חישובים לא החל ב directx 11 אלא כבר ב directx 9 ישנן הרבה אפליקציות שמשתמשות בgpu .יחד עם זאת השימוש בgpu שלא לצורכי גרפיקה לא היה דבר פשוט לכן נוצרו מעטפות כדוגמת Cuda שהינה המפורסמת מבניהם שאפשרה לכתוב ב c מספר פונקציות ו cuda דאגה לבצע את ההמרה לצורך הרצה על הgpu .
כיום בשוק קיימים מספר sdk לצורך ביצוע חישובים באמצעות ה gpu כדוגמת DirectCompute, CUDA, CAL, OpenCL, LRB Native
אנו בחרנו ב DirectCompute מבין כולם מהסיבה של הקרבה ל Direct3D ו לhlsl שהינן טכנולוגיות בהן אנו מנוסים ומכירים אותם .
ה GPU מורכב מ 32 Hyper threaded cores אשר יוצרים 1000 יחידות של ALUs כל יחידה כזו יכולה להריץ במקביל 100 threads לכן ה GPU יכול להגיע ל 100000 Threads שרצים במקביל בניגוד לכמות של thread ים בודדים (בהתאם למספר ה cpu ים) שיכולים לרוץ על Cpu ים במקביל .המטרה של התכנת הינה לכתוב את האלגוריתם כך שמספר ה thread הרב ינוצלו .
Hello world
בתוכנית של ה Hello world שלנו אני מעונין להדגים שימוש ב Direct Compute לצורך עיבוד תמונה .
הבסיס לתכונית שלנו הינה סדרת המאמרים בנושא עיבוד תמונה שנמצאת כאן. בסדרה הזו נעשה העיבוד באמצעות ה Cpu ואני העביר את העיבוד של התמונה ל GPU .


אנו מתחיל ב Filter הפשוט ביותר GrayScale כאשר את הקוד של העיבוד תמונה שמשתמש ב CPU כרגע יוחלף בקוד שיפעיל את ה GPU לצורך זה .

כמובן שאין לזה שום יתרון להמיר את ה קוד ל DirectCompute והמטרה שלנו הינה לימוד המטרה שלנו הינה לימוד הטכנולוגיה .
שלב 1 בניית ה Demo
אחרי הורדנו את הDemo המרנו אותו ל .net 4.0 אנו ניצור ++C ספריה של Cli אשר תבצע את הקריאות ל DirectCompute .

הספרייה שמבצעת את עיבוד התמונה ב GPU מורכבת מ 5 חלקים :
-
אתחול ה Direct compute טעינת ה Shader .
-
בניית ה Buffer של הנתונים והעתקת ה Bitmap ל Buffer של ה DirectCompute .
-
טעינתה Buffer ל Shader והרצת ה Shader .
-
ה shader שהינו הקוד שרץ ב GPU.
-
העתקת התמונה המעובדת מה Shader
איתחול ה Direct compute טעינת ה Shader .
קוד סטנדרטי שטוען את ה Directx dll מביא לנו את ה interface ID3D11Device , ID3D11DeviceContext של Directx11 .
טעינת הShader מתוך קובץ מתבצעת באמצעות קריאה לפונקציה : CreateComputeShader בדומה לטעינת shader עבור rendering pipeline .
בניית ה Buffer של הנתונים והעתקת ה Bitmap ל Buffer של ה DirectCompute.
Structured Buffers
לצורך העברת הנתונים ל GPU אנו נשתמש ב structured buffer שהינו סוג חדש של buffer המאפשר קריאה וכתיבה של structs לתוכו באמצעות index בדומה למערך בשפה עלית .
ב Direct compute משתמשים בשני סוגי Resource views שהוגדרו ל DX11:
Unordered Access Views או בקיצור UAV
הינו סוג חדש של resource view המאפשר קריאה וכתיבה על ידי ה shader בו זמנית . ניתן לטעון עד 8 Uavs ל Shader בו זמנית הטעינה מתבצעת באמצעות פקודת : CSSetUnorderedAccessViews. ה Uav משמש בעיקר לצורך אגירת תוצאות החישובים ושימוש חוזר בהם אם יש צורך .
ב Shader ה UAV מיוצג על ידי RWStructuredBuffer
Structed resource view או בקיצור SRV
הינו ה resource view שמשמש אותנו לצורך וטעינת הimage לshader מיוצג ב Shader באמצעות : StructuredBuffer
ב Demo אנו מגדירים שני SRV ים יצירת ה Structured Buffers מתבצעת באמצעות הפקודה CreateStructuredBuffer

הנוהל CreateStructuredBuffer מוגדר כך

יש לשים לב הד לדגלים של ה BindFlag שמגדירים את ה Buffer כunordered access ולדגל של ה MiscFlag שמגדיר את ה Buffer כ Structed.
לצורך הדוגמא בלבד אנו עוברים ידנית על השורות והעמודות של ה Bitmap ומעתיקים אותן ל input strutactured buffer.
יצירת ה SRV

יצירת ה UAV

טעינתה Buffer ל Shader והרצת ה Shader
שלב ההרצה מתחלק ל 3 חלקים והוא מתבצע באמצעות ה DeviceContext
1.טעינת ה Srv , ה Uav ו הShader ל Device

2.הרצת הShader באמצעות קריאה לפונקציה Dispatch.הפרמטרים של הפונקציה הינם מייצגים את מספר ה Group ים שמוקצים לצורך הרצת ה Shader .

חישוב מספר הthread ים הכללי מבוצע בצורה הבאה


3.ניקוי הנתונים שנטענו .
ה Computed Shader
ה Shader מכיל את ה קוד שנטען ומורץ ב GPU בדומה לShader גרפי שרץ ב pipline Direct3D .מכיוון שאנו עובדים ב structed mode (רק לצורך הדוגמא !!! באפליקציה אמיתית רצוי במקרה של עיבוד תמונה או וידיאו להשתמש ב Row buffer ) אנו צריכים להגדיר את מבנה ה Struct של הנתונים :

אנו מגדירים שני Buffers עם מידע מסוג BGRBufType
1.לData נכנס :
StructuredBuffer<BGRBufType> Buffer0 : register(t0);
זהו Read only buffer המכיל את כל ה struct של התמונה שצריכה לעבור עיבוד .ניתן לפנות לכל אחד מה index ים שלו כמו ל array בצורה הבאה : [Buffer0[index.
ה Buffer משוייך ל Texture register מספר 0
2.ל Data יוצא :
RWStructuredBuffer<BGRBufType> BufferOut : register(u0);
זהו Read write buffer המכיל את כל ה Data היוצא מה Shader של ה image שעבר עיבוד .
ה Buffer משוייך לregister An unordered access resource מספר 0 .
void CSMain( uint3 DTid : SV_DispatchThreadID )
הינה ה פונקציה ה ראשית של ה Shader היא מקבלת כפרמטר את מספר ה thread הנוכחי שכרגע מורץ
Numthreads – הינו Attribute שמגדיר את מספר ה Threadים בכל group המוקצים לביצוע הפונקציה , ביחד עם הפרמטרים של פקודת dispatch אשר מריצה את הפונקציה ניתן לחשב כמה threadים בסה"כ ירוצו

יש לשים לב לפרמטר x של ה DTid הפרמטר הזה מייצג את מספר הthread שכרגע מורץ ,הן ה attribute והן הקריאה לפונקציית ה Dispatch של ה shader מכוונות לכך שהוא יכיל את ה range של 0 עד למספר ה pixel ים אותם יש לעבד .(הערך של theNUM_ELEMENTS )
העתקת התמונה המעובדת מה Shader
על מנת לקרא את הbuffer עם התמונה המעובדת מה GPU אנו צריכים להעתיק את ה UAV שעודכן על ידי ה Shader ל resource שה cpu יוכל לקרא ממנו הפעולה מתבצעת בצורה הבאה :


יוצרים Buffer עם Access flag לקריאה מה CPU ומעתיקים אליו את ה UAV .
לסיכום :
הצגתי כאן Hello world המדגים שימוש ב Computed shader לצורך עיבוד תמונה
הקוד ל Demo נמצא כאן יש לעדכן את המיקום של shader בהתאם למיקום במחשב .

מידע נוסף:
http://msdn.microsoft.com/en-us/library/windows/desktop/ff471566(v=vs.85).aspx
http://www.microsoftpdc.com/2009/P09-16
נשאלתי שאלה לגבי פרויקט גמר של תואר בלימודי מדעי המחשב בנושא גרפיקה תלת ממדית. השואלת רצתה לכתוב משחק בגרפיקה תלת ממדית כעבודת גמר והתענינה באיזה טכנולוגיה לבחור .כמו כן ביקשה הפניות לאתרים שיכניסו אותה לנושא .היא העלתה 2 אפשרויות : Direct3D או wpf תוך כדי ציון שהיא יודעת לתכנת ב #C .
כאשר בוחרים בטכנולוגיה לפרויקט הגמר צריכים להתחשב במספר גורמים:
- הזמן העומד לרשות הסטודנט בד"כ זה לא מישרה מלאה עם שעות נוספות לחצי שנה
- הידע בתכנות שהסטודנט בא איתו
- הידע בתלת מימד שבד"כ מסתכם בקורס או סמינריון
- העובדה שבנוסף לחלק של ה render הגרפי יש גם עבודה בחלק הלוגי של המשחק .
השוואה בין טכנולוגיות :
לפי דעתי אפשר לשקול גם את OpenGL והן XNA .



DirectX
הינה הטכנולוגיה החזקה מבין כול שאר האפשריות אבל כמו שלא נלמד לטוס שעורים ראשונים על F22 Raptor כך להתחיל ללמוד את הטכנולוגיה המורכבת הזו לצורך עבודת גמר זו קפיצה מאוד גדולה .Directx עבודת רק על windows .אין בה fixed pipline והתכנת מחויב לקפוץ ל Shaders מהHello world הראשון כלומר ללמוד hlsl עם כל ההתעסקות ב semantics וכד .DXUT - הינה מעטפת שלא מומלץ להתקרב אליה וכמו כן אין תמיכה ב format ים סטנדרטים של meshes כגון : obj .ב Directx12 הבנתי שהנקודה הזו תשתפר . Directx הינה c++ oriented ונסיון לכתוב בה בשפות אחרות זה די שבירת שיניים .מבחינת גירסאות כאן הבחירה הינה בין שתיים : 9 או 11 כאשר 9 הינה הפשוטה ותומכת ב fixed pipeline
OpenGL
אינני מכיר לעומק (כפי שאני מכיר את DirectX סה"כ ביצעתי בה שני פרויקטים בינוניים ) מההכרות שלי לפי דעתי היא יותר נוקשה מ DirectX ברם היתרון שלה שבניגוד לשאר הטכנולוגית היא איננה Microsoft oriented אלא מאוד cross platform ניתן לכתוב ל android , Linux , windows .
OpenGL איננה C++ oriented אלא אפשר לכתוב בה במגוון שפות python ,java c++ etc. ל openGL מספר גירסאות לא כולם נתמכות בכל ה device ים הגירסה האחרונה OpenGL 4.2 מכילה feathers מתקדמים כגון tessealtion וכד
WPF
לפי דעתי מתאים יותר לכתיבה של גרפיים תלת ממדיים אין גישה ל vertex shader .
XNA
בהרבה יותר פשוטה מה DirectX ,ובהרבה יותר מוגבלת היא תואמת את Directx 9 יחד עם זאת features מתקדמים של directx לא בטוח שהינם הכרחיים לעבודת גמר . ניתן לכתוב ב C# והיא Cross platform ל Microsoft .כמו כן היא נותנת תבנית ראשונית לכתיבת משחק .יש המון דוגמאות ברשת שמהם אפשר להצעיד את המשחק .לכן המלצתי לה על הטכנולוגיה הזו .
Renderscript
מהכרות ברמה של קריאת מאמר פה ושם עובדת הן על ios והן על android בשפת תיכנות C
שיקול נוסף וחשוב הינה תמיכה של Xna בניגון מוזיקה ובקלט של joystick ו keyboard בטכנולוגיות 3Dאחרות יהיה צורך לשלב את החלק הזה מטכנולוגיות אחרות .
ידע בסיסי נדרש
אלגברה
בכל הטכנולוגיות שהוזכרו על מנת לכתוב תוכנה בסיסית יש צורך בידע מתמטי מהתחום של אלגברה ליניארית ,לא יותר מאשר קורס אלגברה ליניארית I ,כמובן שיש טכנולוגיות כגון prt שדורשות ידע מתמטי יותר נרחב שוב לא נראה לי שמסגרת עבדות גמר צריך להגיע אליהן .
כפל מטריצות וקטורים , מטריצת יחידה מטריצה הופכית משטחים וכד
בהחלט יכולים להספיק
תאוריה בנושא גרפיקה ממוחשבת
נדרשת הבנה בסיסית של תחום הגרפיקה התלת ממדית: מושגים כמו
World , view , projection
Transformations
Lights and texture .
תיכנות וארכיטקטורה
לא נדרש יותר מידע בינוני + בתכנות בשפה שהטכנולוגיה קשורה אליה , הצלחת פרויקט גמר איננה תלויה בערך חד חד ערכי אם התכנת עבד לפי כללי solid נוקשים .רוב הטכנולוגיות המורכבות כגון thread ים וכד אין להם שימוש במשחק פשוט שבד"כ חי ב rendering thread .
פיזיקה וAI
אין צורך להיות מדען גרעין של אחמדניגאד ברם צריך להכיר נוסחאות של חוקים בסיסים כגון כח המשיכה , מתקף ותנע וכד .
AI צריך לדעת ברמה בסיסית אם בכלל .
חומרה
ניתן למצא כרטיסי מסך זולים מאוד שתומכים בגרסאות המעודכנות ביותר
Resources
נדרש אוסף של resources כגון meshes , Textures אותו יש ללקט ברשת ,לחפש בתיקיות של ממשחקים של ילדים יצירת 3D Resource הינה דבר מורכב המצריך ידע ב Blender או כל תוכנה אחרת חבל על הזמן כאן במקרה של פרויקט גמר ,כעקרון עם ה pix החדש אפשר להעתיק Resource ,מכיוון שעבודת הגמר איננה פרויקט מסחרי הדבר יותר פשוט.
לינקים
בלוק עם סדרת לימוד ל XNA
קישור ל msdn בנושא xna
אשמח להמשיך לקבל שאלות בתחום של directx ואשתדל לענות עליהן במסגרת הבלוג .
יצא לי פעם לעבוד כתכנת על פרויקט IT במוסד פיננסי כלשהו. למערכת היה ארכיטקטורה מעולה מכל הבחינות: ביצועים , אבטחת מידע , איכות התצוגה עם גרפים תלת ממדים סוף הדרך .אבל כשבאנו להציג בפעם הראשונה את המערכת ללקוח הסתכלו עלינו במבט שהזכיר לי את הקטע בו הרבנים מסתכלים על המורה לבר מצווה של אלכס קופרובסקי והוא לא מבין מה לא בסדר ושואל :"הציצית שלי לא כשרה ? " . גם אנחנו רצינו לשאול האם הצל של הגרף התלת מימדי לא נראה ראלי ? כי אם זה הבעיה אפשר להשתמש ב cascaded shadow maps .
מה שקרה זה שהגרפים והטבלאות שהיו יפיפיים הראו נתונים של שטויות במיץ עגבניות , לא עניין את הלקוח אם השתמשנו ב decorator , אם הארכיטקטורה עונה על כל העקרונות של srp , ocp ,lsp בצורה מושלמת ,מצדו שהכל ייכתב ב method אחד וכל הלוגיקה תופרד באמצעות goto חו"ח שלא נדע מוטמע .אבל עם נתונים כמו שהמערכת מציגה אי אפשר לעשות כלום .
אם כבר דיברנו על הצל אז השיטה הפשוטה של יצירת צל עם stencil buffer לדוגמא יוצרת רקע שחור בקטע שמקור האור נחסם על ידי mesh יוצרת צל לא מציאותי
בתמונה צל לא ראלי

במציאות צל ממש לא מורכב מ rgb 0, 0 , 0 אחיד אלא יש התכנסות של דעיכת לעבר מרכז הצל .

הבעיה שאנשי התוכנה אינם מכירים את ה domain של הפרויקט הינה בעיה שקימת בהרבה מאוד פרויקטים.
ברוב הפרויקטים בהם השתתפתי לצוות התוכנה היה ידע מזערי או בכלל לא היה ב מרחב הבעיה .אם ניקח את כל התכנתים שהשתפו בפרויקטים מעולם הביטוח בארץ אני כמעט בטוח ש95% מהם לא יודעים מה ההבדל בין פיצוי לשיפוי .(גם אני בעונותיי הגדולים לא הצלחתי לקלוט את זה למרות שהסבירו לי מיליון פעם)
כמה אנשי התוכנה יכולים להתקרב ל הבנה של ה domain במסגרת תהליך ה design והפיתוח .האם בסוף הפרויקט שעוסק ב תחום של כירורגיה יוכל כל אחד מהתכנתים להתחיל לצאת ב 4 מהעבודה לצורך השלמת הכנסה ככירורג בכיר באחד מבתי החולים הפרטיים ?
אחד ה patterns הקבועים של ישיבות בהן מוצגות הדרישות של פרויקט הינו התפרצות של תכנת שאומר הדרישה הזו זה בדיוק מה שהטכנולוגיה הזו והזו עונה (כלומר הטכנולוגיה החדשה שאתמול שמעתי עליה ואני מה זה רוצה שהיא תקשט את קורות החיים שלי ) .בד"כ הדובר גורם לתזוזה של הדיון מנושאי ה דרישות מעולם ה domain כגון ה use case או ה use story , מהם המושגים המקצועיים מעולם ה domain העיקריים לדיון טכנולוגי .
הניסיון לגרור את התהליך בהו מוצג ה domain של הבעיה לישיבות טכנולוגיות לפי דעתי פוגע ב Design של הפרויקט ובהמשך בהצלחת הפרויקט כולו .
נכון לפעמים קיים לפרויקט מחסום טכנולוגי דרישה לקחת סרט וידיאו באורך מלא לפרוס אותו להוסיף water mark ולקבצו בחזרה בכמה שניות על חומרה של מחשב ממוצא הינה משימה בלתי אפשרית .ומחסומים כאילו צריכים לאתר כמה שיותר מוקדם בחייו של פרויקט אבל מהמקרה הזה לדיון האם נשתמש ב task או threadpool או ביכולות הasync החדשות של C# 5 במהלך דיון דרישות של הפרויקט המרחק עצום ורב .
מספר הפרויקטים שצריכים להיפסל על הסף בגלל אי קימות של טכנולוגיה הוא לא גבוה במיוחד כנ"ל לגבי מספר הפרויקטים שנפלו עקב הגעה למבוי סתום מבחינה טכנולוגית.אני לא מדבר על בחירה טכנולוגית לא מתאימה אלא על אי אפשרות טוטלית למימוש הפרויקט .
דוגמא לכך לפי דעתי אפשר לראות מה דרישות של FIFA2012 שהתוכנה רצה על Directx9

כמובן שאם FIFA2012 היה נכתב תוך כדי שימוש ב directx11 היה אפשר להשיג חווית משתמש בהרבה יותר טובה בוודאות .אבל בל נשכח שהמשחק הכי פופולרי בימנו Angry Birds יכל עם קצת רצון וקצת יכולת להיכתב ל Commador 64 עם pick ו pokes !!!.
כך שהסוגיה הטכנולוגית זו לפי דעתי איננה מהווה Risk על פרויקט בחלק ניכר מהמקרים.לאומת זאת הבנה של ה Domain ומרחב הבעיה של הפרויקט הינו דבר קריטי להצלחת הפרויקט .
בכול מקרה המטרה בתהליך התהוותו של פרויקט היא להבין את ה Domain לקרב את אנשי הארכיטקטורה והקידוד למרחב הבעיה ולא לקרב את מומחי ה domain ל xml ,soap , json ,rest .
שלושה סוגים של מומחי domain קיימים :
עמית מעולם מתוכנה
בד"כ ממחלקת professional services עולם הDomain שלו זה עולם התוכנה בד"כ הוא ירצה עוד פרמטר ב log שמאפשר רישום של הstack או משהו כזה .
מומחה domain
בעל ידע רב ב domain עצמו וללא כל ידע בעולם המחשבים .
וזה שבאמצע
שבא מעולם האלקטרוניקה עם אוריינטציה תכנותית אילו הבעייתיים כי הם גוררים אותך בעצמם לעולם הטכנולוגיה .פעם יצא לי להשתתף בדיון התנעה של driver לכרטיס ל PC אנשי האלקטרוניקה כבר באו עם ידע מהבית ש windows זה מערכת שאיננה real time לכן חייבים להשתמש בתוכנה שעוטפת את הwindows ,ומאפשרת ל windows להתנהג המערכת real time . לקח די הרבה זמן לשכנע אותם שלדרישות של הכרטיס שלהם אין בזה צורך .לעמיתם האילו שלי במיוחד אילו איתם אני עובד עכשיו אני בד"כ ממליץ על החומר הבא בנושא ה domain.
הידע וההבנה של ה domain הינו קריטי במהלך הארכיטקטורה . ניקח לדוגמא מערכת של רובוט לשטיפת רצפות ארכיטקטורת התוכנה תראה שונה לגמרי אם הראש של השטיפה אפשרי להחלפה באחד אחר עם פונקציות אחרות תוך כדי פעולה , בלתי אפשרי להחליפו בכלל והינו חלק אינטגרלי מהמערכת אפשרי להחליפו רק ב off line האם יש סיכוי שבעתיד יהיה מספר ראשים על אותו הרובוט או שאין לזה בכלל משמעות .
גם בעולם ה IT יכול להיות מערכת סגורה לחלוטין שהינה בעלת דרישות security גבוהות מאשר מערכת אשר מותקנת וחשופה ל web עקב זה שהמערכת מותקנת ברשת שחלק ממשתמשיה אינם מורשים ובעל רצון להתעסק במערכת ולא מפריד בינם למערכת ל fire wall ואף אחד מהם הוא לא guest אלא users ו power users .
בכל מקרה המטרה הינה לאפשר את תקופת הלימוד הזו של הdomain נטו לעזוב את הטכנולוגיה בצד .להתערבב בעולם ה Domain .גם מבחינה אישית זה מעשיר אותך וגם תוצאות הפרויקט נראות שונות לגמרי .
Rendering של Frame המבצע הרבה קריאות לכרטיס המסך הינו בעייתי מבחינת ביצועים .אחד המקרים שמצב כזה קורה הינו Render של scene המכיל הרבה מאוד פרטים די דומים שאינם מאוגדים בmesh .
דוגמאות ל scene כזה הינם :
- עץ עם הרבה עלים מתנפנפים ,
- שדה של שיבולים
- קהל של קבוצת כדור רגל .
- שער ארוך שהמצלמה קרובה אליו .

התמונה לקוחה מ http://software.intel.com/en-us/articles/rendering-grass-with-instancing-in-directx-10/
הסיבה לבעייתיות הינה שעבור כל פרט לדוגמא שיבולת בשדה צריך לבצע מספר קריאות לכרטיס מסך , הדבר משפיע על מספר ה context switching ועל רוחב הפס .
הטכנולוגיה של Instancing נוצרה על מנת למנוע את ה chatty talk מול הכרטיס מסך .כאשר יש לנו הרבה פרטים די זהים .
אצלנו במערכת יש משהו די דומה לצבא של נמלים (אלפי נמלים ) שנעות על הקרקע כל נמלה בכיוון ובקצב שלה .נמלה מוגדרת באמצעות skin mesh .לכל vertex ב mesh של הנמלה משוייכים index ים של bones ,עבור כל bone יש פאקטור של bone weight שבאמצעות שינוי יוצרים את התנועה של הראש המפרקים והגוף של הנמלה .
למרות שהקונספט של instancing הינו ישן גם כיום עם cpu וכרטיס מסך מתקדמים 1000 קריאות כל frame לעדכן את ה weight של ה bones , את מיקום וכיוון הנמלה , ערכי texture שונים הינה פעולה שמכבידה במידה מסוימת על ה rendering thread .
באמצעות טכנולוגיה ה Instancing מתאפשר לנו בקריאת draw אחת לבצע עדכון של כל ה 1000 meshs.
דוגמא מאוד בסיסית שמסבירה את העיקרון של הטכנולוגיה אפשר למצא כאן.הדוגמא מציגה 4 משולשים על המסך אשר נוצרו באמצעות instancing של משולש אחד .

אם נסתכל על ה vertex ים לאורך ה pipeline של Directx באמצעות pix נוכל לראות את הדבר הבא :

ל pipline נכנס Vertex Buffer המכיל בדוגמא 3 vertex ים בלבד .במקרה שבפקודת ה Draw אנו משתמשים ב DrawInstance ה Input assembler stage ב DirectX pipeline מתחיל את ה תהליך של עיבוד ה buffer כמספר ה instance ים שצוין בפקודה .
deviceContext->DrawInstanced(vertexCount, instanceCount, 0, 0);
כלומר אם instanceCount בדוגמא הינו 4 ה pipeline ירוץ 4 פעמים שונות עם אותם הנתונים בכניסה .

ה Vertex Buffer שנכנס ל Input assembler stage מחולק ל 2 חלקים :
חלק 1 אשר נכנס ל slot 0 מכיל מידע המשותף לכל ה instance ים .בד"כ המידע הזה מכיל את ה Vertex , Texture , normal וכד עובר כל הפוליגון ופוליגון המשתתף ב מקטע של ה Rendering
חלק 2 אשר נכנס ל Slot 0 הינו מידע מבדל עבור כל instance ו instance בפעולת הDraw .
D3D11_INPUT_ELEMENT_DESC שמכיל את ה instance data מאופיין על ידי :
InstanceDataStepRate – מגדיר בכמה לקדם את ה index של ה buffer בכל instance
InputSlot – המידע נכנס ל slot 1
SemanticName – ה Demo מגדיר את ה instance buffer כ texture buffer שמכיל ה vector של מיקום עבור כל Instance ו instance .
הקוד שמגדיר את מבנה ה D3D11_INPUT_ELEMENT_DESC עבור ה instance rendering



ב Vertex shader ה Demo משנה את המיקום של כל vertex על פי ערכי ה vector שמשויך ל instance הספציפי.

פעולות נוספות עם Instancing.
ניתן לנתב את פעולת ה shader באמצעות מידע של ה instance לדוגמא אפקט שונה ברמה של קוד שונה שמתבצע לכל אחד מה instance ים.
Texture שונה עבור כל אחד מה instance ים
הערך של bone weight factor שונה עבור כל instance הדבר מאפשר ליצור בדוגמא עם הנמלים אפשרות שהנמלים לא ילכו בדיוק אותן הצעדים אלא כל נמלה תהייה לה את הקצב והמופע שלה של צעידה
לסיכום
Instancing הינה טכנולוגיה של DirectXהמפאשרת לרנדר מספר גדול של אלמנטים דומים באמצעות קריאת Draw אחת .
רצוי למנוע את הכמות הגדולה של האלמנטים ב render כבר בשיכבה של ה cpu על ידי frustom culling , או פעולה לוגית אחרת הקוטמת אלמנטים שבטוח שלא יוצגו .
באחד מה Code reviews שערכתי השבוע בפרויקט שעובד במתודולוגית mvvm נתקלתי בקשירת Command ל Command property של ToggleButton.
לפי עניות דעתי ה Command property של ToggleButton מעודד באופן בילתי מודע את ההפרה של עקרון ה SRP — The Single Responsibility Principle שאומר שלכל class צריך להיות תפקיד אחד בלבד .
הclass שממש את ה ICommand צריך לטפל בשני מצבים שונים :מצב אחד שהמשתמש מבצע checked ומצב שני שהמשתמש מבצע unchecked של ה control .טיפול לוגי שיכול להיות מאוד שונה ולא קשור .
ניקח לדוגמא ToggleButton אשר בלחיצה פנימה הוא צריך להתחיל תהליך של הפעלת מזגן , בלחיצה החוצה ה ToggleButton שלנו אמור לכבות את המזגן , שתי פעולות שאולי קשורות מבחינת תפעולית זו לזו ולכן ב שיכבת ה UI הצמידו אותן זו לזו אבל מבחינה לוגית הן שונות: בהדלקה צריך לבדוק שעברו 5 דקות מהפעם הקודמת ,להתחיל תהליך הדרגתי ומורכב בעוד בכיבוי מנתקים את זרם ,עושים רעש של פאופ וזהו .
אם משתמשים ב Command אחד אשר מושם ב Command Property של ה ToggleButton כאשר נלחץ או משחורר הכפתור קודם כל נקראת הCanExecute אשר מחליטה האם מותר לוגית לבצע את הפקודה אם כן נקראת Execute .
בשתי ה methods האילו דבר ראשון צריך להבין האם ה Command נקרא כתוצאה מלחיצה או שחרור ,חיפוש מידע שמסבך את הקוד ובד"כ גורם לבאגים כי מקור המידע לא תמיד קשור לכפתור והסתמכות על state של השכבה הלוגית לא תמיד תואם את המצד שב UI .(דוגמא לכך שלחצו על הכפתור והמזגן לא נדלק ובכול זאת הכפתור נשאר ב ON)
יש כאן 3 פעולות שונות אשר מוכלות ב class אחד:
- בדיקת המצב של המנוע לפני הפקודה
- כיבוי
- הדלקה .

הפתרון לפי דעתי זה לא להשתמש ב Command property הקיים אלא לפצל את ה property של ה ToggleButton לשני Commands properties שונים.
אחד שיקשור אליו את הCommand של פעולת ה Checked והשני של פעולת ה Unchecked.
בקוד דוגמא מוגדר ה Dependency property על פעולת ה Unchecked

ב xaml יש לקשור את ה Command היעודי ל attached property שיצרנו

כעת כל Command שנקשר ב property מכוון לטיפול בפעולה לוגית אחת .

אחד האתגרים בתכנות מנוע תלת מימד זה בנוסף ל לדעת מה להכניס לכרטיס מסך זה לדעת מה לא להכניס לכרטיס המסך.
אל אף מהירותו וריבוי ה מעבדים ב GPU תחולת עיבוד גדולה תגרום להשהיות של ה Rendering thread כך שאיכות המוצג תיפגע עקב קצב רינדור נמוך (מספר frames לשנייה ).
אם נבחן scene מתוך ה Demo של SubDMesh

נראה שלצייר Scene בודד לוקח כ 50mSec .
בGraph שה Pix נותן לנו ניתן לראות היכן הרוב המאמץ והזמן עיבוד של ה GPU לוקח .כמובן בפקודת Render (מה שמיוצג על ידי החורים הלבנים ) בפרוט ה פקודות ניתן לראות את כמות ה Vertex ים הגדולה שכל פקודת Draw צריכה לטפל כמובן שהכמות מוכפלת עקב זה שה Demo משתמש ב Tesselation.
בכל מקרה בחרתי ב Patch divisions נמוך יחסית על מנת לא להכביד על ה GPUעל ידי יצירת הרבה Vertex חדשים כתוצאה מה Tessellation.


ב Scene שהודגם היה סך הכל חייל אחד מסכן שאני חושב שאפילו משרת אומנם בגבורה אבל בקריה. ב Sceneים אמיתיים כמות הMeshs בהרבה יותר גדולה .
כל Vertex , index שנכנסים לכרטיס מסך עוברים עיבוד דרך כל ה Shaders הפעילים ברגע הזנתם , רק בסוף ה ה Pipeline מתבצע ה Cliping של ה Data שאיננו מוצג .

ה RasterizerState קוצץ את כל ה pixels שלא נכנסים לאותו מלבן ש החלון שמוצג כלומר אם יש לנו scene שמורכב מאצטדיון כדור רגל שלם וכרגע רואים במסך רק שחקן אחד , רק ה pixel של אותו שחקן יגיעו ל pixel shader .
ה Z- test לסוגיהם מתבצעים בשלב יותר מאוחר כלומר אם אני מרדנר קריין חדשות מאחורי שולחן ל pixel shader יגיעו גם ה pixel ים של ה שולחן וגם של המכנסים הקצרים שלו (במקרה הטוב ) שלא נראים כלל .
כאן ללא כל קשר לטכנולוגיה שבה משתמשים בא התפקיד של התכנת למנוע ככול הניתן הגעה של vertex ים ו index ים שלא יגיעו לידי רינדור .כמובן גם פעולות אילו אשר מתבצעות ב Cpu יש להן מחיר מבחינת ביצועים ,אולי חלק מהן ניתן לבצע ב Thread אחר בכל אופן לבדוק בכל Frame כל vertex לפני שהוא מגיע לכרטיס מסך האם הוא Visibility זה דבר בלתי ישים לחלוטין !!!
LOD
מכיוון שאין שום סיבה שבתחום של 3X3 פיקסלים בו אמור להופיע מטוס קרב מרוחק ירונדר F22 עם מיליון Vertex ים נוצר מנגנון של LOD שמהותו לכל mesh שיכול להיות מרונדר ישנם מספר גרסאות שנטענות לכרטיס מסך בהתאם לקירבה של ה mesh לצופה .

ב Direct3d11 ניתן לבצע lod יותר חלק באמצעות tesselation .
View Frustum Culling
viewing frustum הינו החלק בעולם שהינו נראה על ידי המצלמה .

ה View frustum מורכב מ 6 משטחים כאשר כל מה שנמצא בתוכו הינו נראה על ידי המצלמה.
המטרה של הבדיקה הינה לראות האם mesh או חלק מ mesh נמצא בתוך ה View frustum ואם כן אז להזרים את ה mesh לכרטיס מסך.
ישנה דוגמא מאוד טובה ומאוד מאוד מאוד פשוטה אשר נלקחה מקורס תכנות גרפיקה ממוחשבת לתלמידי מדעי החברה באחת מהמכללות .את הדוגמא אפשר למצא כאן.
הדוגמא מבצעת 3 פעולות מרכזיות במטרה להחליט אם אוביקט נימצא ב View frusum
1.הדוגמא מגדירה את 6 ה planes שמרכיבים את הצלעות של ה frustum של המצלמה .הקוד הינו קוד סטנדרטי ביותר שאין צורך להמציאו .
2.עבור כל סוג של אוביקט תלת מימדי הדוגמא מגדירה פונקציה שיודעת לחשב האם האוביקט נחתך \ מוכל בתוך ה frustum או נמצא מחוצה לו
דוגמא לפונקציה כזו עבור כדור הינה :

3.הדוגמא מרנדרת רק את האובייקטים שנמצאים בתוך ה View frustum או נחתכים אייתו.
מכיוון ש mesh הינו צורה יותר מורכבת מכדור או תיבה ואם נתחיל לבדוק כל vertex ב mesh כל Frame הcpu יחנק לנו בד"כ בונים תיבה חוסמת ל Mesh ובודקים האם היא נחתכת או נמצאת בתוך ה View frustum .

החישוב של ה מלבן החוסם את ה mesh יכול להיות מבוצע על ידי שימוש ב פונקציה D3DXComputeBoundingBox שהינה חלק מה Dx sdk .
לפני שנים רבות הייתה המלצה להשתמש בכדור חוסם במקום במלבן חוסם עקב זה שמספר הבדיקות של מלבן חוסם גדול פי 6 מאשר כדור ,היום בcpu ים המודרניים גם בדיקה של מאות מלבנים חוסמים לא מורגשת .
ניתן לבצע אופטימיזציות על החישוב ,רוב האופטמיזציות מכוונות למצבים בהם רק חלק מה mesh מופיע ב frustum . ניתן לחלק את ה mesh ל Quad trees (ב gpu gems בפרק של הterrain יש הסבר איך לעשות את זה ) כאשר מרנדרים terrain הדבר מאוד חיוני , יש מאות אלגוריתמים סטנדרטים לTerrain (דוגמא) לגבי mesh ים בודדים כדוגמת מטוס לא בטוח שהטרחה משתלמת.
כאשר ה meshהינו מסוג Hierarchical Bones Mesh ניתן לבצע את החישוב של איזה חלק מה mesh יכנס ל frustum באופן רקורסיבי .

וכל זה הקדמה לבאג שהופיע אצלנו לפני כשבועיים :
כאשר היו מטיילים עם המצלמה לאורך אחד הצירים היו מבחינים במעין קפיצה של mesh ים קרובים כאשר ה Mesh היה אמור להיכנס לתמונה ,מאין השהייה של כמה מאות מילי רק ל Mesh ואז בבת אחת חלק ממנו הופיע הוא המשיך להיכנס חלק לScene באופן חלק .
לאחר חיפוש הסתבר לנו הדבר הבא :
מכיוון שה Mesh היה מרונדר באמצעות Tessellation map ו Tessellation maps בניגוד לכל ה Maps לסוגיהם (Light , hights וכד) בעל יכולת הרחבה של ה mesh מעבר למימדים שלו . ההרחבה בוצעה רק בGPU וכאשר חישבנו את ה frustum culling ב CPU הדרנו את הMesh למרות שחלק ממנו כבר היה צריך להיות בתמונה .
הפתרון מבחינתנו היה לתת למנגנון ה frustum culling מקדם עבור כל mesh שבאמצעות הfrustum מאין מוגדל עבור mesh ים מסוימים בקרבה ל מסך .
אם נביט בתמונה הבאה אפשר לראות שיש חלק שלם של מים ליד הגלגלים שנוצר על ידי מנגנון ה Tessellation ב gpu ואיננו נכלל בחישוב של ה frustum culling המתבצע ב cpu .

הדרישה
באחד מהפרויקטים עליהם עבדתי בעת האחרונה היינו צריכים לבצע ארכיטקטורה של שכבת DAL עבור אפליקציה שמכילה מספר רב של סוגים של ישויות מידע שונות ביילתי קשורות או תלויות זה בזו כמעט לחלוטין .כלומר לפעולות join לסוגיהן בין סוגי ישויות המידע אין קשר לוגי .
על ישויות המידע השונות יש לבצע פעולות Cruds כתיבה חיפוש עידכון ממחיקה , Full text search וכד
כמות הפריטים בכל רשימה של ישויות מידע נעה בין בודדים לאלפיים ,כאשר דרישה נוספת זה שהסכמה של ישות מידע הינה דבר דינמי שיכול להשתנות עם הזמן .
ישויות המידע כתובות כ Poco objects בלתי תלויות כאשר ישות מידע יכול להיות מורכבת מ Class ראשי שמכיל מספר Classי משנה.

חשבנו לבחון אפשרות של שימוש ב NoSql Database מסוג Document כ Repository משום ששימוש ב RDBMS גם מצריך הרבה עבודה של יצירת הרבה מאוד טבלאות , שינוי סכמות דחופות בניית שיכבת dal מורכבת ואין הנאה מהיתרונות של RDBMS הן בגלל גודל יחסית קטן של הטבלאות והן בגלל שאין קשר בין הטבלאות .
(אפשרות נוספת שחשבנו עליה היתה שימוש ב code first של אחד מה Frameworks )
ה NoSql Document Db שבחרנו לבחון הינו RavenDB .
RavenDB הינו NoSql DataBase שכתוב ב net. ששומר מסמכים בתצורת JSON documents שתומך בטרנזקציות , RavenDB רץ כServer כאשר הממשק אליו הינו באמצעות שאילתות Linq . ניתן למצא פרטים על RavenDB הן באתר ,מאמר ב msdn וגם בסדרת פוסטים בנושא כאן וגם כאן.
היתרונות
-
פשטות התממשקות וכתיבת שיכבת הDal , Interface חושף RavenDB טוב שעונה על ההגדרה של Interface אידיאלי בחלק של פשוט מאוד לעשות איתו דברים פשוטים בחלק של אפשרי לעשות איתו דברים מורכבים אני לא כל כך הסתדרתי אולי זה נובע מחוסר תעוד .
-
פשטות הפעלה ותמיכה ב DB .
-
ביצועים מעולים בכל הפעולות שניסינו גם בכמות מידע שגדולה בכמה סדרי גודל ממה שאנו צריכים .
-
תמיכה מעולה של החברה גם בstackoverflow וגם בקבוצת הדיון .
-
מחיר יחסית זול ל RDBMS ים
6.ממשק ניהול נוח .המאפשר לבצע גיבויים לראות את הלוג , למחוק , לראות את כל הdocuments ב Database , ניהול index ים .
דוגמא למסך מתוך ממשק הניהול בו ניתן לראות את ה Documents השמורים , לבצע חיפוש להוסיף ידנית .

החסרונות:
-
כלי ניהול כדוגמת גיבויים אוטומטיים , שאילתות online לא מפותחים מספיק , למרות שלצרכים שלנו ניתן לתפור את הכלים די בקלות לפי הדוגמאות המסופקות עם החבילה .גיבויים ידניים המערכת מאפשרת לעשות באמצעות ממשק הניהול בקלות.
-
אפשרות הרחבה לעתיד של ה Storage לצורכי BI לדוגמא על מנת לשמור את כל הפעמים שהליך רץ ותוצאת הריצה וחיפושים ותובנות ,הדבר אפשרי אבל מהר מאוד זה מתחיל להיות יותר מסובך מאשר שימוש ב sql db שמיועד למטרות אילו.
-
חוסר תעוד רישמי למרות שאפשר למצא המון מזור באתרים כגון Stackoverflow וכד
-
חוסר בדוגמאות (תמיד חסר לי אולי ב WCF נתקלתי בכמות מספקת ).
-
נושא ה Security לא מפותח מספיק לצרכינו לטעמי .
-
תומך בעיקר בעולם ה net. דבר שבעיתי אצלנו בעיקר באפליקציות שכתובות בpython , java , php .
-
חסרה יכולת לשמור ערכים עם Referance מעגלי של אוביקט אחד לשני ובחזרה .
התקנה והרצה
לאחר שהורדנו את החבילה מהמרשתת על מנת להפעיל את הdb זה להפעיל את ה start batch file (דבר שפותח אוטומטית את ממשק הניהול הסילוורלאיטי ).
על מנת להתחיל לפתח יש לחבר מספר assemblies ל project להעתיק מספר שורות קוד ואתם בפנים .

Open DocumentStore and Session

הממשק מול ה RavenDB מורכב שני אובייקטים מרכזיים :
ה Docuemnt store שמכיל את הממשק ברמה של הDataBase .הוא עוטף את אפשרויות היצירה שלה Session , התחברות ל Db instance
שמירה של אוביקט חדש
השמירה מעדכנת את ה ID של האוביקט הנשמר כאשר מבנה הid אם הוא לא מסופק על ידי הצרכן מחולל בtemplate שמורכב משם הclass עם סיומת רבים + ה id החדש .(המערכת יודעת מבחינת אנגלית להוסיף נכון את הסיומת )

אין צריך להגדיר ב DB את ה Scema של האובייקט או את התיקיות הכל מוגדר אוטומטית.
עדכון אוביקט
על מנת לעדכן אובייקט ב Database יש לבצע את פעולת הStore עם האוביקט המעודכן כאשר ה ID מכוון לID שאותו אנו רוצים לעדכן ,כל שינוי גירסה מעדכן את הערך של ה etag ואת זמן העדכון .
שאילתות ל DB
כאמור שאילתות מתבצעות תוך כדי שימוש ב Linq .
RavenDB עבור כל שאילתה בונה index דינמי בהתאם לתוכנה של השאילתה למטרת החזרת התשובה ,לשאילתות שכיחות RavenDB בונה index סטאטי שמתעדכן ב thread נפרד ,כאן יש עניין של נעילות אם רוצים לקבל את המידע המעודכן ביותר לאותו הרגע .
דוגמה לשאילתה :

ניתן להגדיר מראש את השאילתות אשר ישמרו באופן סטאטי כל עוד לא מוחקים אותן :

http://www.gamlor.info/wordpress/2011/07/ravendb-queries-and-indexes/
שונות
RavenDB תומך ב Attachments של קבצים בינארים (Blobs) כדוגמת קבצי תמונה או אודיו .
ניתן להרחיב את החבילה הביסיסית של RavenDB על ידי Plagins .
יש אפשרות לבחור מנוע אחסון כ Embedded ואז הקריאות ל DB מתבצעות באופן לוקלי ולא דרך ה network , כמו כן ניתן להגדיר שהנתונים ישמרו באופן ארעי בזיכרון RunInMemory = true
שיכבת ה DAL
המטרות שלנו בארכיטקרטורה של שיכבת ה Dal הינן:
- כמה שיותר Decoupling משיכבת ה BLL כך שאם נצטרך מסיבה זו או אחרת להחליף את מנוע ה Storage הנגיעה בשיכבת ה BLL תהייה מינימלית .
- אין צורך שהמערכת תוכל לתמוך בשני מנועי אחסון שונים בשינוי קונפיגורציה .
- זמן עבודה בערך כ 50 שעות (בחיים האמתיים זמן עבודה הינו אלמנט די מכריע בתכנון ארכיטקטורה )
עקב זה שלא נראה שיהיה צורך שהמערכת תוכל לתמוך ביותר ממנוע אחסון אחד שכבת ה Dal יכולה להיות קונקרטית לטכנולוגיה ,וכאשר מסיבה זו או אחרת ירצו לתמוך במנוע חיפוש אחר יבצעו פעולת חציבה ורמונט כללי בassembly של הDAL.
Components diagram

ה Components מהם מורכבת שכבת ה Dal יכולים להיות מקובצים לפי מידת הקונקרטיות לטכנולוגיה כאילו שימחקו ויכתבו מחדש עכב שינוי טכנולוגית האחסון וכאילו שרק יעברו שינוי קל לצורך התאמתם לטכנולוגית אחסון אחרת.
השיכבה יכולה להיצרך ישירות או עלי ידי תפירתה בקלות כ Web Service.
Class diagram

בדיוק כפי שאני מעדיף ששכבת ה Dal תבצע שאילות SQL או תקרא ל Sps ככה אני מעדיף ששאילתות הlinq יהיו חלק משיכבת ה Dal ול BLL יחשף interface לוגי .


הנושא של ארכיטקטורת ה Data containers נושא בחובו דילמה האם להשתמש באותם DataContainersלאורך כל השכבות מה Dal עד ל UI , מה שגורם להם:
- להיות עטופים בattributes שונים שכל אחד קשור לשיכבה אחרת.
- ,לממש interface ים משכבות שונות
- ולדאוג ל Convertors לדוגמא לשיכבה של ה Dal לא מתאים member מסוג Bitmap אלא מערך של Bytes .
אפשרות שניה עבור כל שיכבה לייצר DataContainers משל עצמה כאשר אפשרות זו נושאת בחיבה עבודה של כתיבת Convertors שמציבים את הDataContainer של שיכבה אחת בשניה .
עבור כל DataEntity נכתב accessor קונקרטי שממש את ה interface הספציפי שהוגדר עבור אותו DataEntity .
פעולות משותפות הוגדרו בclass משותף

אלמנטים שמתפעלים את הRavenDB
CustomSerializators – שממירם לנו אובייקטים שלא ניתן אוטומטית להמיר ל JSON
DbOperationsAccessor – מכיל פעולות כמו מחיקת DB , רפליקציה שממומשת על ידנו , יצירת DB
RavenDbSessionWrapper שעוטף לנו את הקריאה גם ל Document Store , ל Init פניה ל configuration להביא את ה port ושם ה DB
RavenDbDataIndex – הגדרות של index ים סטאטים

מסקנות
ראשית נחשפנו לטכנולוגיה של NoSql Document DB .
העמדנו מערכת בזמן קצר יחסית עם ביצועים משביעי רצון .
חסר לנו הקטע של Security כנראה שהתרגלתי לאושר של sql server וכד הן מבחינת אפשרויות הlogins וה roles דברים שהיו חסרים לי כאן .
חסרה לנו התובנה כיצד לבצע שאילתה שמכילה Search על text למרות דוגמאות רבות לא הצלחנו בזאת .
בגירסה הבאה של .wcf 4.5 יכלל גם transport חדש שתפקידו לתמוך ב Udp .
הצורך ב udp transport היה אצלנו כבר כמה שנים הסיבות לכך היו :
- אסינכרוניות מלאה אין מצב ל Dead locks .
- תעבורת רשת יותר קטנה מאשר שאר ה transports חשוב כאשר יש תעבורה גדולה ורוחב פס קטן .
- ביצועים :זמן שליחת , קבלת הודעה בהרבה יותר טוב משאר סוגי ה transports .
בד"כ משתמשים ב udp transport במקרים בהם דרושה יכולת של שגר ושכח לדוגמא מקרר בסופר שמדווח כל פרק זמן על הסטאטוס שלו האם הדלתות פתוחות , הטמפרטורה וכד. לא מעניין את המקרר האם מישהו מאזין אליו , ולא מעניין את המאזין תוכן ההודעה הקודמת אם הוא כבר קיבל הודעה חדשה .

כאשר יש רק סוג אחד של הודעה שנשלחת באופן מחזורי ,אפשר לתהות מה ה Add in values ש Wcf נותן לנו על פני סתם המרה של האוביקט מידע למערך של bytes ושליחתו באמצעות udpclient .כאשר ה Contract נהייה מורכב ,ויש דרישות נוספות כגון security , Discovery , routing מתחילה להיות משמעות לשימוש ב wcf .
על מנת לספק את הדרישה הזו השתמשנו ב Udp transport שסופק כדוגמא ב sdk .הדוגמא מבחינת ה interface שהיא חושפת מאוד דומה לudp transport של 4.5 .
הדוגמא מספקת לנו udp transport channel כאשר ה Channel עצמו ממומש כ IInputChannel ו כ IOutputChannel.


החלקים המעניינים הינם כל הטיפול ב Socket ב Class ים האילו ב OutputChannel בפקודת Send אשר מבצעת סיראליזציה ל Message object וקוראת ל פקודת SendTo של ה Socket

ב input channel המימוש קצת יותר מורכב.
ב פקודת OnOpened של UdpChannelListener נוצר ונפתח udp socket עבור כל endpoint ש ה Service חושף.
לכל socket שנוצר מתחילים להאזין באופן אסינכרוני באמצעות הפונקציה ContinueReceiving .

כשמגיע הודעה חדשה ההודעה החדשה נדחפת לתוך Queue אשר תפקיד ה UdpInputChannel לקרא ממנו את ההודעות ולשלוח אותן ל Dispatcher .
בכוונה הדגשתי את הקטעים שקשורים ל udp socket שאר הקוד מטפל הן בהודעות סינכרוניות והן אסינכרוניות ומהווה בסיס מצוין לפי דעתי לכל צורך למימוש Transport channel החל מ Rs232 וכלה במנגנון שמפריח יונות דאר .
מכיוון שהדוגמה מתעסקת רק עם ה transport channel עדיין נשארים כל המנגנונים שלא קשורים באופן ישיר לתעבורה לדוגמא הEncoder ו הDecoder של ההודעה איזה encoder שרק תבחר או תממש כאן קביל, אפילו תקודד את ההודעה למורס אין שום קשר ל transport

ה encoder הינו class שיורש מ MessageEncoder ואפשר לצינו הן בקוד או בconfiguration .כאשר מגדירים את ה Binding בדוגמא משתמשים ב TextMessageEncodingBindingElement.
הדוגמא מדגימה איך ניתן לבנות את הChannel stack כך שיתקבלו תכונות שאינן אפשריות עקב האופי ה אסינכרוני של udp .
לדוגמא:
- פעולה סינכרונית הClient קורא ל Server וממתין לקבלת תשובה .
- ניהול wcf sessions
- Reliable messages - ה Client שולח הודעה ומחכה timeout עד שההודעה מתבצעת , יכול להיות מצב שה Client שלח את ההודעה כאשר ה Server בכלל למטה ואז כשהריצו את ה Server אם לא חלף לו ה timeout אז הפקודה תתבצע .
הDemo על מנת לתמוך בפעולות אילו מוסיף ל Channel stack את ה ReliableSessionBindingElement.
על מנת לתמוך ב Duplex channels ה demo מוסיף ל Channel stack את ה CompositeDuplexBindingElement.
גם תמיכה ב טרנזקציות אפשרית והיא מודגמת באמצעות הDemo של TransactionMessagePropertyUdpTransport
תמיכה ב hosting ב IIS מודגמת באמצעות ה Demo של UdpActivation.
אחד המקרים שצריך את התכונות האילו ב udp transport הינה כאשר משתמשים בudp כ Interoperability למערכת צד ג שהתעבורה אליה הינה udp והיא מתנהגת באופן סינכרוני .
יש לזכור שכאשר עובדים עם Relaible session מקבלים הפחתה ניכרת בביצועים .
חלק ניכר מ Frameworks ,ארכיטקטורות לסוגיהן כדוגמת MVVM , Spring , MF , מגדירות מסגרת שהארכיטקטורה של המערכת צריכה להתיישר על פיה.
לדוגמא ב mvvm הקשר בין ה modal ל view צריך להיות דרך ה modalview ופנייה ישירה מהview ל modal כדוגמת הירשמות לevent של click של button ומימוש ה event ב xaml.cs שוברת את הארכיטקטורה על אף שלכאורה הכול יעבוד.
לארכיטקטורה כזו מספר יתרונות שהמודגשת ביותר זו כמובן ה Decoupling בין השכבות כך שבעולם אידיאלי ניתן יהיה לשנות את ה view מבלי צורך לשנות את שיכבת ה UIL .בעולם האמיתי זה יותר בכיוון שלתכנת יהיה קצת יותר קל להבין את הקשר בין שיכבת ה UIל UILופחות מסובך לשנות את שיכבת הUIL כך שתתאים ל view החדש .
אחד היתרונות שלפי דעתי יותר משמעוותים שארכיטקטורה כזו נותנת זה אותו השלד של ארכיטקטורה שרק צריך לעטוף בשכבה כמה שיותר דקה של לוגיקה עסקית קונקרטית.
יותר פשוט לעשות ארכיטקטורה לפי כללים מסוימים לדוגמא בשכבת ui המבוססת על mvvm הארכיטקט יודע שהוא צריך להגדיר את ה modal ודרך זרימת הcruds אליו , הוא צריך להגדיר את הmodalview ואת ה forms ו ה user controls של הView .
עדיין יש מרחב התקשקשות ואין שבלונה קבועה אבל זה עדיף בהרבה ממצב בו מתחילים ממצב נקי לגמרי בשיא ההתלהבות וחושבים על פיתוח כל מיני תשתיות גנריות שישמשו גם לעתיד וישום כל מיני patterns תאורטיים והמערכת יוצאת מסורבלת וכל מה שחושבים לגבי העתיד זה מתי ניתן לזרוק את המערכת ולכתוב הכול מחדש .
החסרונות של אותה ארכיטקטורה המבוססת על framework
גמישות – דברים לא מורכבים בדרך כלל קל לעשותם עם אותו ה Framework דברים מורכבים לפעמים ההשקעה שצריך במערכת שמפותחת באמצעות framework גדולה אין ערוך מזו של עבודה ללא ה framework .
לדוגמא : מימוש GraphView של nodes במקום TreeView בMVVM .במצב ערום ללא framework הדבר מורכב אבל אפשרי , כאשר מנסים ליישם את זה באמצעות mvvm ומתחילים לחפש איך לרשת מ HierarchicalDataTemplate , איך לשנות שיהיה אפשר שchild יהיה משותף לכמה אבות כמות ההתעסקות בכיפוף הטכנולוגיה הינה אדירה. עד כי נראה כי ניתן להחליף לנזירים ממגדלי הנוי את העיסוק היום יומי .
אי תמיכה – מחר מיקרוסופט או חברה אחרת יכולים להחליט שהם מזיזים הצידה טכנולוגיה זו או אחרת ואז מה ? קוד לוגי שאיינו מסתמך על טכנולוגיה בהרבה יותר פשוט להעביר לדוגמא מ C# ל java מאשר workflow של wf לאיזשהיא טכנולוגיה מקבילה או לקוד .
Technology awareness – עצם הקישור לטכנולוגיה מגבילה למה שהטכנולוגיה יכולה להציעה לדוגמא אם השתמשנו ב mvvm שקשור ל wpf יהיה קשה לנו להכניס טופס ישן מבוסס על winforms.
מוטיבציה
ברגעי משבר שאתה מתחרט שלא שמעת בקול של הוריך שרצו שתהייה מציל בחוף שרתון ואתה התעקשת ללכת לעבוד בהי טק כי יש שמה מכונות קפה שוות בא לך לעשות double click על כפתור בEditor של ה טופס וליצור event ולממש שם את התוכן שלך בקצרה במקום להתחיל להגדיר commands ו bindings ו לשים את הcommand ב context .
שימוש ב Framework כזה או אחר דומה לטיול מאורגן לחו"ל מאוד נוח רואים יותר דברים לא מבזבזים זמן על התעסקות בדברים אדמינסטרטיבים אבל בכל זאת מפסידים משהו מהחוויה.
WF Framework
WF לא נותן יכולת טכנולוגית שלא היתה קיימת קודם או שהיה צריך להתאמץ מאוד על מנת לישמה נהפוך הוא הרבה מאוד פעמים ליישם בקוד דברים יותר פשוט מאשר מwf .
WF נותן מסגרת למימוש תהליך עסקי , מה שחוסך צורך בהגדרת ארכיטקטורה אם אפשר להתיישר לפי wf .
אחד השימושים שאנו משתמשים בטכנולוגיה זו הינה יצירה דינמית של bl workflows .
דוגמא דומה לישום שלנו הינה מכונה שיודעת להכין עוגות לבד בהתאם למתכון שהוזן על ידי המשתמש . (כמובן שHighlight שלה זה עוגת שוקולד עם ספותה שחורה ).
השף מגדיר באמצעות מסך גרפי את התהליך של אפיית העוגה הוא מגדיר חומרים לעוגה , תהליכים באפיה בדיקה עם קיסם עץ אוטומטי האם היא אפויה וכד.בסיום התכנון השף שומר את התוכנית בבסיס נתונים ,התוכנית תשמש במכונה לצורך הרצת תהליך האפייה .
למרות ש wf תומך ומעודד שימוש בDesigner שלו לצרכי בניית ה Wf (דוגמא DesignerRehosting בsdk ) מנסיון שלנו לא טוב לנסות קשור בין ה Ui של ה wf editor ל UI של משתמש הקצה אלא להתאים את הui לuse case של המשתמש ולא לטכנולוגיה של שיכבת הBLL .כמו שלא ניתן לשף מסך לעשות joins בין טבלאות למרות שאפשר להציג את זה בצורה מאוד נוחה למשתמש .
מה גם שהeditor של wf עדיין לא מפותח דיו ואיננו מאפשר קסטומיזציה טובה .לדוגמא בגירסה הנוכחית הוא אינו תומך בcustom activity designer באפשרות להכניס control אחר מאשר edit box לדוגמא combobox ולאפיין את הקישור אליו.(כפי שהבנתי בגרסה הבא מיקרוסופט אפשרו את היכולת הזו ).
המערכת אצל המשתמש מפיקה את ה workflow באופן דינמי ושומרת אותו בבסיס נתונים.
יתרונות של הארכיטקטורה הינן :
הארכיטקטורה מפרידה תהליך מאוד מורכב של קליטת נתונים ממפעיל והפעלת מכונה עם אותם נתונים לשני חלקים :
חלק סטאטי שהמכונה בזמנה החופשי מייצרת workflow על פי הנתונים של המפעיל . כאשר הworkflow שמיוצר הינו מינימליסטי ומאוד קונקרטי לטובת תהליך אחד בלבד.
חלק דינמי שהוא יותר real time שמריץ את הworkflow שיוצר מראש .
תהליך דביגאבילי :
ניתן להשתמש בכלים שה wf חושף בצורה מיטבית הן להציג את התוכנית לפני הריצה והן תוך כדי ריצה לראות את התקדמות שלה .

לWF ישנן יכולות tracking מאוד טובות שהינן Build in .
שימוש ביכולות הWF מובנות של
כדוגמת :
Cancelation ו ב Compensation
אם באמצע תהליך אפיית העוגה מסיבה מסוימת מחליט האופה לבטל את התהליך או שלוגיקת התוכנית עצמה קוראת ל Cancel
יכולות אילו נותנות אפשרות להגדיר Activities אשר ירוצו הן במקרה שActivity הסתיים בהצלחה ובהמשך הריצה נקרא Cancel ואז צריך מאין להחזיר את הגלגל אחורה ולבצע פעולה מנוגדת לפעולה שכבר הסתימה בהצלחה כמו אחרי הזמנת כרטיסי טיסה אם המערכת מבטלת את התהליך כתוצאה מאי אישור של מנהל אזי יש לבצע פעולת פיצוי של ביטול ההזמנה .
ונותנת גם אפשרות להגדיר ל activity סוג של תהליך שיקרה כאשר הcancel נקרא כאשר הוא במהלך הריצה ולא השלים את עבודתו .
תשתיות הWF נותנות יכולות לכלול השהיית התהליך והמשכתו בשלב מאוחר יותר .
טרנזיטיביות זה לא בדיוק תכונה נתמכת במערכת עקב החוק השני של טרמומכניקה שקצת קשה להפריד חזרה את הביצים , קמח והגזר מהתערובת של העוגה אם לא הצלחנו בתהליך הדלקת התנור .
Use case

המפעיל של המכונה טוען את הקובץ הworkfow נטען ויכול להריצו ביחד עם פעולות כמו לבטל הרצה , לראות את התוכנית של האפייה וכד

אבני הבניין העיקריות של הארכיטקטורה

Class diagram
Custom activities
אילו Activities קונקרטיים שמהווים את אבני הבניין לWorkflow שיחולל אוטומטית.

הערות
1.הDesigners של ה Activities כתובים ב Wpf ,הבעיה המרכזית איתם בגירסה 4.0 הינה העדר יכולת להכיל controls לעריכת Arguments או variables שאינם EditBox ואז לדברים מורכבים כגון list של אלמנטים בלתי אפשרי לתת אפשרות עריכה .
למיקרוסופט יש פיתרון פנימי לבעיה הזו הוא משמש לדוגמא ב Send activity לצורך מילוי השדות של הפרמטרים , לא הצלחתי להעתיק את הפתרון שלהם לאפליקציה שלי.
ניתן לקשור לXaml בBinding ערכים של modalview אשר ישמשו בזמן debuging להצגה אקטיבית של state של הריצה .לדוגמא: במקום הDesigner הסטנדרטי של Delay activity ניתן להחליפו בDesigner שמראה זמן יורד תוך כדי ריצה .
2.על מנת לבצע פעולה ממושכת כמו דימוי האפייה כמומלץ אנו לא ממשים בתוך ה Bakeing activity סוג של while loop עם sleep ובדיקה מחזורית,אלא משתמשים בextention .
כאשר הactivity מסיים את פעולות האתחול שלו הוא נעצר בbookmark ואז extention ששייך לו מבצע את הבדיקות וכאשר התנאי מתקיים הוא משחרר את הbookmark וה Activity יכול להמשיך בעבודתו .
הדבר נועד לאפשר את תהליכי ה Cancelation וכד

הקוד:


ברוכים הבאים לפרק השלישי של הטרילוגיה שעוסקת בשינוי ה data packet שזורם לרשת או ממנה באמצעות Ndis filter driver.
לצערי בגירסה הנוכחית של NDIS עדיין אין תמיכה בשאילתות Linq ואו בReflection לכן את העבודה שלה parsing של ה NBL עלינו לבצע באמצעות פקודות וmacros של wdm ו ndis .
מבחינת תוכן לוגי ה NBL בהודעת udp בנוי בצורה הבאה :

חלק של Header הודעת Ethernet
נציג של שיכבת הקו בosi .
חלק שזה מכיל את ה Mac של כתובת המקור ,
ה Mac של כתובת היעד .
ופרוטוקול המידע של ההודעה לדוגמא: ip4 ,ip6 , arp , Icmp וכד

חלק של Header הודעת Ip4
נציג של שיכבת הרשת ב osi
מכיל את הנתונים הבאים :
גרסת הפרוטוקול ,
אורח הפתיח ,
סוג השרות ,
הגודל הכולל של החבילה ,
מספר הזיהוי של החבילה שמשמש כאשר הודעה מורכבת ממספר חלקים
(TTL - Time To Live).
הפרוטוקול שבאמצעותו מעבירים את המידע לדוגמא : tcp , udp ,
כתובות ip של שולח ההודעה וכתובת ip של הנמען הסופי .

חלק של Header הודעת Udp.
נציג שכבת ה תעבורה ב OSI
מכיל את פורט ה מקור ופורט היעד אורך החבילה וה Checksum .

payload , Data
המידע עצמו שנשלח או התקבל ברשת לדוגמא הודעות soap , http ,smtp וכד

מבנה המידע בהודעת udp
באיור מוצג structs שמרכיבים את מבנה של הHeader המבנה מתאים להודעת udp בלבד ,כל הודעה אחרת המרכיבים שלה יהיו שונים .
כל הודעה מכילה מפתח שמצביע על סוג ההודעה הבאה על פיו אני יודעים לדלות את המידע של הheaders המתאימים.

מבחינת פיזור המידע ב NBL המידע ערוך בצורה הבאה :

Object diagram

מציאת הFirst Network buffer מתבצעת בצורה הבאה

להודעת udp יש שני Buffers הראשון של הheader והשני של המידע .מציאת הBuffer ים של המידע מתבצעת בשיטה הבאה :

בקוד הבא ניתן לראות את הדרך בא מוצאים את ה packet הראשון של ה Header .
ndis הינו little indian כלומר צריך להפוך את מיקום ה Byts בStructs כאשר מלבישם עליו מערך של bytes

משתמשים בפונקציה RtlUshortByteSwap על מנת לבצע את ההצרחה של ה Bytes.
הקוד שמוצא את הpattern ומחליף ערך הינו :

אנו מוצאים את הpattern באמצעות NdisEqualMemory ומחליפים char אחד בו .
בUdp אין צורך לעדכן Check sum
כלי עזר מאוד חושב הינו ה Wire Shark , יש לשים לב שwire shark איננו עובד במקביל ל Kernel debuging
הרבה מאוד פעמים structs של אוביקטי רשת שמורידם מה internet אינם מדויקים ובאמצעות wire shark ניתן לבדוק ולהשוות לשמה בdriver .
את הקוד ניתן למצא כאן
באחד מהפרויקטים שעבדתי עליהם לאחרונה היתה דרישה להחליף תוכנת עמדה במערכת מבזורת בתוכנה אחרת .
התקשורת בין כל העמדות הינה WCF ,כאשר הcontract של העמדה החדשה שונה לחלוטין מהcontract הקיים ואין אפשרות להחליפו לזה שהיה קיים .(העמדה הייתה ונשארה server )
ל Contract החדש אין תאימות חד חד ערכית ברמת הקריאות כלומר חלק מהקריאות של ה Client היו צריכות מספר קריאות בContract של ה Server החדש חלק היינו צריכים לשמור ב Cache על מנת לאגד מספר קריאות מה client לקריאה אחת בServer וכד.

חשבנו על 3 פתרונות אפשריים
פתרון 1 מערכת חיצונית
לכתוב או לקנות מערכת חיצונית שהינה מאין biztalk sever אשר יבצע את פעולת התרגום .
יתרונות
מבחינת decoupling מאוד גבוה בין מנגנון ההמרה לאפליקציה , השינוי היחיד שנדרש לבצעו באפליקציה זה החלפת ה address של ה wcf server .
אפשרות ניטור של המידע הזורם + העבודה המתבצעת באותו מתאם .
תמיכה יכולת גידול לעתיד לdevice ים או שיטות תקשורת שונות .
חסרונות
הבעיה שצריך לתחזק עוד אפליקציה \ service שאיננו חלק אינטגרלי מהאפליקציה .(תיעוד התקנה קיטלוג )
לקנות אפליקציה חיצונית עולה כסף .
Hop נוסף של מידע שאין בו צורך . המידע נשלח ל wcf service ולאחריו ל עמדה הנוספת.
פתרון 2 השיטה הסינית
שיטת הquick and drity לאתר את כל המקומות בקוד שפונים לwcf client ולשתול בכל המקומות switch ים של האם לשלוח בintrerface החדש או הישן . + להוסיף את כל הלוגיקה בתוכנה עצמה של ה Client .
יתרונות
למרות שיש הרבה מאוד עבודה העבודה פשוטה אין סיכון טכנולוגי .
חסרונות
מכיוון שבאפליקציה אין שיכבה אחת מסודרת שקראה לwcf clients אלא הקריאות התבצעו מכל מקום אפשרי וישנם מאות מקומות כאילו הקוד של האפליקציה יסרבל עוד יותר כי זה לא רק הקריאה עצמה יש להוסיף לוגיקה לכל קריאה .
כמובן שאפשר לעשות את זה יותר אלגנטי ולא להשתמש בSwitch אלא שימוש ב Factory שיחזיר Iterface ל ממשק .
הפתרון ה 3 Wcf custom channel
פתרון באמצעות Custom protocol channel שכתבנו שתפקידו לתרגם את ההודעות .
יתרונות
פתרון שהינו מאוד decoupled אין צורך לפתוח אף אחד מהתוכנות . אפשר באמצעות קונפיגורציה להכיל אותו .
לא נוצר עוד hop מיותר
מאתגר מקצועית
אפשר באמצעותו ללמוד wcf לעומק
מאוד מרשים ראשי צוותים סמי מיקצועיים (כאילו שיועדים מה זה wcf בשתי מילים אבל אין להם שמץ ניסיון בארכיטקטורה נכונה וכד )שאתה מספר להם ברעיון עבודה שעשית כזה דבר חושבים איזה ילד פלא אתה ,השועלים הותיקים לא יתנו לך לסיים את הטרילוגיה והם כבר יראו לך את הדלת. שתסבך עד מוות את הפרויקט של מישהו אחר .
חסרונות
מאוד technology aware כלומר מבוסס על יכולות ההרחבה של wcf ותלוי ביכולות האלו . ומכיוון ש wcf איינו קוד פתוח ניתן להיתקע עם memory leak בעיית ביצועים או חלקים שבלתי ניתנים למימוש .
Wcf channels
התשתית של wcf מכילה ערימה של channels המתפקדים כ chain of responsibility
דומה מאוד ל ndis filter drivers .

בין כל החלקים של ה channel זורמים אוביקטים מסוג Message .
הMessage
מורכב מ Headers וPayload של המידע (הAction והפרמטרים שלה ).
כל channel יכול להוריד להוסיף או לשנות את ה header ים .
כמו כן ה Channel יכול לשנות את ה payload כלומר את המידע עצמו שעובר ב Message
(שינוי פשוט של מידע יכול להתבצע באמצעות Behaviors כדוגמת מימוש IOperationBehavior ביתר קלות )
הפתרון שלנו התבסס על זה ש Channel במקרים מסוימים יכול להחליט שהוא לא מעביר הלאה הודעה (כלומר חוסם אותה ) או מעביר כמה הודעות במקומה ) או מעביר כמה הודעות בזמנו החופשי בthread אחר אחרי שההודעה בכלל נסתיימה .
הפתרון הזה עובד טוב רק ב interface אסינכרוני .בinterface סנכרוני המצב מסתבך כי המערכת מנסה להחזיר לך תמיד הודעה על כל קריאה באופן סינכרוני ויצירת מצב ששתי הודעות נשלחות ומקבלים רק תשובה אחת מאוד בעיתי , אינני יודע אם אפשרי בכלל.
אנו כתבנו Custom channel משלנו והכנסנו אותו ל channel stack .

ה Channel הכיל את הלוגיקה כלומר את המימוש של ה Bridge שמתאם בין ההודעות , הchannel הינו statefull הוא זכר את כל המידע מהקריאות הקודמות שדרושות לצורך התאום .
הפיתרון מכיל את ה class ים הבאים :

LogicOrcBinding
הcustom binding שמכיל את ה binding elements שלנו .

הגדרנו רק 2 elements כאשר תשתיות ה wcf מוסיפות את החסר בהתאם לקונפיגורציה אם זה security , relaibale session וכד
התשתית מוסיפה את שאר ה elements במקומות הנכונים כך שהצפנה לדוגמה תתבצע אחרי הטיפול שלנו .
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
LogicOrcBindingElement , LogicOrcChannelFactory , LogicOrcChannelListener
מכילים את ה בוילרפארט קוד בד"כ לא נוגעים או משנים אותם. תפקידם לעטוף את היצירה של ה channel שלנו .
כאשר יש פרמטרים שצריך להעביר מה Binding עצמו ,צריך לדאוג להעביר דרכם .
LogicOrcBodyWriter
class עזר פשוט שתפקדו לעזור בלכתוב את ה פרמטרים של הAction בMessage
הClass מכיל event בשם writeBodyCallback שנקרא אוטומטית על ידי המערכת לצורך עדכון הפרמטרים .

LogicOrcChannel
זהו לב המערכת

בCtor אנו מקבלים Reference ל Channel הבא בstack

אנו נשתמש בReference הזה לשני דברים :
1.להפנות מימוש methods למימוש ב Channel הבאה בstack ב methods שאין לנו צורך לממשן
לדוגמא:

2.הפניית הMessage אחרי הטיפול לChannel הבאה ב stack
הmethods שמטפלות בשליחת הנתונים ל channel הבאה בstack הינן :
public void Send(Message message, TimeSpan timeout) ,
public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
כאן משתלבת הלוגיקה העסקית של הBridge בשליחה של הודעות לServer ,ניתן לא להעביר את הMessage ל channel הבא ובכך לחסום את ההודעה ניתן להעביר הודעה אחרת או מספר הודעות אחרות ,כמו כן ניתן להעביר את הטיפול ל thread אחר ובזמן שונה להעביר הודעה ל Channel הבא ב Stack .

דוגמא למימוש
פונקצית עזר לבניית Message חדש בהסתמך על העתקת ה Header של ה message שהתקבל מבChannel הקודם ושינוי ה Action

הפונקציה הבאה מקבלת פקודה אחת ומפצלת אותה ל שניים

הMethods שמטפלות בקריאת הנתונים בצורה סינכרונית או אסינכרונית בהתאמה הינן :
public bool TryReceive(TimeSpan timeout, out Message message)
והצמד
public
IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state)
public bool EndTryReceive(IAsyncResult result, out Message message)
אחרי ששלחנו שתי הודעות Call1 וCall2 ל Client אמו מקבלים גם שתי תשובות אותן אנו צריכים לאחד לתשובה אחת .את הפעולה הזו אנו עושים ב EndTryReceive

Exception
בזמן הפיתוח אם מבצעים שגיאה בקריאה של ה Contract לדוגמא קוראים ל Action שאיננו נמצא ב contrat של ה Server המערכת נותנת שגיאה מפורטת :
{"The message with Action 'http://tempuri.org/ILogicOrcSample/CallWithWrongAction' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None)."}
טעיות בשמות של ה פרמטרים בד"כ יגרמו ל להעברת null בפרמטרים של ה method
בדיקות
שתי בדיקות נעשו לצורך ולידציה של הפתרון :
1.בדיקת memory leak שהמערכת לא משאירה אצלה זבל במקרה ונחסמת פקודה מלהגיע ליעדה וכד.על מנת לבצע את הבדיקות עם התוכנית דוגמא יש להוריד את כל ה חלק של ה Diagnostic מה Configuration files , לבצע את הקריאות הנדרשות בloop ומידי פעם לקרא ל GC.Collect
2.בדיקת ביצועים ש ה Bridge Channel לא פוגע בביצועים כתוצאה מביצוע הלוגיקה שלו
בבדיקה שבצעתי לא נראה כל דליפת זיכרון אך אני ממליץ בחום לכל מי שחושב על הפתרון לבדוק גם את נושא הזיכרון , יציבות , ביצועים של הקונספט האם הם מתאימים לדרישותיו.
קישור לקוד דוגמא:
קוד דוגמא מאוד בסיסי נמצא כאן .
הקוד מבוסס על דוגמא של מיקרוסופט בשם Chunking channel לדוגמא הוכנסו רק הדברים האלמנטריים ביותר לצורך הדגמת הקונספט .
על מנת להשתמש בה יש כמוסן להוסיף לה את כל הקוד הסטנדרטי כגון טיפול ב Timeouts סגירת Channels ללא exceptions וכד.
ל 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 .
ביום שישי שעבר בשעה 9 בבוקר בדיוק שהתחלתי לפרוק את המוצרים מהעגלה למסוע של הקופאית בסופר התחילו המסופים של הקופות אחד אחרי השני לקרוס וקול הצווינג של קריאת הbar code נדם .
הקופאיות ניסו לרסט את המסופים אבל זה לא עזר ,והתור והלחץ של הקונים (יום שישי יום קצר) התחילו לגעוש .
היחידים שנראו מבסוטים מכל העניין היו הקרפיונים שקיבלו חסד של עוד כמה שעות לפני שטוחנים אותם ושמים להם חתיכת גזר על העין .ביחד עם אותו סבאלה שהחליט לספר צ'יזבטים על אותם הימים בהם קנית במכולת חצי כיכר לחם והתווכחת איזה חצי יותר גדול .
מנהל הסופר ניסה להתקשר למרכז התמיכה ולתאר להם את הבעיה שהמקסימום מידע טכני שהוא יכל למסור לאותה מרכזנית זה הודעה הזויה של :Failed to login due to transaction abortion משהו כזה ,משהו שנראה לי שגם למפתח הראשי של האלפיקציה לא היה אומר הרבה .
האפשרות לבקש ממנהל הסופר לייצר קבצי minidump ולשלוח למרכז הבקרה לא היתה ראלית ,כמו כן להגיד למנהל הסופר שכעת שולחים לו קבצי symbols של הגרסה שמורצת אצלו שיוריד בבקשה מה msdn את ה windbg ויתחיל לדבג את האפליקציה (זה טוב להדגמות עם תכוניות קטנות ).
גם האפשרות לכרוז ברמקול של הסופר :יש פה תכנת בקהל גם לא נראתה משהו. במיוחד אם היו מוצאים תכנתים בקהל הלקוחות סביר להניח שעזרה לא היו מקבלים מהם אלא עצות וחוות דעת על למה צריך לכתוב את כל האפליקציה מחדש , למה כדאי לעבור לענן , אנדרויד , mvc4 silverlight 5 או spring .
לא משנה כמות המוטיבזציה שהיו יוצרים אצל מנהל הסופר על ידי משפטים כדוגמת "עתיד הסופר בידך אין אף אחד מאחוריך!!! " האפשרות להמשיך את יום הקניות כסדרו תלויה עכשיו אך ורק ביכולות הrecoverability של האפליקציה !!
כמעט כל אפליקציה יכולה לקורס אם זה עקב בעיית חומרה או בעיית תוכנה ,אני יודע שבמטוסי נוסעים ישנם לפעמים מספר מחשבים לאותה משימה גיבוי אחד לשני ,אצלנו בסופר בנס ציונה לא נראה לי שזה המצב .
אינני יודע למה לפתע קרסו כל העמדות. יכול להיות שהארכיטקט של המערכת לא לקח בחשבון אפשרות של נתק בין ה clients ל servers , או אפשרות שה server יקרוס והמערכת לא תוכננה כOccasionally Connected Systems Architecture .
יכול להיות שהמערכת תוכננה כ Occasionally Connected Systems Architecture אבל הפצה של poison message לclient שאין נגדו הגנה גרמה לשיתוק של כל העמדות .
דוגמא ל poision message הינה עדכון של פרי חדש (ספוטה שחורה ) בטבלת התמונות של הפירות עם תמונה בגודל 0 שגורמת לexeption

(הספותה השחורה הינה פרי רעיל כל עוד איננה בשלה כשהיא בשלה ניתן לייצר ממנה עוגות שוקולד מדהימות).
מרחב המקורות ל Malfunction במערכת client server של 24 קופות שכל הזמן מעדכנות נתונים בשרת וכל הזמן נדחף להם נתונים חדשים כגון מבצעים של יום שישי כמעט ולא יכול להיות אפס .
עם הזמן כמות המידע שנצברת ומשתנה במערכת גורמת למצבים שלא תוכננו כמו שאילת select בהצבה למשתנה שאמורה להחזיר רק ערך אחד מחזירה יותר ואז ה store procedure נופלת .בעיות של buffer overflow , בעיות של race condition שנוצר עקב שילוב של כמות מידע וכד .
בכל מקרה לפי דעתי על הארכיטקט של המערכת לתת את הדעת שמצב של קריסה לוקלית בעמדה אחת או כללית בכל העמדות אפשרי .
ישנו פיתרון build in לחלק מסוים מהבעיות מנגנון ה Application Recovery and Restart ומנגנון הWindows Error Reporting ניתן למצא עליו פרטים ב http://msdn.microsoft.com/en-us/library/cc303708.aspx אבל הפתרון הינו חלקי ולא היה עונה על הבעיה שהתוכנה בקופות לא קרסה לא היה second chance exceptionהמערכת ברוב העמדות אפילו לא נתקעה אלא פשוט הפסיקה לעבוד לקבל barcodes .יכול להיות מצב שרק ה תת מנגנון של בחירת פירות לא עובד והמערכת בלתי שמישה לחלוטין.
Recoverability היכולת של אפליקציה לעלות מחדש ולהמשיך לעבוד מהנקודה בה היא הפסיקה אחרי נפילה באג או תקיעה .(אם אוטומטית או על ידי מפעיל ).
ברמה של product management יש מספר עקרונות לתמיכה ב Recoverability
1.זמן recovery ועליה מחדש מינימלי.במשך השעה הזו שהקופות לא פעלו עשרות עגלות של מוצרים ננטשו על ידי קונים ממורמרים כמובן שאין זמן ל production debugging וכד .
2.אפשרות ניטור הבעיה – מיועד למקרים קיצוניים בהם restart של אפליקציה לא עוזר בד"כ כאשר הבעיה נגרמת על ידי מצב סטאטי כמו בעיית חומרה כגון HD מלא , או בעיית תוכנה מסוג של poison message.
אפשרות לפתוח חלון של קופאי בכיר המציג את הבעיה מגובה תמיד באפשרות לפתוח קובץ לוג באמצעות Notepad ולהבין משם מה הבעיה .
3.החזרת האפליקציה במדויק לstate הקודם
האפשרות שאחרי reset של עמדה יצטרכו להתחיל חשבון שהוא כבר לפני סיום מחדש איננה פיסבילית לחלוטין ,הרי המסוע של המוצרים לפני הקופה כבר מלא במוצרים של הלקוח הבא . בזמן ההמתנה הלקוחות על מנת ליצור לפחות תחושה של התקדמות הערימו ערמות על גבי ערמות של מוצרים על המסוע .להתחיל להחזיר את המוצרים ממסוע הקידמי לזה שלפני הקופה ,תוך כדי שלקוחות מפחדים שיחויבו פעמים …….
בכל מקרה צריך לשים לב שהיו מספר reset ים של האפליקציה לפני שהיא חזרה לעבוד , לכן איפוס של ה state ששמור לצרכי המשך עבודה אחרי כל עליה הינו שגיאה לוגית ,אחרי שהאפליקציה חזרה לסורה למשתמש צריכה להיות האופציה לבחור את ה state של ה Recovery המתאים לו מבין כל הsession הקודמים .
4.מנגנון שמירת ה Session צריך לתמוך במצב שלאפשר לעבוד בעמדה אחרת במקרה ועמדה התקלקלה לגמרי.
5.אפשרות לעשות roll back למידע חדש שיכול לגרום לmulfunction יכול להיות שזה מידע ב cahce בלוקלי או מידע שנמצא בשרת ואינו מאפשר למערכות להמשיך לעבוד .
עקרונות ארכיטקטונים לתוכנה שתיתמוך ב Recoverability
הפרדה בין state לפונקציונליות
מבחינה ארכיטקטונית על מנת לתמוך ב Recoverability של האפליקציה יש להקפיד על הפרדה בין state ל פונקציונליות .
state שמופרד מהלוגיקה יכול להישמר תדיר לצרכי recovery על ידי מגנוני הסיראליזציה הקיימים כמעט בכל השפות והטכנולוגיות לדוגמא:מנגנון ה pickle , shelve בפיטון .
האויב הגדול ביותר של ההפרדה בין state לפקונציונליות זה מימוש לא נכון של class properties כלומר get accessor שמשנה את הstate של הclass ו Set Accessor של property אשר כאשר קוראים לו מספר פעמים עם אותו הערך הstate של ה class לא נשאר אותו הדבר .
שמירה מתמדת על ה state של ה אפליקציה לצרכי שיחזור המצב , הstate השמור לא צריך להיות רק חלק מה db של האפליקציה אלא גם כגיבוי חיצוני חיצוני כדוגמת קובץ או שימוש בNo sql db מסוג document .
יכול להיות שהstate ששמור הינו מינימלי כגון שלב עריכת החשבון של לקוח , רוב הנתונים כבר נשמרו בdb

תיכנון המערכת כ Occasionally Connected Systems Architecture
מערכת שתוכננה כ Occasionally Connected Systems Architecture על אף היותה בהרבה יותר מורכבת ממערכת שהינה client server פשוטה , הRecoverability שלה בהרבה יותר גבוהה ,יכול להיות מצב כמו בדוגמה שלנו שהשרת התקלקל אפשר היה לסיים בשקט את הפעילות תוך כדי שמירה של כל הפרטים לdb לוקלי אשר יסונכרן בoffline מתישהוא .

logging
logging נכון ומפורט נטול מידע בילתי רלוונטי ומידע מיותר שחוזר על עצמו כגון להפציץ את הלוג בהודעות מחזוריות תקינות .לא פעם נתקלתי בלוג של כמה גיגות מפוצץ בהודעות :Message was send או second chance exception occurred מבלי stack trace .
והכי חביב זה הודעות wrong value ב default של switch case מבלי לציין מהו הworng value .
יכלותי לעביר סדנה של כמה חודשים עם כמות משחקי הניחושים ששיחקתי בחיי בזכות עצלות רגעית של מפתחים .
זמן עליה \ ירידה של המערכת סביר
לא צריך להטעין את כל המידע שבעולם ל cache בזמן עליית המערכת .אפשר לטעון מידע בJIT
כלי ניהול
רצוי שכל הניהול של האפליקציה המאפשרים בחינה של ה Log ו ניהול הdb (מחיקה של poision messages ) לא יהיו חלק מהאפליקציה לא של ה client ולא של ה server
בפוסט הקודם דיברתי על מרחב הבעיה , פתרונות אפשריים ותאור הDriver בקליפת אגוז ,הפוסט הזה אני אמשיך וארחיב לגבי המימוש .
לאחר שהעתקנו אלינו את הדוגמא של הDriver מה DDK מ
C:\WinDDK\7600.16385.1\src\network\ndis\filter
קימפלנו את ה code של Driver (עדיין עם build בסביבת command כשאצלנו בחברה יוצאים לסרט לתכנתי הdrivers מקרינים במיוחד סרטים מהתקופה של הכלים בהם הם עובדים רק בשבוע שעבר היה זה מבצע באנטבה עם יורם גאון ואריק שני. אנו מתאפקים עוד קצת עד שvisual studio 11 ישוחרר ותכנתי ה drivers יקפצו 30 שנה קדימה)
והתקנו את ה filter driver או virtual machine או במחשב אחר .
(בVirtual machine יש להקפיד שהקונפיגורציה של הרשת תהייה חיבור מלא אחרת זה לא יעבוד )
ההתקנה מאוד פשוטה מהNetwork connections .

לאחר שהתקנו את ה driver אנו רוצים לראות שאכן ה driver עובד והודעות שנשלחות לNIC אכן עוברות דרך ה Filter Driver שלנו .
הContainer של הודעות המידע (בשונה מהודעות הoid עליהן ארחיב פעם אחרת ) שעוברות ב Ndis drviers לסוגיו (אם זה בminiports , Filters ,protocols driverts etc' ) הינו struct מסוג Network buffers list .
מבנה ה Network buffer list

הפרמטר של ה NetBufferLists בcallbacks של :
SendNetBufferListsHandler, ReturnNetBufferListsHandler, SendNetBufferListsCompleteHandler ReceiveNetBufferListsHandler
מורכב מ linked list של NET_BUFFER_LIST struct
כל NetBufferList מצביע על struct של linked list of NET_BUFFER
אשר מצביע על Strcut של MDL linked list
כל MDL strcut מצביע על buffer של מידע .
על מנת להרגיש את הפעולה של ה filter יצרתי תוכנית קצרה ששולחת הודעת udp ,העדפתי udp כי כמות המידע שזורמת בהודעת udp קטנה בהרבה מזו של tcp אין צורך בתעבורת handshake וכד
בכוונה אני עוצר את התוכנית רגע לפני שליחת ההודעה על מנת להימנע לעצור ב breakpoints שנגרמים כתוצאה מביצוע פעולת GetHostEntery .

לעניות דעתי כלי ה Debug של ndis שמוכלים ב Ndiskd.dll מאוד חזקים ומאפשרים ניתוח מהיר ופשוט של הן המידע והן הstack של ה ndis
http://msdn.microsoft.com/en-us/library/windows/hardware/ff552270(v=vs.85).aspx
אחרי שהתחברתי למחשב שמריץ את הfilter driver טענתי symbols והרצתי את תוכנית הבדיקה ,אני שם breakpoint בcallback של FilterSendNetBufferList

מיד אחרי שליחת ההודעה אני נעצר ב breakpoint

באמצאות כלי הdebug של ndis בחנתי את תוכן ההודעה .
פקודת ndiskd.nbl פורסת אוטומטית את הnbl בהינתן ה Address של ה nbl הראשון .
כפי שרואים באיור הבא ההודעה מורכבת משני mdl ים אחד מכיל את הheader של הודעת הudp והשני את הpayload של המידע ששלחנו .

הNBL מכיל metadata נוסף כדוגמת ה SourceHandle המוצג באיור הבא ממנו ניתן ללמוד על מיקום ה driver שלנו בndis stack ואיך ה ndis stack בנוי ומורכב .

אחד השינויים המהותיים ב Direct3D11 הינו שהשימוש בקבצי Effect המרכזים את כל ה shaders ואת ההגדרה של ה resources שshaders צריכים הוחלף בשימוש בקבצים דיסקרטיים עבור כל shader כלומר שימוש בקבצים מופרדים עבור ה pixel shader , vertex shader ה shaders של ה tesselation ה geometry shaders וכד .
ההגדרה של technique שמכילה את כל הflow החל מהכניסה ל graphic pipeline ועד ל הגדרות של הz-buffer הועברה מהeffect file לקוד של האפליקציה .
שינוי נוסף ב Direct3D11 הינו שהhlsl5 הפך להיות Object orientedנוספה תמיכה בinterfaces , ב ירושות ,
בדומה לאבולוציה של ++C מ C .(מעניין אם יום אחד נראה קוד hlsl דינמי סטייל python או php משהו כמו if ( !isset ($light)) die ();
אם בגירסות קודמות שלן directx היינו יוצרים תצרותיות של הקוד על ידי שימוש ב if define כעת .hlsl5 נותן לנו את היכולת להגדיר class קונקרטי עבור כל טכניקה של רינדור .
מכיוון שיש decouple בין האפליקציה ל shaderים ניתן לשכלל את ה shader ים ולהרחיבם מבלי לשנות את האפליקציה , הם לא צריכים להיות חלק מתהליך הקומפילציה ובלבד שכל הresources מסופקים להם .
לעיתים נוצר צורך מצד האפליקציה לתחקר את ה shader הנטען על ידה לגבי איזה class ים הוא מכיל איזה interface ים הוא ממש ואיזה resource ים הוא מגדיר זאת על מנת שהאפליקציה תוכל להפעילו .
הדבר דומה ל reflection ב net.
DX3D11 נותן לנו כלים לביצוע התחקור באמצעות מנגנון :Shader Reflection API אשר במרכזו נמצא com interface :
ID3D11ShaderReflection
לאחר שטענו את ה shader אנו יכולים לקבל את ה Reference ל ID3D11ShaderReflection על ידי:

הקוד הינו רלוונטי לכל סוגי ה shaders כלומר אין משתנה שמגדיר האם אנו עובדים עם ps , vs וכד .
דוגמא לשימוש בקוד הזה נמצאת ב DynamicShaderlinkageFX11 אשר ב Direct3d sdk
הדוגמא מראה לנו איך אפשר לשנות את סוגי התאורה על ה scene באמצעות שימוש ב dynamic linkage של shaders .
החלק שנוגע לתחקור הshader מתבצע בפונקציה
OnD3D11CreateDevice
אשר נמצאת בקובץ DynamicShaderLinkage11\DynamicShaderLinkage11.cpp
בדוגמא מודגם אפשרות של טעינה ושינוי דינמי של תהליך חישוב ה pixel shader באמצעות class ים שונים הממשיים class אבסטרקטי של חישוב מנת תאורה ל pixel .
כידוע חישוב מנת האור לpixel מורכב מכמה מרכיבים :
float3 Lighting = saturate( Ambient + Diffuse + Specular
עבור כל אחד מהרכיבים אפשר לחשב את המקדם של עוצמת האור בדרכים שונות בתלות בסיבוך של החישוב כמות ה resource ים שנדרשים וכד

כאן אחרי שדגמנו את ה texture אנו מכפילים את הערך בפונקציה וירטאולית :IlluminateAmbient
בדוגמא ניתנים שני מימושים שונים קונקרטיים ל IlluminateAmbient
אחד הפשוט :

והשני מסתמך על
Hemispheres of light כלומר התחשבות גם של האור הישיר של השמים וגם האור שמוקרן מהאדמה

ה shader מגדיר שני הטכנולוגיות לחישוב הAmbient light בעוד האפליקציה צריכה לבחור עם איזה מימוש משתמשים .
האפליקציה צריכה למצא ב shader את המשתנה שמחזיק את ה reference ל class שממש את חישוב האור :abstractAmbientLighting ולהציב לתוכו את ה instance של ה class הנבחר.
שלבים בתחקור של ה shader בsample ובהצבה:
1.קבלת ID3D11ShaderReflection
2.שאילתה לגבי כמות ה interface slots ב Shader
כלומר שאילתה באמצעות הפונקציה
pReflector->GetNumInterfaceSlots
שמחזירה את כמות ה interface pointers בshader
הסיבה לשאילתה הזו הקצאת מערך של
ID3D11ClassInstance
שיכיל את כל הreference ים ל class ים הקונקרטיים כמו כן נשתמש בערך הזה בהמשך כאשר נציב ל shader את מערך instance ים שבחרנו להשתמש בהם
3.עבור כל pointer ל interface ספציפי אני מעוניין לדעת את מיקומו היחסי ב shader
זאת מתבצע באמצעות :

כעת עלינו לקבל מ הshader את ה instance עבור כל אחד מה class ים הקונקרטים :

לקבוע עבור ה render את הps איתו הוא יעבוד ,כאשר הפרמטר השני מכיל את טבלת הinterface ים של ה class ים הקונקרטיים מסודרים לפי מספר ה slot אותם תחקרנו והפרמטר השלישי מכיל את גודל טבלת הclass ים הקונקרטיים .

כמובן שאפשר לשחק עם ה shader במהלך תהליך ה render לחלקים שונים להפעיל class ים שונים ,דוגמא זו שהינה פשוטה קובעת את ה ps רק פעם אחת במהלך render ,ניתן כמובן לחלקים קרובים לקבוע pixel shader מורכב ויקר מבחינת צריכת משאבים ולחלקים רחוקים להשתמש ב ps יותר פשוט .
More Posts
Next page »