Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Bạn có thể chẩn đoán một số vấn đề có thể xảy ra về hiệu suất liên quan đến đỉnh bằng cách sử dụng tính năng lập hồ sơ khung hình. Sử dụng ngăn Commands (Lệnh) để xem tất cả các lệnh gọi vẽ mà trò chơi của bạn thực hiện trong một khung hình nhất định và số lượng các thành phần cơ bản được vẽ trên mỗi lệnh gọi vẽ. Điều này có thể giúp bạn ước tính tổng số đỉnh được gửi trong một khung hình.
Hình 1. Khung hiển thị lập hồ sơ khung hình cho một lệnh gọi glDrawElements duy nhất, cho thấy 2.718 nguyên hàm tam giác được vẽ
Nén thuộc tính đỉnh
Một vấn đề thường gặp mà trò chơi của bạn có thể gặp phải là kích thước đỉnh trung bình lớn. Một số lượng lớn đỉnh được gửi với kích thước đỉnh trung bình cao sẽ dẫn đến băng thông đọc bộ nhớ đỉnh lớn khi được GPU đọc.
Để quan sát định dạng đỉnh cho một lệnh gọi vẽ nhất định, hãy hoàn tất các bước sau:
Chọn một lệnh gọi vẽ mà bạn quan tâm.
Đây có thể là một lệnh gọi vẽ thông thường cho cảnh, một lệnh gọi vẽ có số lượng lớn đỉnh, một lệnh gọi vẽ cho một mô hình nhân vật phức tạp hoặc một số loại lệnh gọi vẽ khác.
Chuyển đến ngăn Pipeline (Quy trình) rồi nhấp vào IA để lắp ráp đầu vào.
Thao tác này xác định định dạng đỉnh cho các đỉnh đi vào GPU.
Quan sát một loạt thuộc tính và định dạng của chúng; ví dụ: R32G32B32_SFLOAT là một số thực có dấu 32 bit gồm 3 thành phần.
Hình 2. Tập hợp đầu vào cho một lệnh gọi vẽ, với các thuộc tính chưa nén dẫn đến kích thước đỉnh là 56 byte
Thông thường, các thuộc tính đỉnh có thể được nén mà không làm giảm chất lượng của các mô hình được vẽ. Cụ thể, bạn nên:
Nén vị trí đỉnh thành các giá trị bán chính xác 16 bit
Nén toạ độ UV hoạ tiết thành ushort số nguyên không dấu 16 bit
Nén không gian tiếp tuyến bằng cách mã hoá các vectơ pháp tuyến, tiếp tuyến và binormal bằng quaternion
Các thuộc tính khác cũng có thể được xem xét cho các loại có độ chính xác thấp hơn tuỳ theo từng trường hợp.
Tách luồng Vertex
Bạn cũng có thể tìm hiểu xem các luồng thuộc tính đỉnh có được phân chia một cách thích hợp hay không. Trên các kiến trúc kết xuất theo ô như GPU di động, vị trí đỉnh được dùng trước tiên trong một lượt phân loại để tạo các ô nguyên thuỷ được xử lý trong mỗi ô. Nếu các thuộc tính đỉnh được đan xen vào một vùng đệm duy nhất, thì tất cả dữ liệu đỉnh sẽ được đọc vào bộ nhớ đệm để phân loại, ngay cả khi chỉ sử dụng vị trí đỉnh.
Để giảm băng thông bộ nhớ đọc đỉnh và cải thiện hiệu suất bộ nhớ đệm, từ đó giảm thời gian dành cho lượt truyền phân loại, dữ liệu đỉnh phải được chia thành hai luồng riêng biệt, một cho vị trí đỉnh và một cho tất cả các thuộc tính đỉnh khác.
Cách kiểm tra xem các thuộc tính đỉnh có được chia tách phù hợp hay không:
Chọn một lệnh gọi vẽ mà bạn quan tâm và ghi lại số lệnh gọi vẽ.
Đây có thể là một lệnh gọi vẽ thông thường cho cảnh, một lệnh gọi vẽ có số lượng lớn đỉnh, một lệnh gọi vẽ cho một mô hình nhân vật phức tạp hoặc một số loại lệnh gọi vẽ khác.
Chuyển đến ngăn Pipeline (Quy trình) rồi nhấp vào IA để lắp ráp đầu vào. Thao tác này xác định định dạng đỉnh cho các đỉnh đi vào GPU.
Quan sát các liên kết của thuộc tính đỉnh; thông thường, các liên kết này có thể tăng tuyến tính (0, 1, 2, 3, v.v.), nhưng không phải lúc nào cũng như vậy.
Vị trí đỉnh thường là thuộc tính đỉnh đầu tiên được liệt kê.
Trong ngăn State (Trạng thái), hãy tìm LastDrawInfos và mở rộng số cuộc gọi vẽ khớp. Sau đó, hãy mở rộng BoundVertexBuffers cho lệnh gọi vẽ này.
Quan sát các vùng đệm đỉnh được liên kết trong lệnh gọi vẽ đã cho, với các chỉ mục khớp với các liên kết thuộc tính đỉnh từ trước đó.
Mở rộng các liên kết cho thuộc tính đỉnh của lệnh gọi vẽ và mở rộng các vùng đệm.
Quan sát VulkanHandle cho các vùng đệm, đại diện cho bộ nhớ cơ bản mà nguồn dữ liệu đỉnh đến từ đó. Nếu VulkanHandle khác nhau, điều này có nghĩa là các thuộc tính bắt nguồn từ các vùng đệm cơ bản khác nhau. Nếu các VulkanHandle giống nhau nhưng độ lệch lớn (ví dụ: lớn hơn 100), thì các thuộc tính vẫn có thể bắt nguồn từ các vùng đệm con khác nhau, nhưng bạn cần điều tra thêm.
Hình 3. Tập hợp đầu vào cho một lệnh gọi vẽ, với bảng điều khiển trạng thái ở bên phải cho thấy rằng các thuộc tính tại liên kết 0 và 1, vị trí đỉnh và vị trí bình thường, dùng chung một vùng đệm cơ bản duy nhất
Để biết thêm thông tin chi tiết về việc phân chia luồng đỉnh và cách giải quyết vấn đề này trên nhiều công cụ phát triển trò chơi, hãy xem bài đăng trên blog của chúng tôi về chủ đề này.
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2025-07-27 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 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."]]