קל לארגן דפים בעזרת אוספים
אפשר לשמור ולסווג תוכן על סמך ההעדפות שלך.
אפשר לאבחן כמה בעיות אפשריות בביצועים שקשורות לקודקודים באמצעות פרופיל של פריימים. בחלונית Commands אפשר לראות את כל קריאות הציור שהמשחק מבצע בפריים נתון, ואת מספר הפרימיטיבים שמצוירים לכל קריאת ציור. כך אפשר לקבל הערכה של המספר הכולל של הקודקודים שנשלחו בפריים בודד.
איור 1. תצוגת פרופיל של פריימים לשיחה אחת, שבה מוצגים 2,718 פרימיטיבים של משולשים ששורטטוglDrawElements
דחיסת מאפיינים ב-Vertex
בעיה נפוצה במשחקים היא גודל ממוצע גדול של קודקודים. מספר גדול של קודקודים שנשלחים עם גודל קודקוד ממוצע גבוה מוביל לרוחב פס גדול של קריאת זיכרון קודקודים כשקוראים אותם על ידי ה-GPU.
כדי לראות את פורמט הקודקוד של קריאה מסוימת לציור, מבצעים את השלבים הבאים:
בוחרים קריאה לציור שמעניינת אתכם.
זה יכול להיות קריאה רגילה לציור של הסצנה, קריאה לציור עם מספר גדול של קודקודים, קריאה לציור של מודל מורכב של דמות או סוג אחר של קריאה לציור.
מנווטים לחלונית Pipeline ולוחצים על IA כדי להרכיב את הקלט.
ההגדרה הזו מגדירה את פורמט הקודקודים של קודקודים שנכנסים ל-GPU.
מתבוננים בסדרה של מאפיינים ובפורמטים שלהם. לדוגמה, R32G32B32_SFLOAT הוא מספר נקודה צפה חתום בן 32 ביט עם 3 רכיבים.
איור 2. הרכבת קלט לקריאה לציור, עם מאפיינים לא דחוסים שגורמים לגודל קודקוד של 56 בייט
בדרך כלל, אפשר לדחוס מאפייני קודקודים עם צמצום מינימלי באיכות של המודלים שנוצרו. במיוחד, אנחנו ממליצים:
דחיסת מיקום הקודקוד לנקודות צפות של 16 ביט בחצי דיוק
דחיסת קואורדינטות של טקסטורת UV למספרים שלמים לא מסומנים של 16 ביט (ushort)
דחיסת מרחב המשיק על ידי קידוד וקטורים נורמליים, וקטורי משיק ווקטורים בינורמליים באמצעות קווטרניונים
יכול להיות שגם מאפיינים אחרים שונים ייחשבו לסוגים עם דיוק נמוך יותר, בהתאם למקרה.
פיצול של זרם קודקודים
אפשר גם לבדוק אם זרמי מאפייני הקודקודים פוצלו בצורה מתאימה. בארכיטקטורות של רינדור מחולק למשבצות, כמו מעבדי GPU לניידים, המיקומים של הקודקודים משמשים קודם כל בשלב של מיון לתיבות כדי ליצור תיבות של פרימיטיבים שעוברים עיבוד בכל משבצת. אם מאפייני הקודקודים משולבים במאגר יחיד, כל נתוני הקודקודים נקראים למטמון לצורך סיווג, גם אם נעשה שימוש רק במיקומי הקודקודים.
כדי לצמצם את רוחב הפס של הזיכרון לקריאת קודקודים ולשפר את יעילות המטמון, וכך לצמצם את הזמן שמוקדש למעבר binning, צריך לפצל את נתוני הקודקוד לשני זרמים נפרדים, אחד למיקומי הקודקודים ואחד לכל שאר מאפייני הקודקודים.
כדי לבדוק אם מאפייני הקודקוד פוצלו בצורה מתאימה:
בוחרים קריאה לציור שמעניינת אתכם ורושמים את המספר שלה.
זה יכול להיות קריאה רגילה לציור של הסצנה, קריאה לציור עם מספר גדול של קודקודים, קריאה לציור של מודל מורכב של דמות או סוג אחר של קריאה לציור.
מנווטים לחלונית Pipeline ולוחצים על IA כדי להרכיב את הקלט. הפקודה הזו מגדירה את פורמט הקודקודים של קודקודים שנכנסים ל-GPU.
בודקים את הקישורים של מאפייני הקודקודים. בדרך כלל הם עולים באופן לינארי (0, 1, 2, 3 וכו'), אבל זה לא תמיד המצב.
מיקום הקודקוד הוא בדרך כלל מאפיין הקודקוד הראשון שמופיע ברשימה.
בחלונית State, מאתרים את LastDrawInfos ומרחיבים את מספר ההגרלה התואם. לאחר מכן, מרחיבים את BoundVertexBuffers עבור קריאת הציור הזו.
בודקים את מאגרי הקודקודים שנקשרו במהלך קריאת הציור שצוינה, עם אינדקסים שתואמים לקשירות של מאפייני הקודקודים מהשלבים הקודמים.
מרחיבים את ההתאמות של מאפייני הקודקוד של קריאת הציור, ומרחיבים את המאגרים.
שימו לב ל-VulkanHandle עבור המאגרים, שמייצגים את הזיכרון הבסיסי שממנו מגיע מקור נתוני הקודקוד. אם הערכים של VulkanHandle שונים, זה אומר שהמאפיינים מגיעים ממאגרי מידע שונים. אם ערכי VulkanHandle זהים אבל ההיסטים גדולים (לדוגמה, גדולים מ-100), יכול להיות שהמאפיינים עדיין מגיעים ממאגרי משנה שונים, אבל צריך לבדוק את זה לעומק.
איור 3. הרכבת קלט לקריאה לציור, עם חלונית המצב משמאל שבה מוצג שמאפייני הקישור 0 ו-1, מיקום הקודקוד והנורמל, חולקים מאגר בסיסי יחיד
לפרטים נוספים על פיצול של זרם קודקודים ועל פתרון הבעיה במנועי משחקים שונים, אפשר לעיין בפוסט בבלוג בנושא.
דוגמאות התוכן והקוד שבדף הזה כפופות לרישיונות המפורטים בקטע רישיון לתוכן. Java ו-OpenJDK הם סימנים מסחריים או סימנים מסחריים רשומים של חברת Oracle ו/או של השותפים העצמאיים שלה.
עדכון אחרון: 2025-07-27 (שעון UTC).
[null,null,["עדכון אחרון: 2025-07-27 (שעון UTC)."],[],[],null,["# Analyze vertex formats\n\nYou may diagnose a few possible vertex-related performance problems through the\nuse of frame profiling. Use the **Commands** pane to view all of the draw calls\nyour game performs in a given frame and counts of primitives drawn per draw\ncall. This can give an approximation of the overall number of vertices submitted\nin a single frame.\n**Figure 1.** Frame profiling view for a single `glDrawElements` call, showing 2,718 triangle primitives drawn\n\nVertex attribute compression\n----------------------------\n\nOne common problem your game may face is a large average vertex size. A\nlarge number of vertices submitted with a high average vertex size results in a\nlarge vertex memory read bandwidth when read by the GPU.\n\nTo observe the vertex format for a given draw call, complete the following steps:\n\n1. Select a draw call of interest.\n\n This can be a typical draw call for the scene, a draw call with a large\n number of vertices, a draw call for a complex character model, or some other\n type of draw call.\n2. Navigate to the **Pipeline** pane, and click **IA** for input assembly.\n This defines the vertex format for vertices coming into the GPU.\n\n3. Observe a series of attributes and their formats; for example,\n `R32G32B32_SFLOAT` is a 3-component 32-bit signed float.\n\n**Figure 2.**Input assembly for a draw call, with uncompressed attributes resulting in a vertex size of 56 bytes\n\nFrequently, vertex attributes can be compressed with minimal reduction in the\nquality of the models drawn. In particular, we recommend:\n\n- Compressing vertex position to half-precision 16-bit floats\n- Compressing UV texture coordinates to 16-bit unsigned integer ushorts\n- Compressing the tangent space by encoding normal, tangent, and binormal vectors using quaternions\n\nOther miscellaneous attributes may also be considered for lower-precision types\non a case-by-case basis.\n\nVertex stream splitting\n-----------------------\n\nYou can also investigate whether vertex attribute streams are appropriately\nsplit. On tiled rendering architectures such as mobile GPUs, vertex positions\nare first used in a binning pass to create bins of primitives processed in each\ntile. If vertex attributes are interleaved into a single buffer, all vertex data\nis read into cache for binning, even though only vertex positions are used.\n\nTo reduce vertex read memory bandwidth and improve cache efficiency, and thus\nreduce time spent on the binning pass, vertex data should be split into two\nseparate streams, one for vertex positions, and one for all other vertex\nattributes.\n\nTo investigate whether vertex attributes are appropriately split:\n\n1. Select a draw call of interest, and note the draw call number.\n\n This can be a typical draw call for the scene, a draw call with a large\n number of vertices, a draw call for a complex character model, or some other\n type of draw call.\n2. Navigate to the **Pipeline** pane, and click **IA** for input assembly. This\n defines the vertex format for vertices coming into the GPU.\n\n3. Observe the bindings of your vertex attributes; typically these might\n increase linearly (0, 1, 2, 3, etc.), but this is not always the case.\n Vertex position is typically the first vertex attribute listed.\n\n4. In the **State** pane, find the `LastDrawInfos` and expand the matching draw\n call number. Then, expand the `BoundVertexBuffers` for this draw call.\n\n5. Observe the vertex buffers bound during the given draw call, with indices\n matching the vertex attribute bindings from earlier.\n\n6. Expand the bindings for your draw call's vertex attributes, and expand the\n buffers.\n\n7. Observe the `VulkanHandle` for the buffers, which represent the underlying\n memory that the vertex data sources from. If the `VulkanHandle`s are\n different, this means the attributes originate from different underlying\n buffers. If the `VulkanHandle`s are the same but the offsets are large\n (for example, greater than 100), the attributes may still originate from\n different sub-buffers, but this requires further investigation.\n\n**Figure 3.**Input assembly for a draw call, with the state panel to the right showing that the attributes at binding 0 and 1, vertex position and normal, share a single underlying buffer\n\nFor more detail about vertex stream splitting and how to resolve it on various\ngame engines, see our [blog post](/agi/frame-trace/link-to-Omars-blog-post) on the subject."]]