באחד מהפרויקטים שעבדתי עליהם לאחרונה היתה דרישה להחליף תוכנת עמדה במערכת מבזורת בתוכנה אחרת .
התקשורת בין כל העמדות הינה 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 יותר פשוט .
באחד מהפרויקטים עליהם עבדתי לאחרונה היתה דרישה ליירט כל network packet שנכנס או יוצא מהNIC (ללא כל קשר לאפליקציה המקבלת או שולחת אותו ) לחפש pattern מסוים באותו הpacket של רצף bytes מסויים ואם הpattern מופיע לבצע באותו packet שינוי .
כמובן ששינוי ה packet מצריך חישוב מחדש של ה checksum בהתחשב בפרוטוקול של ההודעה הנשלחת .
שתי טכנולוגיות נשקלו על ידנו לטובת מימוש הדרישה :
WFP קיצור של Windows Filtering Platform שהינו פלטפורמה לירוט של תעבורת רשת ,שהינה זמינה החל מwindows vista . זו גם הטכנולוגיה המומלצת בכל פה על ידי אנשי מיקרוסופט כמו אולר שוויצרי לצרכים כאילו .
NDIS filter driver שהינה סוג של kernel driver שמשמ לירוט ושינוי של תעבורת רשת.
אל אף היתרון הגדול של wfp המאפשר עבודה בuser mode בחרנו לממש את הדרישה כNDIS filter driver . וזאת מכמה סיבות בהן:
גמישות –NDIS filter drivers חושפים יכולות אשר WFP לא חושף לדוגמא אפשרות לעבד OID requests
מבחינת פשטות של קידוד הפתרון יותר פשוט לכתוב NDIS Filter driver מאשר WFP .המבנה של אפליקצית wfp יותר מסובך מndis filter driver .
מכיוון שיש לנו בקבוצה יותר ניסיון בכתיבת ותחזוקת NDIS drivers מאשר ב WFP .
כמו כן ישנה דוגמא שהינה השלד של ה פתרון שלנו בWindows driver kit ורוב הדוגמאות שמצאנו של WFP היו בנושא של inspection של packets ופילטור של packets ולא של שינוי.
בניגוד לחלק מהקטגוריות של wdm drivers ,דיבאגינג והרצה של NDIS drivers ניתן לעשות בvirtual machine דבר שמקל מאוד את הפיתוח .
הדוגמא שיצאנו ממנה הינה :NDISLWF.SYS אשר נמצאת בnetwork\ndis\filter שהינה דוגמא של ndis 6.0 filter driver .
פעולות ההכנה שביצענו בדוגמא היו :
1.הוספת תמיכה ב tracing באמצאות wpp חשבנו על האפשרות של תמיכה מלאה ב etw עם אפשרות של יכולות לשאילתות לגבי מספר ה packets ששונו , יחסית למספר הpackets שהתקבלו וכד אבל נראה כי לא היה לזה צורך מבחינת הנדסת המוצר ומכיוון שהוספת תמיכה בetw בהרבה יותר מורכבת מאשר wpp ,הסתפקנו בwpp .
מכיוון שעבודה עם virtual machine לצורך פיתוח dirvers לא רואים את ה output של DbgPrint החלפנו את כל הקריאות של DbgPrint ב DoTraceMessage של ה Wpp
2.הוספת תמיכה ב kmdf שזה nice to have וממש לא must .
המרחב הטריטוריאלי של הFilter driver בndis stack נמצא בין ה miniport adapter ל protocol driver כלומר המידע שמגיע עליו הינו מידע גולמי ישר מהNIC או row data שצריך להישלח לndis ,אין לנו בזמן העיבוד אינדיקציה לגבי הprotocol של ההודעה אבל אנו יודעים שהתשתית איננו מערבבת בין הודעות כלומר לא תגיע חצי הודעה או שתי הודעות באותה קריאה .

