Skip to content

Commit

Permalink
Hydrogent: implemented vertex pooling
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMostDiligent committed Nov 18, 2023
1 parent d500a38 commit ffcc9d4
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 80 deletions.
30 changes: 11 additions & 19 deletions Hydrogent/interface/HnMesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <memory>
#include <unordered_map>
#include <map>

#include "pxr/imaging/hd/types.h"
#include "pxr/imaging/hd/mesh.h"
Expand Down Expand Up @@ -106,18 +107,6 @@ class HnMesh final : public pxr::HdMesh
Uint32 GetNumEdges() const { return m_NumEdges; }
Uint32 GetNumPoints() const { return m_Topology.GetNumPoints(); }

/// Returns the start vertex of the face data in the vertex buffers.
///
/// \remarks This value should be used as the base vertex for
/// draw commands.
Uint32 GetFaceStartVertex() const { return m_FaceStartVertex; }

/// Returns the start vertex of the points data.
///
/// \remarks This value should be used as the base vertex for
/// draw commands.
Uint32 GetPointsStartVertex() const { return m_PointsStartVertex; }

/// Returns the start index of the face data in the index buffer.
///
/// \remarks This value should be used as the start index location
Expand Down Expand Up @@ -149,6 +138,8 @@ class HnMesh final : public pxr::HdMesh

void UpdateVertexBuffers(HnRenderDelegate& RenderDelegate);
void UpdateIndexBuffer(HnRenderDelegate& RenderDelegate);
void AllocatePooledResources(pxr::HdSceneDelegate& SceneDelegate,
pxr::HdRenderParam* RenderParam);

void UpdateReprMaterialTags(pxr::HdSceneDelegate* SceneDelegate,
pxr::HdRenderParam* RenderParam);
Expand Down Expand Up @@ -210,18 +201,19 @@ class HnMesh final : public pxr::HdMesh

struct VertexData
{
using BufferSourceMapType = std::unordered_map<pxr::TfToken, std::shared_ptr<pxr::HdBufferSource>, pxr::TfToken::HashFunctor>;
// Use map to keep buffer sources sorted by name
using BufferSourceMapType = std::map<pxr::TfToken, std::shared_ptr<pxr::HdBufferSource>>;
BufferSourceMapType VertexSources;
BufferSourceMapType FaceSources;

std::unordered_map<pxr::TfToken, Uint32, pxr::TfToken::HashFunctor> NameToPoolIndex;
};
std::unique_ptr<VertexData> m_VertexData;

Uint32 m_NumFaceTriangles = 0;
Uint32 m_NumEdges = 0;
Uint32 m_FaceStartVertex = 0;
Uint32 m_PointsStartVertex = 0;
Uint32 m_FaceStartIndex = 0;
Uint32 m_EdgeStartIndex = 0;
Uint32 m_NumFaceTriangles = 0;
Uint32 m_NumEdges = 0;
Uint32 m_FaceStartIndex = 0;
Uint32 m_EdgeStartIndex = 0;

float4x4 m_Transform = float4x4::Identity();
float4 m_DisplayColor = {1, 1, 1, 1};
Expand Down
206 changes: 170 additions & 36 deletions Hydrogent/src/HnMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ void HnMesh::UpdateRepr(pxr::HdSceneDelegate& SceneDelegate,
}
}
UpdateConstantPrimvars(SceneDelegate, RenderParam, DirtyBits, ReprToken);

AllocatePooledResources(SceneDelegate, RenderParam);
DirtyBits &= ~pxr::HdChangeTracker::DirtyPrimvar;
}

Expand Down Expand Up @@ -315,17 +317,6 @@ void HnMesh::UpdateTopology(pxr::HdSceneDelegate& SceneDelegate,
m_NumFaceTriangles = static_cast<Uint32>(m_IndexData->TrianglesFaceIndices.size());
m_NumEdges = static_cast<Uint32>(m_IndexData->MeshEdgeIndices.size());

if (static_cast<const HnRenderParam*>(RenderParam)->GetUseIndexPool())
{
HnRenderDelegate* RenderDelegate = static_cast<HnRenderDelegate*>(SceneDelegate.GetRenderIndex().GetRenderDelegate());
GLTF::ResourceManager& ResMgr = RenderDelegate->GetResourceManager();

m_FaceIndexAllocation = ResMgr.AllocateIndices(sizeof(Uint32) * m_NumFaceTriangles * 3);
m_EdgeIndexAllocation = ResMgr.AllocateIndices(sizeof(Uint32) * m_NumEdges * 2);
m_FaceStartIndex = m_FaceIndexAllocation->GetOffset() / sizeof(Uint32);
m_EdgeStartIndex = m_EdgeIndexAllocation->GetOffset() / sizeof(Uint32);
}

DirtyBits &= ~pxr::HdChangeTracker::DirtyTopology;
}

Expand Down Expand Up @@ -646,6 +637,121 @@ void HnMesh::ConvertVertexPrimvarSources()
}
}

