Load Static Mesh Data

Data Load and Render Pass

Strictly speaking, Data Load is not the execution content of the rendering pass.

However, as the data source for the rendering Pass, it is necessary to discuss this part before discussing the Pass execution process.

There are 2 design styles:

  1. Upload on demand: When rendering a Pass, if it is found that the data for a Mesh is not in the VRAM, the data for that Mesh is uploaded from memory to VRAM, and then the pointer is used for rendering.
  2. Upload on load: When loading a Mesh, the data is directly uploaded to the VRAM. Then, during rendering Pass, only the corresponding address in the previously uploaded VRAM is obtained to render that Mesh.

Unreal Engine has chosen the second style.

How is the Static Mesh data required for rendering loaded? We have already discussed this question in the previous section, but in this part, we will delve into the various details.

Load Static Mesh Render Data from Disk

Let’s take a look at this small arrow

image

How the unreal engine load the FStaticMeshRenderDatafrom the disk?

You can image the process as this:

image
  • FStaticMeshRenderData::Serialize
    • For each FStaticMeshLODResources
      • FStaticMeshLODResources::Serialize
        • For each LOD section, load the section data
        • Load the vertex and index buffer.

From CPU Memory to GPU Memory

After loading the data from disk, we need to transfer the necessary data into GPU memory,

image

To illustrate how initialization is mapped to the graphics API, let's consider the position vertex buffer as an example.

This may be helpful for someone who wants to check the source code themselves.

Position Vertex Buffer Example

Let's take PositionVertexBuffer as an example.

The entire initialization process can be divided into two levels: the Renderer level and the RHI level.

The initialization process at the Renderer level involves calling the following functions in sequence:

Once we enter the RHI level, it will ultimately map to specific rendering API calls, namely:

From this level onwards, the mapped functions differ depending on the chosen API. If you, like me, are using DirectX12 RHI, then the next step would be to map to the creation of an FD3D12Buffer.

ℹ️
I will not further delve into the details related to the FD3D12 layer, as it would make this part very long and also exceed my scope of expertise.

Loaded Data on GPU

The Vertex Position data on the GPU appears to be like this:

image

The index buffer on the GPU appears as follows:

image

The final appearance of our Cube looks like this:

image

A Detail Look of the Vertex Buffer

image

Readers may be interested in the specific properties of this Buffer.

  1. Stride: 12 bytes. Each element is a vertex coordinate, consisting of 3 float coordinates, which is 3 x 4 = 12 bytes.
  2. Element Count: Note that our Cube consists of 6 faces, with each face containing 9 vertices. Therefore, the total number of vertices is 9 x 6 = 54.
  3. Why aren't the edge vertices shared?
    There are actually two vertics, not one
    There are actually two vertics, not one
    The edge is not smoothed because the normals are not the same.
    The edge is not smoothed because the normals are not the same.

    This is because the normal direction of two vertices at the same position on the same edge is different. Therefore, they are split into 2 vertices. If you are familiar with the "smoothing groups" feature in 3D Max or other DCC software, these edges are considered "hard edges" here.