הapi של מימוש ה driver מבוסס על פקודות מספריית הndis .
המבנה של ה Ndis filter driver הינו מאוד פשוט ומבוסס על רישום להודעות callback של התשתית.רישום לחלק מההודעות הינו חובה ולחלקן כאופציה . בסיום הטיפול בcallback הdriver צריך לקרא לפקודת ndis שמזרימה את ההודעה ל filter driver הבאה בתור.
כך שהpattern של ה driver נראה כ Observable pattern בשילוב עם chain of responsibility.
במהלך הביצוע של הDriverEntery ה Driver מבצע רושם את עצמו כ Ndis filter driver באמצעות קריאה לNdisFRegisterFilterDriver.
NdisFRegisterFilterDriver מקבל כפרמטר את הstruct של
NDIS_FILTER_DRIVER_CHARACTERISTICS שמכיל את הpointers ל callbacks של NDIS לדוגמא :
SendNetBufferListsHandler נקרא עבור הודעות שנשלחות ל nic .
הcallback של SendNetBufferListsHandler מכיל referance ל NBL שמכיל את ה row data שנשלח בצורה של network buffer list .
בסיום עיבוד המידע הdriver חייב לקרא ל NdisMSendNetBufferListsComplete על מנת להמשיך את דרכו של ה NBL לעבר ה Miniport adapter של ה ndis
בישום שלנו היינו צריכים רק לשנות את ה payload בתנאים מסוימים כך שכל הודעה שהתקבלה ב callback אחרי עיבדו סינכרוני של המידע מיד המשכנו את התהליך על ידי קריאה ל NdisMSendNetBufferListsComplete ,יחד עם זאת ניתן לעבד את המידע אסינכרונית לקבוץ ולהפריד הודעות (חשוב להקפיד על הסדר של השליחה מהcallback).
הישום שלנו ממש את הטיפול ב SendNetBufferListsHandler בצורה הבאה

