Как я могу быстро обновить буфер вершин Dynami c? - PullRequest
2 голосов
/ 25 марта 2020

Я пытаюсь создать простой инструмент для 3D-моделирования.

есть некоторая работа по перемещению вершины (или вершин) для преобразования модели.

Я использовал Dynami c буфер вершин, потому что думал, что он нуждается в большом обновлении.

, но производительность в модели с высоким полигоном слишком низкая, хотя я меняю только одну вершину.

есть ли другие методы? или я ошибся?

вот мой D3D11_BUFFER_DES C

Usage = D3D11_USAGE_DYNAMIC;
CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
BindFlags = D3D11_BIND_VERTEX_BUFFER;
ByteWidth = sizeof(ST_Vertex) * _nVertexCount
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = pVerticesInfo;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pVertexBuffer);

и моя функция обновления

D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3dMappedResource);

ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;

for (int i = 0; i < vIndice.size(); ++i)
{
    pBuffer[vIndice[i]].xfPosition.x = pVerticesInfo[vIndice[i]].xfPosition.x;
    pBuffer[vIndice[i]].xfPosition.y = pVerticesInfo[vIndice[i]].xfPosition.y;
    pBuffer[vIndice[i]].xfPosition.z = pVerticesInfo[vIndice[i]].xfPosition.z;
}
pImmediateContext->Unmap(_pVertexBuffer, 0);

Ответы [ 2 ]

1 голос
/ 17 апреля 2020

Как упоминалось в предыдущем ответе, вы обновляете весь буфер каждый раз, что будет медленно в зависимости от размера модели.

Решение действительно заключается в том, чтобы реализовать частичные обновления, для этого есть две возможности: Вы хотите обновить одну вершину, или вы хотите обновить произвольные индексы (например, вы хотите переместить N вершин в одну go, в разные места, например, в вершину 1,20,23.

Первое решение довольно простое, сначала создайте свой буфер со следующим описанием:

Usage = D3D11_USAGE_DEFAULT;
CPUAccessFlags = 0;
BindFlags = D3D11_BIND_VERTEX_BUFFER;
ByteWidth = sizeof(ST_Vertex) * _nVertexCount
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = pVerticesInfo;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pVertexBuffer);

Это гарантирует, что ваш буфер вершин доступен только для gpu.

Далее создайте вторую динамику c буфер, имеющий размер одной вершины (в этом случае вам не нужны никакие флаги связывания, поскольку он будет использоваться только для копий)

_pCopyVertexBuffer

Usage = D3D11_USAGE_DYNAMIC; //Staging works as well
CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
BindFlags = 0;
ByteWidth = sizeof(ST_Vertex);
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = NULL;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pCopyVertexBuffer);

when you move a vertex, copy the changed vertex in the copy buffer :

ST_Vertex changedVertex;

D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3dMappedResource);

ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;

pBuffer->xfPosition.x = changedVertex.xfPosition.x;
pBuffer->.xfPosition.y = changedVertex.xfPosition.y;
pBuffer->.xfPosition.z = changedVertex.xfPosition.z;

pImmediateContext->Unmap(_pVertexBuffer, 0);

Поскольку вы используете D3D11_MAP_WRITE_DISCARD, обязательно запишите все Атрибуты там (не только положение).

Теперь, когда вы сделали, вы можете использовать ID3D11DeviceContext :: CopySubresourceRegion для только скопируйте измененную вершину в текущем местоположении:

Я предполагаю, что vertexID является индексом измененной вершины:

pd3DeviceContext->CopySubresourceRegion(_pVertexBuffer, 
0, //must be 0
vertexID * sizeof(ST_Vertex), //location of the vertex in you gpu vertex buffer
0, //must be 0
0, //must be 0
_pCopyVertexBuffer, 
0, //must be 0
NULL //in this case we copy the full content of _pCopyVertexBuffer, so we can set to null
);

Теперь, если вы хотите обновить список вершин, все получится более сложный, и у вас есть несколько вариантов:

- Сначала вы примените эту технику с одной вершиной в al oop, это будет работать довольно хорошо, если ваша ревизия мала.

-Если ваша ревизия установлена очень большой (близкий к почти полному размеру вершины, вы, вероятно, вместо этого можете переписать весь буфер).