void HnMesh::AllocatePooledResources(pxr::HdSceneDelegate& SceneDelegate,
pxr::HdRenderParam* RenderParam)
{
HnRenderDelegate* RenderDelegate = static_cast<HnRenderDelegate*>(SceneDelegate.GetRenderIndex().GetRenderDelegate());
GLTF::ResourceManager& ResMgr = RenderDelegate->GetResourceManager();

if (m_IndexData && static_cast<const HnRenderParam*>(RenderParam)->GetUseIndexPool())
{
if (!m_IndexData->TrianglesFaceIndices.empty())
{
m_FaceIndexAllocation = ResMgr.AllocateIndices(sizeof(Uint32) * m_NumFaceTriangles * 3);
m_FaceStartIndex = m_FaceIndexAllocation->GetOffset() / sizeof(Uint32);
}

if (!m_IndexData->MeshEdgeIndices.empty())
{
m_EdgeIndexAllocation = ResMgr.AllocateIndices(sizeof(Uint32) * m_NumEdges * 2);
m_EdgeStartIndex = m_EdgeIndexAllocation->GetOffset() / sizeof(Uint32);
}
}

if (m_VertexData && static_cast<const HnRenderParam*>(RenderParam)->GetUseVertexPool())
{
// Allocate vertex buffers for face data
Uint32 FaceStartVertex = 0;
{
// If face sources are not empty, use them, otherwise use vertex sources
const auto& Sources = !m_VertexData->FaceSources.empty() ?
m_VertexData->FaceSources :
m_VertexData->VertexSources;

GLTF::ResourceManager::VertexLayoutKey VtxKey;
VtxKey.Elements.reserve(Sources.size());
for (auto& source_it : Sources)
{
const auto ElementType = source_it.second->GetTupleType().type;
const auto ElementSize = HdDataSizeOfType(ElementType);

m_VertexData->NameToPoolIndex[source_it.first] = static_cast<Uint32>(VtxKey.Elements.size());
VtxKey.Elements.emplace_back(static_cast<Uint32>(ElementSize), BIND_VERTEX_BUFFER);
}

const Uint32 NumVerts = !m_VertexData->FaceSources.empty() ?
GetNumFaceTriangles() * 3 :
GetNumPoints();

m_FaceVertsAllocation = ResMgr.AllocateVertices(VtxKey, NumVerts);
VERIFY_EXPR(m_FaceVertsAllocation);
FaceStartVertex = m_FaceVertsAllocation->GetStartVertex();
}

Uint32 PointsStartVertex = 0;
if (!m_VertexData->FaceSources.empty())
{
// Allocate separate vertex buffers for vertex sources
auto points_it = m_VertexData->VertexSources.find(pxr::HdTokens->points);
if (points_it != m_VertexData->VertexSources.end())
{
const auto ElementType = points_it->second->GetTupleType().type;
const auto ElementSize = HdDataSizeOfType(ElementType);

GLTF::ResourceManager::VertexLayoutKey VtxKey;
VtxKey.Elements.emplace_back(static_cast<Uint32>(ElementSize), BIND_VERTEX_BUFFER);
m_PointsAllocation = ResMgr.AllocateVertices(VtxKey, GetNumPoints());
VERIFY_EXPR(m_PointsAllocation);
PointsStartVertex = m_PointsAllocation->GetStartVertex();
}
else
{
UNEXPECTED("Vertex data does not contain points");
}
}
else
{
// All sources are vertex sources
m_PointsAllocation = m_FaceVertsAllocation;
PointsStartVertex = FaceStartVertex;
}

// WebGL/GLES do not support base vertex, so we need to adjust indices.
if (FaceStartVertex != 0)
{
if (!m_IndexData->TrianglesFaceIndices.empty())
{
for (pxr::GfVec3i& Tri : m_IndexData->TrianglesFaceIndices)
{
Tri[0] += FaceStartVertex;
Tri[1] += FaceStartVertex;
Tri[2] += FaceStartVertex;
}
}
else
{
m_IndexData->TrianglesFaceIndices.resize(GetNumFaceTriangles());
for (Uint32 i = 0; i < m_IndexData->TrianglesFaceIndices.size(); ++i)
{
pxr::GfVec3i& Tri{m_IndexData->TrianglesFaceIndices[i]};
Tri[0] = FaceStartVertex + i * 3 + 0;
Tri[1] = FaceStartVertex + i * 3 + 1;
Tri[2] = FaceStartVertex + i * 3 + 2;
}
}
}

if (PointsStartVertex != 0 && !m_IndexData->MeshEdgeIndices.empty())
{
for (pxr::GfVec2i& Edge : m_IndexData->MeshEdgeIndices)
{
Edge[0] += PointsStartVertex;
Edge[1] += PointsStartVertex;
}
}
}
}

void HnMesh::UpdateVertexBuffers(HnRenderDelegate& RenderDelegate)
{
const RenderDeviceX_N& Device{RenderDelegate.GetDevice()};
Expand All @@ -656,7 +762,11 @@ void HnMesh::UpdateVertexBuffers(HnRenderDelegate& RenderDelegate)
return;
}

