顶点结构体gs_vb_data和gs_vertex_buffer
2024-06-18
40
0
以下内容来自于d3d11-subsystem和d3d11-subsystem的分析
gs_vb_data结构体用于存储顶点数据数组,数组大小由num成员决定。另外这个顶点数据的内容不是单一类型顶点,而是可以包含很多,因指针是否为空而确定是否存在。
基础结构体定义如下:
struct vec3 {
union {
struct {
float x, y, z, w;
};
float ptr[4];
__m128 m;
};
};
struct gs_tvertarray {
size_t width;
void *array;
};
gs_vb_data结构体定义如下:
struct gs_vb_data {
size_t num;
struct vec3 *points;//输入顶点的位置
struct vec3 *normals;//输入顶点的法线向量
struct vec3 *tangents;//输入顶点的正切向量
uint32_t *colors;//颜色
size_t num_tex; //这里的纹理应该是要支持纹理数组,不过一般只指定为1.即不支持动态纹理。
struct gs_tvertarray *tvarray;
};
- num:指的是points、normals、tangents、colors的数组的大小。如果都存在,那么他们是数组大小相同的数组。
- num_tex:指的是tvarray数组的大小。tvarray用于纹理。
gs_vb_data相关的函数:
static inline struct gs_vb_data *gs_vbdata_create(void)
{
return (struct gs_vb_data *)bzalloc(sizeof(struct gs_vb_data));
}
static inline void gs_vbdata_destroy(struct gs_vb_data *data)
{
uint32_t i;
if (!data)
return;
bfree(data->points);
bfree(data->normals);
bfree(data->tangents);
bfree(data->colors);
for (i = 0; i < data->num_tex; i++)
bfree(data->tvarray[i].array);
bfree(data->tvarray);
bfree(data);
}
最终数据的封装为gs_vertex_buffer结构体,从名可以看出是顶点缓冲区的封装,这里包括了顶点数据的所有信息(位置、颜色、切线,纹理等)。
这里只是浅显地介绍一下gs_vertex_buffer结构体的初始化。
struct gs_vertex_buffer : gs_obj {
ComPtr<ID3D11Buffer> vertexBuffer; //顶点缓冲区
ComPtr<ID3D11Buffer> normalBuffer;//法线缓冲区
ComPtr<ID3D11Buffer> colorBuffer;//颜色缓冲区
ComPtr<ID3D11Buffer> tangentBuffer; //正切缓冲区
vector<ComPtr<ID3D11Buffer>> uvBuffers;//纹理缓冲区数组
bool dynamic;//是否动态创建,创建纹理时用的CPUAccessFlags是否有D3D11_CPU_ACCESS_WRITE标识。
VBDataPtr vbd; //
size_t numVerts;//等同于vbd中的num,即数组大小。
vector<size_t> uvSizes;//纹理大小
//构造函数
gs_vertex_buffer(gs_device_t *device, struct gs_vb_data *data, uint32_t flags);
};
构造时device是显卡的通用导出结构体,data就是gs_vb_data,flags与动态创建相关。
gs_vertex_buffer::gs_vertex_buffer(gs_device_t *device, struct gs_vb_data *data, uint32_t flags)
: gs_obj(device, gs_type::gs_vertex_buffer),
dynamic((flags & GS_DYNAMIC) != 0),
vbd(data),
numVerts(data->num)
{
if (!data->num)
throw "Cannot initialize vertex buffer with 0 vertices";
if (!data->points)
throw "No points specified for vertex buffer";
BuildBuffers();
}
所以关键点就是BuildBuffers函数。
void gs_vertex_buffer::BuildBuffers()
{
//顶点位置必须存在
InitBuffer(sizeof(vec3), vbd.data->num, vbd.data->points, &vertexBuffer);
//法线
if (vbd.data->normals)
InitBuffer(sizeof(vec3), vbd.data->num, vbd.data->normals,&normalBuffer);
//正切向量
if (vbd.data->tangents)
InitBuffer(sizeof(vec3), vbd.data->num, vbd.data->tangents, &tangentBuffer);
//颜色
if (vbd.data->colors)
InitBuffer(sizeof(uint32_t), vbd.data->num, vbd.data->colors, &colorBuffer);
//纹理
for (size_t i = 0; i < vbd.data->num_tex; i++) {
struct gs_tvertarray *tverts = vbd.data->tvarray + i;
if (tverts->width != 2 && tverts->width != 4)
throw "Invalid texture vertex size specified";
if (!tverts->array)
throw "No texture vertices specified";
ComPtr<ID3D11Buffer> buffer;
InitBuffer(tverts->width * sizeof(float), vbd.data->num, tverts->array, &buffer);
uvBuffers.push_back(buffer);
uvSizes.push_back(tverts->width * sizeof(float));//纹理尺寸 像素宽度xsizeof(float)
}
}
而对于InitBuffer,则是创建缓冲区。从这来看,无论是什么样的缓冲区,都是通过D3D11Device::CreateBuffer实现的。该函数也描述符
Creates a buffer (vertex buffer, index buffer, or shader-constant buffer).即见https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_buffer_desc
void gs_vertex_buffer::InitBuffer(const size_t elementSize,
const size_t numVerts, void *array,
ID3D11Buffer **buffer)
{
D3D11_BUFFER_DESC bd;
D3D11_SUBRESOURCE_DATA srd;
HRESULT hr;
memset(&bd, 0, sizeof(bd));
memset(&srd, 0, sizeof(srd));
bd.Usage = dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
bd.CPUAccessFlags = dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; //绑的是顶点,而不是索引
bd.ByteWidth = UINT(elementSize * numVerts);//缓冲区大小
srd.pSysMem = array;
hr = device->device->CreateBuffer(&bd, &srd, buffer);
if (FAILED(hr))
throw HRError("Failed to create buffer", hr);
}
缓冲区内容更新:
void gs_vertex_buffer::FlushBuffer(ID3D11Buffer *buffer, void *array, size_t elementSize)
{
D3D11_MAPPED_SUBRESOURCE msr;
HRESULT hr;
if (FAILED(hr = device->context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &msr)))
throw HRError("Failed to map buffer", hr);
memcpy(msr.pData, array, elementSize * vbd.data->num);
device->context->Unmap(buffer, 0);
}
另外,这里有两个与根据着色器配置生成缓冲区数组的函数,其中PushBuffer函数是为MakeBufferList服务的。
static inline void PushBuffer(UINT *refNumBuffers, //数组最大标识
ID3D11Buffer **buffers,uint32_t *strides,//数组返回
ID3D11Buffer *buffer,size_t elementSize, const char *name)//元素
{
const UINT numBuffers = *refNumBuffers;
if (buffer) {
buffers[numBuffers] = buffer;
strides[numBuffers] = (uint32_t)elementSize;
*refNumBuffers = numBuffers + 1;
} else {
blog(LOG_ERROR, "This vertex shader requires a %s buffer",
name);
}
}
UINT gs_vertex_buffer::MakeBufferList(gs_vertex_shader *shader,
ID3D11Buffer **buffers, uint32_t *strides)
{
UINT numBuffers = 0;
PushBuffer(&numBuffers, buffers, strides, vertexBuffer, sizeof(vec3),"point");
if (shader->hasNormals)
PushBuffer(&numBuffers, buffers, strides, normalBuffer, sizeof(vec3), "normal");
if (shader->hasColors)
PushBuffer(&numBuffers, buffers, strides, colorBuffer, sizeof(uint32_t), "color");
if (shader->hasTangents)
PushBuffer(&numBuffers, buffers, strides, tangentBuffer, sizeof(vec3), "tangent");
if (shader->nTexUnits <= uvBuffers.size())
{
for (size_t i = 0; i < shader->nTexUnits; i++) {
buffers[numBuffers] = uvBuffers[i];
strides[numBuffers] = (uint32_t)uvSizes[i];
++numBuffers;
}
} else {
blog(LOG_ERROR,
"This vertex shader requires at least %u "
"texture buffers.",
(uint32_t)shader->nTexUnits);
}
return numBuffers;
}