-Продолжительный метод заключается в использовании вычислительного шейдера для выполнения обновлений (это тот, который я обычно использую как наиболее гибкая версия). Публикация всего кода привязки c ++ была бы слишком длинной, но вот концепция:

  • ваш буфер вершин должен иметь BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_UNORDERED_ACCESS; // это позволяет писать с вычислением
  • , вам необходимо создать ID3D11UnorderedAccessView для этого буфера (чтобы шейдер мог записать в него)
  • вам нужны следующие флаги mis c: D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS // это позволяет записать как RWByteAddressBuffer
  • , после чего вы создаете два динамических c структурированных буфера (я предпочитаю их над byteaddress, но в dx11 буфер вершин и структурированный не разрешены, поэтому для записи вам нужен raw)
  • первый структурированный буфер имеет шаг ST_Vertex (это ваш набор изменений)
  • второй структурированный буфер имеет шаг 4 (uint, это индексы)
  • оба структурированы буферы получают произвольное количество элементов (обычно я использую 1024 или 2048), так что это будет максимальное количество вершин, которое вы можете обновить за один проход.
  • для обоих структурированных буферов необходим ID3D11ShaderResourceView (шейдер виден, читается только)

Затем процесс обновления выглядит следующим образом:

* 104 9 * запись измененных вершин и местоположений в структурированных буферах (используя сброс карты, если вам нужно меньше копировать, все нормально) присоединить оба структурированных буфера для чтения присоединить ID3D11UnorderedAccessView для записи установка вычислительного шейдера отправка вызова отсоединение ID3D11UnorderedAccessView для записи (это ОЧЕНЬ важно)

Это пример кода вычислительного шейдера ( Я полагаю, что для простоты вершина является позицией

1065 *
0 голосов
/ 11 апреля 2020

Для целей этого вопроса я собираюсь предположить, что у вас уже есть механизм выбора вершины из списка вершин, основанный на приведении лучей или некотором другом методе выбора, и механизм для создания вектора смещения, детализирующего, как вершина был перемещен в пространство модели.

Используемый вами метод обновления буфера достаточен для всего, что не превышает нескольких сотен вершин, но в крупномасштабных моделях он становится чрезвычайно медленным. Это потому, что вы обновляете все , а не отдельные вершины, которые вы изменили.

Чтобы исправить это, вы должны обновить только те вершины, которые вы изменили, и для этого вам нужно создать набор изменений .

По сути, набор изменений - это не что иное, как набор изменений, внесенных в данные - список вершин, которые необходимо обновить. Так как мы уже знаем, какие вершины были изменены (иначе мы не могли бы ими манипулировать), мы можем отобразить в буфере GPU go именно на эту вершину и скопировать только эти вершины в буфер GPU.

В вашем методе модификации вершины запишите индекс вершины, которая была изменена пользователем:

//Modify the vertex coordinates based on mouse displacement
pVerticesInfo[SelectedVertexIndex].xfPosition.x += DisplacementVector.x;
pVerticesInfo[SelectedVertexIndex].xfPosition.y += DisplacementVector.y;
pVerticesInfo[SelectedVertexIndex].xfPosition.z += DisplacementVector.z;
//Add the changed vertex to the list of changes.
changedVertices.add(SelectedVertexIndex);
//And update the GPU buffer
UpdateD3DBuffer();

В UpdateD3DBuffer() выполните следующее:

D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE, 0, &d3dMappedResource);

ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;

for (int i = 0; i < changedVertices.size(); ++i)
{
    pBuffer[changedVertices[i]].xfPosition.x = pVerticesInfo[changedVertices[i]].xfPosition.x;
    pBuffer[changedVertices[i]].xfPosition.y = pVerticesInfo[changedVertices[i]].xfPosition.y;
    pBuffer[changedVertices[i]].xfPosition.z = pVerticesInfo[changedVertices[i]].xfPosition.z;
}
pImmediateContext->Unmap(_pVertexBuffer, 0);
changedVertices.clear();

В результате обновляются только измененные вершины, а не все вершины в модели.

Это также позволяет выполнять некоторые более сложные манипуляции. Вы можете выбрать несколько вершин и переместить их все как группу, выбрать целое лицо и переместить все связанные вершины или относительно легко перемещать целые области модели, предполагая, что ваш метод выбора способен справиться с этим.

Кроме того, если вы записываете наборы изменений с достаточным количеством информации (затронутые вершины и индекс смещения), вы можете довольно легко реализовать функцию отмены, просто поменяв вектор смещения и применив выбранный набор изменений.

...