auto CreateBuffer = [&](const pxr::HdBufferSource* pSource, const pxr::TfToken& PrimName) {
auto PrepareVertexBuffer = [&](const pxr::TfToken& Name,
const pxr::HdBufferSource* pSource,
const pxr::TfToken& PrimName,
IVertexPoolAllocation* pAllocation,
Uint32 PoolIndex = ~0u) {
if (pSource == nullptr)
return RefCntAutoPtr<IBuffer>{};

Expand All @@ -668,41 +778,65 @@ void HnMesh::UpdateVertexBuffers(HnRenderDelegate& RenderDelegate)
else if (PrimName == pxr::HdTokens->normals)
VERIFY(ElementType == pxr::HdTypeFloatVec3, "Unexpected normal size");

const auto Name = GetId().GetString() + " - " + PrimName.GetString();
BufferDesc Desc{
Name.c_str(),
NumElements * ElementSize,
BIND_VERTEX_BUFFER,
USAGE_IMMUTABLE,
};
RefCntAutoPtr<IBuffer> pBuffer;
if (pAllocation == nullptr)
{
const auto BufferName = GetId().GetString() + " - " + PrimName.GetString();
BufferDesc Desc{
BufferName.c_str(),
NumElements * ElementSize,
BIND_VERTEX_BUFFER,
USAGE_IMMUTABLE,
};

BufferData InitData{pSource->GetData(), Desc.Size};
pBuffer = Device.CreateBuffer(Desc, &InitData);
}
else
{
if (PoolIndex == ~0u)
{
auto idx_it = m_VertexData->NameToPoolIndex.find(Name);
if (idx_it != m_VertexData->NameToPoolIndex.end())
PoolIndex = idx_it->second;
else
UNEXPECTED("Failed to find vertex buffer index for ", Name);
}

if (PoolIndex != ~0u)
{
pBuffer = pAllocation->GetBuffer(PoolIndex);

BufferData InitData{pSource->GetData(), Desc.Size};
return Device.CreateBuffer(Desc, &InitData);
IDeviceContext* pCtx = RenderDelegate.GetDeviceContext();
VERIFY_EXPR(pAllocation->GetVertexCount() == NumElements);
pCtx->UpdateBuffer(pBuffer, pAllocation->GetStartVertex() * ElementSize, NumElements * ElementSize, pSource->GetData(), RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
}
}
return pBuffer;
};

auto points_it = m_VertexData->VertexSources.find(pxr::HdTokens->points);
if (points_it == m_VertexData->VertexSources.end())
const auto& Sources = !m_VertexData->FaceSources.empty() ?
m_VertexData->FaceSources :
m_VertexData->VertexSources;
for (auto source_it : Sources)
{
LOG_ERROR_MESSAGE("Vertex data does not contain points");
return;
m_FaceVertexBuffers[source_it.first] = PrepareVertexBuffer(source_it.first, source_it.second.get(), source_it.first, m_FaceVertsAllocation);
}

m_pPointsVertexBuffer = CreateBuffer(points_it->second.get(), pxr::HdTokens->points);

if (!m_VertexData->FaceSources.empty())
if (m_VertexData->FaceSources.empty())
{
for (auto source_it : m_VertexData->FaceSources)
{
m_FaceVertexBuffers[source_it.first] = CreateBuffer(source_it.second.get(), source_it.first);
}
m_pPointsVertexBuffer = GetFaceVertexBuffer(pxr::HdTokens->points);
}
else
{
for (auto source_it : m_VertexData->VertexSources)
auto points_it = m_VertexData->VertexSources.find(pxr::HdTokens->points);
if (points_it != m_VertexData->VertexSources.end())
{
m_pPointsVertexBuffer = PrepareVertexBuffer(pxr::HdTokens->points, points_it->second.get(), pxr::HdTokens->points, m_PointsAllocation, 0);
}
else
{
m_FaceVertexBuffers[source_it.first] = source_it.first == pxr::HdTokens->points ?
m_pPointsVertexBuffer :
CreateBuffer(source_it.second.get(), source_it.first);
LOG_ERROR_MESSAGE("Vertex data does not contain points");
}
}

Expand Down
10 changes: 4 additions & 6 deletions Hydrogent/src/HnRenderDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,11 @@ static std::shared_ptr<USD_Renderer> CreateUSDRenderer(IRenderDevice* pDevic

static RefCntAutoPtr<GLTF::ResourceManager> CreateResourceManager(IRenderDevice* pDevice)
{
#ifdef DILIGENT_DEBUG
// Initial vertex and index counts are not important as the
// real number of vertices and indices will be determined after
// all meshes are synced for the first time.
static constexpr Uint32 InitialVertexCount = 1024;
static constexpr Uint32 InitialIndexCount = 4096;
#else
static constexpr Uint32 InitialVertexCount = 64 << 10;
static constexpr Uint32 InitialIndexCount = 256 << 10;
#endif
static constexpr Uint32 InitialIndexCount = 1024;

GLTF::ResourceManager::CreateInfo ResMgrCI;

Expand Down
Loading

0 comments on commit ffcc9d4

Please sign in to comment.