OBS-Direct11数据结构
+ -

顶点结构体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;
}

0 篇笔记 写笔记

2.2.1缓冲区资源-顶点缓冲区
2.1资源解析既然我们已经了解了资源和资源视图的一些基本原理,现在是时候开始对各种类型的资源进行更详细的检查了,看看是什么使它们彼此不同,以及它们可以做什么。本节将介绍每种类型的资源,并讨论它们可以用来创建的每种可用子类型。我们从缓冲区资源开始,探索可以使用的每种缓冲区类型及其各种属性。接下来是关于......
2.2.1缓冲区资源-索引缓冲区
我们将看到的第二种缓冲区类型是索引缓冲区。索引缓冲区提供了通过引用存储在顶点缓冲区中的顶点数据来定义基元的非常有用的能力。或多或少,索引缓冲区提供指向顶点列表的索引列表。根据所需的基元类型(如点、线和三角形),将形成适当大小的索引组,以定义该基元由哪些顶点组成。图2.10直观地描述了该操作。索引......
2.2.1缓冲区资源-常量缓冲区
常量缓冲区是我们遇到的第一种资源类型,可从可编程着色器阶段访问,随后在HLSL代码中使用。常量缓冲器用于向在流水线中执行的可编程着色器程序提供常数信息。术语常量是指在执行draw或dispatch调用的整个过程中,该缓冲区内的数据保持不变。仅在管道调用之间更改的任何信息(如世界变换矩阵或对象颜色......
2.2.1缓冲区资源-缓冲区/结构化缓冲区资源
这是我们遇到的第一种缓冲区类型,可从可编程着色器阶段的HLSL代码中直接访问。在HLSL中,常量缓冲区是用cbuffer关键字声明的资源对象。清单2.9中提供了一个示例声明。cbuffer Transforms{ matrix WorldMatrixj matrix ViewPr......
2.2.1缓冲区资源-Append/Consume Buffers
Append和comsume buffer都是SBV的变体。本质上,他们都是需要UAV绑定的资源,但他们在HLSL中实现了一种类似堆栈的访问行为:使用append()函数把元素push到buffer中,或者用consume()函数拉出元素。对于这两种buffer来说,添加元素的顺序并不重要,但所添加......
2.2.1缓冲区资源-字节地址缓冲区
字节地址缓冲区旨在允许开发人员在缓冲区资源中实现自定义数据结构。根据定义,内存块的使用可以由与之结合的算法进行解释。例如,如果链表数据结构将用于存储32位颜色值的列表,则每个链接节点将由一个颜色值和一个到下一个元素的链接组成。当添加第一个元素时,颜色值在偏移量0处写入内存位置,并且到下一个元素的链接......
2.2.1缓冲区资源-间接参数缓冲区Indirect Argument Buffers
最后一个缓冲区配置是间接参数缓冲区。正如我们将在本书的许多案例中看到的那样,Direct3D11已经采取了许多步骤,使GPU更加有用,并且能够独立操作。间接参数缓冲区就是其中一个步骤。它用于从资源中为渲染和计算管道调用方法提供参数,而不是由主机程序直接传递这些参数。允许这种类型的流水线控制背后的概念......
顶点结构体gs_vb_data和gs_vertex_buffer
以下内容来自于d3d11-subsystem和d3d11-subsystem的分析gs_vb_data结构体用于存储顶点数据数组,数组大小由num成员决定。另外这个顶点数据的内容不是单一类型顶点,而是可以包含很多,因指针是否为空而确定是否存在。基础结构体定义如下:struct vec3 {......
索引结构体gs_index_buffer
对应于顶点结构体,也有一个索引结构体gs_index_buffer.struct DataPtr { void *data; inline DataPtr(void *data) : data(data) {} inline ~DataPtr() { bfree(data......
6.4.1默认常量缓冲区
任何在没有static常量修饰符的情况下声明的全局变量都将被编译器视为名为$Globals的默认常量缓冲区内的常量。类似地,标记为统一的着色器入口点函数的参数将被放置在另一个名为$Params的默认常量缓冲区中.......
6.4.2 HLSL纹理缓冲区
由于常量缓冲区针对小尺寸和统一的访问模式进行了优化,因此在某些情况下,它们可能具有未知期望的性能特性。一种常见的情况是用于蒙皮的骨骼矩阵阵列。在这种情况下,每个顶点都包含一个或多个索引,指示阵列中的哪个骨骼应用于变换位置和法线。在许多硬件类型上,这种访问模式将导致执行着色器程序的线程序列化对骨骼数据......
常量缓冲区
1.创建常量缓冲区 // 设置常量缓存区 D3D11_BUFFER_DESC cbd; ZeroMemory(&cbd, sizeof(cbd)); cbd.Usage = D3D11_USAGE_DEFAULT; cbd.ByteWidth = si......
作者信息
站长漫谈
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!