בפוסטים הבאים אני אפרט את המימוש של ה filter לעומק.
קיבלתי את השאלה הזו באיימיל :
לצבי שלום
אנו כותבים אפליקצית תלת מימד ב directx 11 ומעונינים להוסיף ל scene שלנו מראות .
איך אפשר לעשות את זה באופן פשוט ?
ובכן יש מספר שיטות להוספת מראות ל scene .
בדרך כלל אנו מקבלים מהגרפיקאי הגדרות של מיקום המראות בנוסף להגדרות shading של האוביקטים שצוירו במראה .
(הShading של התמונה במראה נועדה למנוע תחושה של השתקפות של 100% כמו מראה מושלמת אלא לבצע אפקטים שישנו את הדמות המשתקפת )
תפקידנו כתכנתים לדאוג להפיח חיים במראה כלומר לדאוג שלתהליך ההשתקפות הדינמי שכאשר דמות עוברת ליד המראה המראה תשקף אותה .
השיטה שאני אציג כאן מורכבת משלושה תהליכים מרכזים :
השלב הראשון הינו רינדור שטח המראה לתוך ה stencil buffer כלומר יצירת שטח ב stensil buffer אשר מיצג את שטח המראה .
השלב השני רינודר הscene מנקודת מבט של המראה .
השלב השלישי סידור הz – buffer וה Setnecil buffer של ה Scene כך שפעולות הציור הבאות התור לא ישנו דברים בדמות שצוירה במראה .
מכיוון שאנו עובדים עם Direct3d11 התהליך יכול להתבצע בdeffred context על מנת למקבל את תהליך הרינדור כמו כן יש לזכור שתהליך רינדור כל
ה scene בשביל ההשתקפות במראה הינו תהליך יקר מבחינת ביצועים בדרך כלל המראה יחסית למסך קטנה ויש מקום להרבה אפילו הרבה מאוד אופטימיזציות .
חשוב לא לרנדר מראות שלא מופיעות בscene ברגע נתון לעבוד עם back buffer culling נכון ועוד .
בדמו MultithreadedRendering11 אשר נמצא בsdk יש דוגמא לרנדור מראות בשיטה הזו .
הרינדור של המראות בDemo הזה מתבצע בפונקציה :RenderMirror אשר מקבלת את ה ID3D11DeviceContext
שעליו צריכים לצייר את המראות .
התרשים זרימה של הפונקציה הזו הינה :
בפוסטים הבאים אני אנסה לתאר לעומק את כל אחד משהלבים הן מבחינת Direct3d api והן מבחינה מתמטית של הscene
מיקרוסופט נכנסה לתחום של סלולאר בעמדה נחותה .בשוק ישנם כבר כמה שחקנים ראשים :אפל ואנדרואיד
שכשהם מסתכלים אחורנית הם לא רואים את מיקרוסופט בתחום .
הנקודה המאוד חזקה ואולי היחידה של מיקרוסופט לפי דעתי הינה כלי הפיתוח .
תכנת יכול בחינם להוריד את2010 Visual studio express מאוד בקלות להתקין את התוספים לצורך פיתוח ב Windows phone 7 התכנות בsilver light הינו קל פשוט תוך פרק זמן מינימלי אפשר להרים אפליקציה בינונית. וגם אפליקציה מורכבת זה לא דבר מסובך.
הטעות הפטאלית שלצערי מיקרוסופט עושה זה שהיא מונעת בכל מחיר את הקשר הישיר בין המפתח לצרכן על ידי זה שהיא מחייבת שכל אפליקציה שמפותחת לwindows phone 7 תימכר לצרכן דרך Windows Phone Marketplace שלה תוך כדי שהיא גוזרת קופון . ומכתיבה תנאים לתוכן האפליקציה וכד .
הקשר הישיר בין המפתח לצרכן לפי דעתי זה מה שבנה את מיקרוסופט ללא המודל הזה ,אם על כל אתר אינטרנט שנכתב או כל אפליקציה שנכתבה לwindows היה המפתח מחויב למכור אותו דרך מיקרוסופט ,מיקרוסופט לא היתה מגיע לאן שהיא הגיע .
אומנם אפל עובדת באותה שיטה והיא מצליחה אבל מיקרוסופט צריכה להביא את הadd in value שלה על מנת להאבק בנחיתות החומרתית ובמספר האפליקציות שהמתחרים מציגים .
אם לי יש סוכנות של מוצר צריכה עם 4 עובדים משווקים ואני יכולתי לרכוש עבורם מכשיר נייד ולהשקיע מינימום שבמינימום בפיתוח אפליקציה שרובה hard coded ופעם בשנה לבצע עדכונים זה היה פותח שוק גדול בפני מיקרוסופט לפי דעתי .
לפי דעתי מפתחים ויזמים לא אוהבים את הרעיון שמישהו יפלטר אותם ויכתיב להם תנאים והגבלות הן לאפליקציה והן למודל העסקי .
אחת הבעיות הגדולות שיש לנו בסימולטור הינה אובייקטים תלת מממדיים המכילים כמות עצומה של פוליגונים ישנם אובייקטים המגיעים לסדר גודל של מיליון פוליגונים באוביקטים כאילו הVertex Buffer יכול להגיע לסביבות 30M הindexBuffer יכול להגיע ל 15M .כאשר יש מספר אוביקטים כאילו מפורטים בscene המרונדרים בLOD 1 המערכת נחנקת גם בחומרה מקצועית ביותר .
בעיה נוספת שיוצרים אובייקטים כאילו גדולים הינה ברגע שצריכים לבצע חישובים כדוגמת Collosion Detection הדבר כמעט בלתי אפשרי מכיוון שמעבר על כל ה vertex ים בחישובים דורש כח חישוב רב .
בעבר כבר הומצאו הרבה פיתרונות לבעיה כדוגמת Bump Maps שהינה texture resource המכיל ערכי Normal של surface .כאשר משתמשים בטכנולוגיה של bump map ב pixel shader בזמן חישוב את עוצמת האור של pixel מתחשבים עם בערך הדגם מ Bump map וכך יוצרים אשלייה יש משטח לא חלק , זו שיטה שאומנם עובדת ובהרבה אפליקציות משתמשים בה אל היא די פרמטייבית בעלת הרבה חסרונות בעיקר מכיוון שהיא בעצם לא משנה את האוביקט ואז לדוגמא הצל של האוביקט שונה ממה שהוא היה צריך להיות במציאות .
השיטה שכרגע נחשבת לטובה ביותר הינה displacement map שיטה זו בניגוד ל שיטות כמו bump mapping, normal mapping, and parallax mapping כן משנה את מיקום הvertex . (בהזדמנות אני אכתוב על displacement map לעומק ).כדי להשתמש ב displacement map יש להכין את ה mesh עליו רוצים לעבוד על ידי חלוקתו לתתי פוליגונים .
כפי שרואים בתמונה בשורה השניה התהליך של Displacment map מתחיל על ידי mesh בעל רזולוציה נמוכה העובר פעולת tesselation כלומר כל vertex מחולק לתת פוליגונים אשר מוזזים בשלב השני על פי הdisplacment map. השורה הראשונה באיור מציגה את tessaltion של map לפי רמות .
לשיטה הזו גם יתרונות לגבי lod של פריטים ב scene לדוגמא אם ניקח עדר של חיות ,בעלי החיים שאנו רוצים ל רנדר עם lod גבוהה אנו נבצע להם פעולת tesselation עמוקה לאומת חיות שנמצאות רחוק מהמצלמה ועליהם או שלא נבצע כלל או שנבצע tesselation ברמה נמוכה .
פעולת הtesselation צורכת כח חישוב רב שעד לא מזמן לא היה אפשרי לבצעו ב render חי של משחק וכד , אפליקציות שהיו צריכות לבצע tesselation ביצועו את הפעולה ב offline .
ב Direct3d 10 כבר התחילו לנסות ולפתור את הבעיה על ידי חלוקת הmesh לתת פוליגונים באמצעות הGeometry shader , זה עבד בחומרה חזקה בצורה סבירה אבל עדיין זה לא היה זה .
Direct3d 11, כבר תומך בפעולה הזו מול רכיבים ייעודיים בחומרה שמטרתם לבצע את פעולת הdisplacment . (המוצר הראשון שתמך ביכולת הזו אומנם בצורה פחות חזקה הינו הxbox 360 ).
פעולת הtesselation מתבצעת מול כל סוגי הprimitive או ה patches ( ב Direct3d 11 נוספו עוד סוגי resource בנוסף לprimitive שיכולים להיכנס ל Input assembler stage שמיועדים לתמיכה ב tesselatio ה petches) .
התהליך של הtesselation מורכב מ חלוקה (sub divition ) של ה primitive ומיפוי כל vertex חדש ל mesh .
לצורך תמיכה ב tesselation נוצרו עוד 3 שלבים ב direct3d pipline של direct3d 11:
שלושת השלבים החדשים אשר מופעים בירוק יעדם תמיכה ב hardware tesselation .
כאשר 2 מהם הם ניתנים לתכנות ואחד הינו קבוע אבל אפשר לשלוט על הפרמטרים של דרכי פעולתו .
הVertex shader
מבצע את אותן הפעולות כמו בעיבודים שאינם כוללים tesselation
אם זה טיפול בכל הנושא של transformation של המרות ל מרחבים הרצויים של ה vertexים וכד
הHull Shader
זהו שלב ההכנה ל tesselation זהו שלב שהוא fully programmable
לשלב הזה יש שני תפקידים
1.לקבוע את מקדם החלוקה של כל נקודה: tessellation factors , כל נקודה יכולה לקבל ערך בין 1 ל 64 (כאשר ערכים שלא בתחום כמו 0 יגרמו לנקודה לא להשתתף בתהליך ) בכניסה לפונקציה שמבצעת את הtesselation מקבלים את כל הנקודות בpatch עבורן יש לקבוע את הערך של ה tessellation factors , היתרון בזה שאפשר לקבוע לכל נקודה באופן פרטני את ערך ה tesselation שלה הינו לצורך טיפול בlod )
2.התפקיד השני של ה hull shader זה לפעמים צריך לבצע טרנספורמציה של הנקודה בהתאם לאלגוריתם שיתאים ל tesselation לא תמיד מתבצעת טרנספורמציה בשלב הזה הרבה פעמים הcontrol points מעוברות כמו שהן .
הכניסה ל Hull shader ישנם בין 1 ל 32 נקודות שמרכיבות את ה patch עליו צריכים לבצע tesselation
והוא כאמור מוציא הם את הcontrol points שעברו טרנספורמציה אם נדרשת ואת ערכי ה factor .
כמו כן באמצעות attribute על הפונקציה של ה hull shader בדומה לattribute שלC# ב net . נקבעים פרמטרים בשביל השלב הקבוע של ה tesselation .
ה Tesselator
זה בעצם השלב שמבצע את פעולת הtesselation הוא אינו programmable אבל אפשר לשלוט עליו באמצעות פרמטרים התוצאה של שלב זה הינם בערכי uv של texture .
הDoamin Shader stage
הינו השלב שתפקידו לבצע את הטרנספורמציה של התוצר של ה tesselator שבא בצורת ערכי uv בחזרה לvertex ים .
הVertexים שנוצרו ב Domain shader ממשיכים ל geometry shader ומשם ההתנהגות של ה pipeline רגילה לחלוטין .
בpostים הבאים אני אפרט על כל השלבים , אופן התכנות שלהם וכמו כן אכתוב על הdisplacement map ושימושו.
אחרי שקראתי את http://blogs.microsoft.co.il/blogs/pavely/archive/2010/07/20/how-to-be-a-cool-c-programmer.aspx
חשבתי מה זה בעצם השטויות האילו של הסינטקס המסובך הזה בC#:
אני חושב שבאמת היתרון הגדול ביותר ובעצם המטרה של כל הSyntex ים של lambda expression , Linq וכד הינו ששימוש תדיר בהם חוסך לחלוטין ארועי code review מכוננים על ידי ראשי צוותים ומנהלים שפתאום הקוד נהפך בשבילם לבלתי קריא בעליל .
באמת פעם היו עושים צוותים שלמים + מנהלים ישיבות code review משותפת ארוע שנעלם כמו השקמים בתל אביב החיים בלי פאלפון ,טלביזיה עם ערוץ אחד ומערכת הפעלה לטלפוניים ניידים ,מחשבי כף יד מבית מיקרוסופט ישנם אגדות עם שפעם עשו את זה .
כמו בסיפור בגדי המלך החדשים ראשי הצוותים והמנהלים שלא מתעסקים ברמה היום יומית בקוד לא יודו שהם לא מבינים כלום .אלא רק יסתכלו על הקוד עם עיני עגל משתהים וחושבים בלב :מה קרא לנו ? איך פתאום אנו לא מבנים כלום בתכנות ,אסור שאף אחד יגלה את זה אחרת אנו אבודים במקרה ויצמצמו צוותים ונצטרך לשוב להיות תכנתים .יכול להיות שכולם מבינים מה כתוב כאן חוץ ממני ?.
איזה שטות עשיתי שהסכמתי שנעבור ל.net מה רע היה לנו עם visual studio 6.0 C++ הבנתי אז יופי את הכל ,בעצם כמעט את הכל חוץ מsmart pointers ו templates מסובכים . בעצם גם על copy constractor מעולם לא הייתי סגור.
בכל מקרה אני חייב להגיד עכשיו משהו חכם ומהר . אולי אני העיר שאין מספיק הערות בקוד ? לא לא רעיון טוב הם יחשבו שאני רוצה הערות כי אני לא מבין את הקוד.
צריך להמשיך להביט בבליל מילים וסימנים האילו בפרצוף רציני שנראה כאילו קוזמו קרמר בפרק שהוא הצטרף לאיזשהיא עבודה במקרה כי הוא גרי וגורג חיפשו שירותים בבנין כתב את הקוד .
הסינטקס המוזר התחיל בהרבה קודם מ Linq ו lambda expressions כאשר הם נתקלו בsyntex של c# property רק עם get;set;
אני חושב שמיקרוסופט הבינו את הצורך והמצוקה של התכנתים שראשי הצוותים ומנהלים לסוגיהם לא יתערבו להם בקוד ויתנו לפתח בשקט backdoors ,פיצרים שהם קולים אבל באמת שאף אחד לא צריך וכד ואז מיקרוסופט פיתחו רעיון גאוני בצורת שפת תכנות חדשה שאף אחד לא מבין
שפת תכנות המבוססת על צופן גרמני סודי היחיד של פוצח על ידי המודעין הבריטי
קבלו את השלמה ארצי של כל שפות התכנות #F
אני יודע שבאחת מהתעשיות הביטחוניות במספר צוותים כולל הצוות שעובד על מערכות האוויוניקה של מטוס הקרב העתידי של מדינת ישראל הלביא (אין לכם מושג איזה ביטחון תעסוקתי נותן ועד עובדים חזק שלא מרשה שסתם כך יסגרו פרויקטים על ימין ושמאל מבלי סיבה רצינית) התחילו לפתח בF# ההוראה שיצאה מהקב"ט של החברה הינה שמחשבים הנושאים קוד F# ללא קשר לסיווג הפרויקט יכולים להיות מחוברים ישירות לinternet אפילו בלי fire wall או אנטי וירוס הסינטקס של השפה מספק הגנה אולטימטיבית מפני כל מי שמנסה לפענח את הקוד של שפה שהיא בעצם שילוב של דוטפרוסטראקטור ושפת תיכנות.
והוא כלומר הקב"ט מתקשה מאוד לדמיין את אחמידניגד , נאסארלה ,או חאפז אל אסד גוניור וב"ב בכובע של השחקן מנפלאות התבונה מנסה לפצח את הסוד השמור מכולם איך לכול הרוחות והשדים סוגרים לולאה בשפה המקוצמת הזו .
אני זוכר שבבית ספר התיכון (הייתי בבית ספר תיכון של חנונים לא של יובל בנאי ) לימדו אותנו ששפת תיכנות אז קראו לזה שפות הדור השלישי תוכננו כך שהם היו דומות לשפה אנושית ויחסכו את הצורך להתעסק עם assembler ב F# נראה לי שעוברים לassembler כדי להבין למה הקוד מתכוון .
בגרסאות קודמות של Direct3d הופיע Demo בשם DisplacementMapping10 שבא להדגים את היכולת של Displacement Mapping בהשוואה ל לטכניקות פשוטות יותר כמו Normal mapping .
Displacment Mapping demo
התוצאה של הDemo הינה רידנור של לטאה כאשר משתמשים ב Displacement Mapping ניתן לשנות את המעטפת של הMesh (רינדור הסנפיר על הגב של הלטאה ) דבר שהינו בילתי אפשרי בטכנולוגית Normal mapping המצויה .
בהשוואה ל רינדור עם Displacment mapping
הבעיה עם ה Demo הזה לפי דעתי היתה איטיות גם בכרטיסים לא חזקים לכן אולי הוא ירד .
הטכניקה של Displacment mapping (קימות כל מיני וריציות ) קיבלה boost בDirect3d11 בשל השימוש ב Hardware tessellation stage שמאפשר פיצול פוליגונים שהינם חלק מה mesh לפוליגונים קטנים .
מכיוון שפעולה זו מתבצעת על ידי החומרה של כרטיסי מסך תומכי Direct3d11 יש שיפור ניכר בביצועים של אפליקציות וdemos שמשתמשים בטכנולוגיה של Displacement mapping .
Tangent space שלפעמים נקרא גם Texture Space הינו מרחב בעולם התלת מימד שנוסף ל World, View ,Projection שייעודו הינו לחסוך משאבי GPU של חישובי טרנספורמציות של בPixel Shader .
כאשר מיישמים Per Pixel lighting יש הרבה טכנולוגיות שדורשות שלכל Texel בTexture יוצמד גם ערך של Normal דוגמא לטכנולוגיה כזו הינה : Normal mapping.
בד"כ כלל לאובייקטים התלת מימדיים עליהם אנו צריכים לישם טכנולוגית הקשורות ל per pixel light אנו מקבלים מהגרפיקאי שני קבצי Resource :קובץ של Color map וקובץ של Normal map כך שלכל Texel ב Color Map יש את וקטור הנורמל המשלים שלו ב Normal Map.
ערכי ה Texture מבוטאים על ידי color ערכי ה Normal מבוטאים על ידי vector מנורמל .
אם מגדירים את הערכים ב Normal Map כוקטורים ב object Space מכיוון שכאשר מיישמים חישוב של light ב Per pixel light גם ה Light Direction וגם ה Camera vector משתתפים בחישוב היינו צריכים על כל אחד מערכי ה Normal שדגמנו מה Normal Map לבצע טרנספורמציה למרחב בו מוגדרים הוקטורים של ה Light ו ה Camera . דבר זה יגרום לעומס עבודה ב Pixel shader .
על מנת לחסוך את הטרנספורמציה הזו מגדירים מרחב חדש בשם Tangent space
מרחב הצירים בTangent space מוגדר כ:
ה Vector X מוגדר לאורך ה U של ה Texture מוגדר כ Tangent
ה Vector Y מוגדר לאורך הV של ה Texture מוגדר Bitangent
ה Vector Z מוגדר כנורמל של ה Surface . מוגדר כ Normal
כאשר עובדים עם Tangent Space הNormals ב Normal maps מוגדרים ב Tangent space .
ערכי ה Normal נדגמים מהResource view על ידי ה PixelShader בדומה לדרך שבה דוגמים Texture רגיל כאשר ערכי ה xyz נמצאים ב ערכי הrgb
float3 n = normalize(tex2D(normalMap, IN.texCoord).rgb * 2.0f - 1.0f);
עבור כל polygon את ה light direction וה Camera vector אנו צריכים להמיר ל.Tangent space פעולה זו מתבצעת עבור כל וקטור ב Vertex Shader וכך אנו מקבלים ב Pixel shader את כל הוקטורים באותו מרחב (Tangent space ) דבר שחוסך לנו טרנספורמציות עבור כל texel .
על מנת להשתמש עם Tangent space אנו צריכים לבצע את הפעולות האילו
נצרף אלמנט של Tangent ל
D3D10_INPUT_ELEMENT_DESC
{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D10_INPUT_PER_VERTEX_DATA, 0 },
אשר יוכנס לפקודת CreateInputLayout
מכיוון שיש לנו גם את הערך של ה Tangent וגם את הערך של הNormal אנו חוסכים את המרכיב של ה Bitangent בכל Vertex אותו נחשב בVertex Shader באמצעות Cross product .
במקרה והobject שאנו הולכם לרנדר לא מכיל ערכי Tangent מחושבים עבור כל ווקטור אנו צריכים לחשב את הערכים האילו לפני ששולחים את הVertexים לInput-Assembler Stage .
יש הרבה דוגמאות קוד ברשת לקוד שעובר על כל הפוליגונים של ישות ומחולל ומחולל לכל Vertex את ערכי ה Tangent שלו .
ב Vertex Shader אנו כאמור צריכים להמיר את המרחב של ה Viewer vector והlight direction ל Tangent space
השלבים ביצירת מטריצת ההמרה לTangent space הינם :
אנו כופלים את הערכים של ה Vertex במטריצה ההופכית המשוחלפת של ה World transformation
float3 n = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);
float3 t = mul(IN.tangent.xyz, (float3x3)worldInverseTransposeMatrix);
אנו מחשבים את רכיב ה Bitangent על ידי Corss product כאשר ה ערך של IN.tangent.w מכי לערכים או של 1- או 1 שמצבעים על הקוטביות של ה Tangenet space coordinates
float3 b = cross(n, t) * IN.tangent.w;
מרכיבים את מטריצת ההמרה למרחב ה Tangent sapce
float3x3 tbnMatrix = float3x3(t.x, b.x, n.x,
t.y, b.y, n.y,
t.z, b.z, n.z);
המטריצה קודם מחזירה את הvetcor ל מרחב העצם ממרחב העולם ואח"כ מעבירה את העצם למרחב ה Tangent
כעת שיש לנו את מטריצת ההמרה אנו מכפילים את ערכי ה Light direction וה camera vector ב מטריצת ההמרה .
את הערכים המומרים שקיבלו אנו מציבים בstruct התוצאה של ה Vertex shader והם ישמשו ב חישוב ה light ב pixel shader .
כאשר בשלב של ה Pixel shader כל הוקטורים או הערכים שמשמשים לחישובי אור וכד נמצאים במרחב ה Texture דבר שחוסך לנו המרות של וקטורים ב pixel shader .
איזה דוד שאל שאלה בפורום תמיכה של Codeproject .
יש לו אפליקציה שמרנדרת Scene של Direcrt3D והוא מעוניין לשלב את התוצאה שלה עם streaming של וידיאו באמצעות DirectShow filter .
הוא שואל איך עושים את זה ?
ובכן לפי דעתי הכיוון הוא לא נכון .שילוב נכון של directshow video ו Direct3d עדיף שיתבצע באמצעות התשתית של Direct3d ולא של Directshow .
לכך יש מספר סיבות .
1.התשתית של Direct3D מתעדכנת טכנולוגית ומקבלת תשומת לב ממיקרוסופט בניגוד ל Directshow שתקוע כבר למעלה מ10 שנים באותו מקום .
2.על מנת לעבוד את התוצאה של הdx pipeline בתוך directshow filter צריכים גישה של הcpu לתוצר של הoutput merger stage.
אומנם הרינדור של Direct3d יכול להתבצע על Resource מסוג Texture אבל הResource חייב להיות מוגדר עםdefault usage שהכוונה שרק ל GPU יש הרשאת כתיבה וקריאה מה Resource הCPU לא יכול לגשת לTexture שהוגדר כ ID3D10RenderTargetView .
3.גם אם היינו משיגים את התוצאה של הdx pipeline המיזוג שלה עם הsurface של הdirectshow הינה פעולה יקרת משאבים מבחינת CPU סביר להניח שהפורמטים שונים ויש מצב שעל כל byte נצטרך לבצע פקודת if בCPU + הצבות וחישובים ,וכל frame מורכב מ 640 * 480 קריאות כאילו .
מניסיון מחשב חזק יכול לתמוך בפעולה כזו אבל חלק ניכר מהמשאבים כל הזמן ילך על הפעולה הזו .
4.בdirectshow filter אין גישה לכל נתוני התוצר של הoutput merger stage הכוונה לZ- Buffer ו stencil Buffer דבר שלא יאפשר לבצע חישובי הסתרות וכד כלומר הוידיאו לא חלק מהעולם של ה3D.
5.הוידיאו לא יהיה חלק אינטגרלי מהעולם כלומר כל חישובי ההצללה והתאורה לא יכללו בvideo מכיוון שהם נעשים בPixel shader .
בפוסט הבא אני אשתדל לכתוב איך לפתור את הבעיה באמצעות הפיכת ה Video Surface לDirect3D texture resource ושילובה בrendering של ה Sence .
לפעמים יש צורך לשלב בין שני scene ים ששתי אפליקציות Direct3D שונות יוצרות לתוך Scene אחד .
לדוגמא: אני רוצה להשתמש באפליקציה שלי בBing maps לצורך יצירת ועדכון הterrain ומעל הTerrain אני צריך לרנדר את האובייקטים שלי .
בניגוד למקרה עם אפליקציות 2D שאולי אפשר לשלב תמונות על ידי משחק עם ערכי alpha המצב בעולם של התלת מימד מורכב בהרבה .
הטכנולוגיות יכולות להיות מדור שונהBing עובד מול Direct3d9 ואני עובד עם Direct3d11 .כל ה api והאובייקטים שונים מאוד .
פעולת הrender חייבת להיות משותפת על מנת שהZ-buffer filter יפעל גם על האובייקטים שלי וגם על האובייקטים של Bing .כלומר אם מכונית שאני מרנדר נמצאת מאחורי הר שbing מרדנר ההר צריך להסתיר את המכונית שאני מרנדר .
פעולות בshaders של הצללה ואור צריכות להיות משותפות .(הקונספט של שמש בגבעון דום וירח בעמק איילון לא תופס כאן ) אותו מקור תאורה משפיע הן על האובייקטים שלי והן על האובייקטים המיובאים .
כמו כן יש אוביקטים בscene של Bing שאני לא רוצה שירונדרו אצלי באפליקציה אני רוצה רק את הTerrain נטו מבלי כפתורים שBing מרנדרת על הscene שלה .
מכיוון שאי אפשר לשתף את הsurface ים בין האפליקציות וbing לא פותח לנו api של קבלת הResource ים מbing (הVertexים והTexture ים ) אנו צריכים להוציא את הResources מBing לא בדרך המלך.
הפתרון שמימשנו
הפתרון מתבסס על יירוט של הקריאות ל dx api על ידי Bing .
יצרנו אפליקצית .net win form שמריצה WebBrowser Control ולתוך ה WebBrowser control אנו טוענים עמוד html מקומי שמציג את Bing אפשר לשלוט על Bing באמצעות פקודות של automation פשוטת של בחירת המיקום בעולם שהמפה מציגה וכד .
אנו יוצרים proxy dll שתפקידו ליירט קריאות מBing ל d3d9.dll ישנם מספר דוגמאות לשלד של dll כזה במרחבי ה Web . הDLL בשם d3d9.dll ממוקם בספריה שמריצה את האפליקציה עם Bing ולכן Bing טוען את הProxy dll שלנו כאשר הוא קורא לCreateDevice
אנו כתבנו proxy ל interface ים החשובים של Dx9 אשר הם נקראים במקום האורגינלים .
ברגע שאנו מיירטים פקודה שBing פקד וזו פקודה שמעדכנת Resource אנו מעתיקם את המידע שלה ל shared memory שמשותף ל dll המירט ו האפליקציה שלנו וכך יש לנו ביד את הpre resource ואנו מכניסים אותו ל pipline שלנו בתהליך הRender שלנו .
אנו צריכים לקלף או להתאים את הResource לDX11 יש שוני מהותי בין העבודה עם Resources בDX9 שהשתנה עם המעבר ל DX10 יש שונה בהדרת הSurfaceים וכד
אין צורך שתהליך הירוט יעביר את קריאות הRender ל d3d9.dll האורגינל חבל על תעבורה על הBus המיותרת ועבודת הכרטיס מסך.יש פקודות בעיקר אתחול שכן צריכות לקרא ל d3d9.dll המקורי על מנת ש bing יוכל לאתחל את העבודה שלו. הכל בתלות במשחק של trial and error .
בפוסט הקודם כתבתי על שימוש ב Billboard לטובת יצירת עצים על Terrain התוצאה של Billboard הינה בסדר אבל לא מצוינת הרי עצים לא יכולים להיות סימטריים לחלוטין מכל כיוון ותנועה של המצלמה מוגבלת לציר Y .
אצלנו באפליקציה נדרשנו לעצים יותר מציאותיים וכמו שחקלאי בעל מטע אבוקדו לא מגדל את העצים שלו לבד מאפס מגלעין בתוך כוס של אשל עם קיסמי שיניים אלא קונה שתילים מוכנים ככה יש חברה שמוכרת עצים תלת מימדים באינטרנט: SpeedTree . הם מוכרים גן בוטני שלם תלת מימדי. קצת הרבה יקר אבל יחסית להעסיק מעצב גרפי + תכנת שיצרו עצים מ 0 זה סיפור בהרבה יותר יקר.
לשמחתי לא אישרו לי לקנות עצים כאילו מוכנים לכן נאלצתי להפשיל שרוולים וליצור עצים לבד עם סיוע של מעצב גרפי ומאיה .
ברינדור העץ יש שני חלקים כאשר כל חלק עם האתגר שלו .
הענפים והעלים :שכאן האתגר הינו בעיקר להתמודד עם הכמות הגדולה של עלים וענפים כאשר איננו רוצים לחנוק את הbus ואת ה gpu עם מיליוני הvertexים כאן הGeometry Shader הינו מכשיר יעיל ביותר כאן אני אגע בנושא זה בהמשך .
הגזע של ה עץ : עצים מכיוון שהם יצורים אורגנים חיים הם לא בנויים כגוף חלק כמו מטוס אלא מהרבה הרבה בליטות וזיזים .כאשר החלק המאתגר נמצא בעיקר בשוליים של העץ. הריבועים הכחולים בתמונה .
את הפנים של העץ (ריבוע צהוב ) ניתן לרנדר באמצעות טכנולוגיות פשוטת ועתיקות כגון BumpMapping .
הבעיה עם BumpMapping שטכנולוגיה זו איננה יודעת להתמודד עם השוליים של אוביקט כמו כן כאשר המצלמה זזה לא רואים את השינוי בעומק של הקליפה.
יש מספר טכנולוגיות שמשפרות את הביצועים של bumpmapping כגון :
parallax occlusion mapping
Relief mapping
displacement mapping
כאשר לכל אחד מהן יש את המגרעות שלה ( displacement mapping למרות שמסוגלת אלגוריטמית לתת את הפתרון חונקת כל כרטיס מסך שאיננו תותח על ).
בפוסטים הבאים אני אתאר את הפתרון שלנו לגזע ובהמשך לעלים .
More Posts
Next page »