More level mesh buffer fun

This commit is contained in:
Magnus Norddahl 2024-08-23 00:01:08 +02:00
commit d28a3571b5
5 changed files with 359 additions and 244 deletions

View file

@ -4,6 +4,13 @@
LevelMesh::LevelMesh()
{
LevelMeshLimits limits;
limits.MaxVertices = 12;
limits.MaxIndexes = 3 * 4;
limits.MaxSurfaces = 1;
limits.MaxUniforms = 1;
Reset(limits);
// Default portal
LevelMeshPortal portal;
Portals.Push(portal);
@ -11,38 +18,35 @@ LevelMesh::LevelMesh()
AddEmptyMesh();
UpdateCollision();
Mesh.MaxVertices = std::max(Mesh.Vertices.Size() * 2, (unsigned int)10000);
Mesh.MaxIndexes = std::max(Mesh.Indexes.Size() * 2, (unsigned int)10000);
Mesh.MaxSurfaces = std::max(Mesh.SurfaceIndexes.Size() * 2, (unsigned int)10000);
Mesh.MaxUniforms = std::max(Mesh.Uniforms.Size() * 2, (unsigned int)10000);
Mesh.MaxSurfaceIndexes = std::max(Mesh.SurfaceIndexes.Size() * 2, (unsigned int)10000);
Mesh.MaxNodes = (int)std::max(Collision->get_nodes().size() * 2, (size_t)10000);
Mesh.MaxLights = 100'000;
Mesh.MaxLightIndexes = 4 * 1024 * 1024;
}
void LevelMesh::AddEmptyMesh()
{
GeometryAllocInfo ginfo = AllocGeometry(12, 3 * 4);
// Default empty mesh (we can't make it completely empty since vulkan doesn't like that)
float minval = -100001.0f;
float maxval = -100000.0f;
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ maxval, minval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ minval, maxval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ maxval, minval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ minval, maxval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
ginfo.Vertices[0] = { minval, minval, minval };
ginfo.Vertices[1] = { maxval, minval, minval };
ginfo.Vertices[2] = { maxval, maxval, minval };
ginfo.Vertices[3] = { minval, minval, minval };
ginfo.Vertices[4] = { minval, maxval, minval };
ginfo.Vertices[5] = { maxval, maxval, minval };
ginfo.Vertices[6] = { minval, minval, maxval };
ginfo.Vertices[7] = { maxval, minval, maxval };
ginfo.Vertices[8] = { maxval, maxval, maxval };
ginfo.Vertices[9] = { minval, minval, maxval };
ginfo.Vertices[10] = { minval, maxval, maxval };
ginfo.Vertices[11] = { maxval, maxval, maxval };
for (int i = 0; i < 3 * 4; i++)
Mesh.Indexes.Push(i);
ginfo.Indexes[i] = i;
UploadAll();
Mesh.IndexCount = ginfo.IndexCount;
UploadPortals();
}
LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, float maxDist)
@ -109,7 +113,7 @@ LevelMeshTileStats LevelMesh::GatherTilePixelStats()
void LevelMesh::UpdateCollision()
{
Collision = std::make_unique<TriangleMeshShape>(Mesh.Vertices.Data(), Mesh.Vertices.Size(), Mesh.Indexes.Data(), Mesh.Indexes.Size());
Collision = std::make_unique<TriangleMeshShape>(Mesh.Vertices.Data(), Mesh.Vertices.Size(), Mesh.Indexes.Data(), Mesh.IndexCount);
UploadCollision();
}

View file

@ -11,6 +11,7 @@
#include "hw_levelmeshsurface.h"
#include "hw_materialstate.h"
#include "hw_surfaceuniforms.h"
#include "engineerrors.h"
#include <memory>
#include <dp_rect_pack.h>
@ -44,12 +45,32 @@ struct UniformsAllocInfo
int Count = 0;
};
struct SurfaceAllocInfo
{
LevelMeshSurface* Surface = nullptr;
int Index = 0;
};
struct LightAllocInfo
{
LevelMeshLight* Light = nullptr;
int Index = 0;
};
struct MeshBufferRange
{
int Offset = 0;
int Size = 0;
int Start = 0;
int End = 0;
bool operator<(const MeshBufferRange& other) const { return Offset < other.Offset; }
int Count() const { return End - Start; }
};
struct LevelMeshLimits
{
int MaxVertices = 0;
int MaxSurfaces = 0;
int MaxUniforms = 0;
int MaxIndexes = 0;
};
class LevelMesh
@ -71,20 +92,18 @@ public:
GeometryAllocInfo AllocGeometry(int vertexCount, int indexCount)
{
GeometryAllocInfo info;
info.VertexStart = Mesh.Vertices.Size();
info.VertexStart = RemoveRange(FreeLists.Vertex, vertexCount);
info.VertexCount = vertexCount;
info.IndexStart = Mesh.Indexes.Size();
info.IndexStart = RemoveRange(FreeLists.Index, indexCount);
info.IndexCount = indexCount;
Mesh.Vertices.Resize(info.VertexStart + vertexCount);
Mesh.UniformIndexes.Resize(info.VertexStart + vertexCount);
Mesh.Indexes.Resize(info.IndexStart + indexCount);
info.Vertices = &Mesh.Vertices[info.VertexStart];
info.UniformIndexes = &Mesh.UniformIndexes[info.VertexStart];
info.Indexes = &Mesh.Indexes[info.IndexStart];
AddRange(UploadRanges.Vertex, { info.VertexStart, info.VertexCount });
AddRange(UploadRanges.UniformIndexes, { info.VertexStart, info.VertexCount });
AddRange(UploadRanges.Index, { info.IndexStart, info.IndexCount });
AddRange(UploadRanges.Vertex, { info.VertexStart, info.VertexStart + info.VertexCount });
AddRange(UploadRanges.UniformIndexes, { info.VertexStart, info.VertexStart + info.VertexCount });
AddRange(UploadRanges.Index, { info.IndexStart, info.IndexStart + info.IndexCount });
AddRange(UploadRanges.SurfaceIndex, { info.IndexStart / 3, (info.IndexStart + info.IndexCount) / 3 + 1 });
return info;
}
@ -92,14 +111,23 @@ public:
UniformsAllocInfo AllocUniforms(int count)
{
UniformsAllocInfo info;
info.Start = Mesh.Uniforms.Size();
info.Start = RemoveRange(FreeLists.Uniforms, count);
info.Count = count;
Mesh.Uniforms.Resize(info.Start + count);
Mesh.Materials.Resize(info.Start + count);
info.Uniforms = &Mesh.Uniforms[info.Start];
info.Materials = &Mesh.Materials[info.Start];
AddRange(UploadRanges.Uniforms, { info.Start, info.Count });
AddRange(UploadRanges.Uniforms, { info.Start, info.Start + info.Count });
return info;
}
SurfaceAllocInfo AllocSurface()
{
SurfaceAllocInfo info;
info.Index = RemoveRange(FreeLists.Surface, 1);
info.Surface = &Mesh.Surfaces[info.Index];
AddRange(UploadRanges.Surface, { info.Index, info.Index + 1 });
return info;
}
@ -109,10 +137,10 @@ public:
// Convert triangles to degenerates
for (int i = 0; i < indexCount; i++)
Mesh.Indexes[indexStart + i] = 0;
AddRange(UploadRanges.Index, { indexStart, indexCount });
AddRange(UploadRanges.Index, { indexStart, indexStart + indexCount });
AddRange(FreeLists.Vertex, { vertexStart, vertexCount });
AddRange(FreeLists.Index, { indexStart, indexCount });
AddRange(FreeLists.Vertex, { vertexStart, vertexStart + vertexCount });
AddRange(FreeLists.Index, { indexStart, indexStart + indexCount });
}
void FreeUniforms(int start, int count)
@ -127,6 +155,28 @@ public:
AddRange(FreeLists.Surface, { (int)surfaceIndex, 1 });
}
// Sets the sizes of all the GPU buffers and empties the mesh
virtual void Reset(const LevelMeshLimits& limits)
{
Mesh.Vertices.Resize(limits.MaxVertices);
Mesh.UniformIndexes.Resize(limits.MaxVertices);
Mesh.Surfaces.Resize(limits.MaxSurfaces);
Mesh.Uniforms.Resize(limits.MaxUniforms);
Mesh.Materials.Resize(limits.MaxUniforms);
Mesh.Lights.Resize(16);
Mesh.LightIndexes.Resize(16);
Mesh.Indexes.Resize(limits.MaxIndexes);
Mesh.SurfaceIndexes.Resize(limits.MaxIndexes / 3 + 1);
FreeLists.Vertex.Clear(); FreeLists.Vertex.Push({ 0, limits.MaxVertices });
FreeLists.Index.Clear(); FreeLists.Index.Push({ 0, limits.MaxIndexes });
FreeLists.Uniforms.Clear(); FreeLists.Uniforms.Push({ 0, limits.MaxUniforms });
FreeLists.Surface.Clear(); FreeLists.Surface.Push({ 0, limits.MaxSurfaces });
}
// Data placed in GPU buffers
struct
{
@ -146,16 +196,10 @@ public:
// Index data
TArray<uint32_t> Indexes;
TArray<int> SurfaceIndexes;
int IndexCount = 0; // Index range filled with data
// Above data must not be resized beyond these limits as that's the size of the GPU buffers
int MaxVertices = 0;
int MaxIndexes = 0;
int MaxSurfaces = 0;
int MaxUniforms = 0;
int MaxSurfaceIndexes = 0;
// GPU buffer size for collision nodes
int MaxNodes = 0;
int MaxLights = 0;
int MaxLightIndexes = 0;
} Mesh;
// Ranges in mesh that have changed since last upload
@ -207,26 +251,10 @@ public:
void AddEmptyMesh();
void UploadAll()
void UploadPortals()
{
ClearRanges(UploadRanges.Vertex);
AddRange(UploadRanges.Vertex, { 0, (int)Mesh.Vertices.Size() });
ClearRanges(UploadRanges.Index);
AddRange(UploadRanges.Index, { 0, (int)Mesh.Indexes.Size() });
ClearRanges(UploadRanges.SurfaceIndex);
AddRange(UploadRanges.SurfaceIndex, { 0, (int)Mesh.SurfaceIndexes.Size() });
ClearRanges(UploadRanges.Surface);
AddRange(UploadRanges.Surface, { 0, (int)Mesh.Surfaces.Size() });
ClearRanges(UploadRanges.UniformIndexes);
AddRange(UploadRanges.UniformIndexes, { 0, (int)Mesh.UniformIndexes.Size() });
ClearRanges(UploadRanges.Uniforms);
AddRange(UploadRanges.Uniforms, { 0, (int)Mesh.Uniforms.Size() });
ClearRanges(UploadRanges.Portals);
UploadRanges.Portals.Clear();
AddRange(UploadRanges.Portals, { 0, (int)Portals.Size() });
ClearRanges(UploadRanges.Light);
AddRange(UploadRanges.Light, { 0, (int)Mesh.Lights.Size() });
ClearRanges(UploadRanges.LightIndex);
AddRange(UploadRanges.LightIndex, { 0, (int)Mesh.LightIndexes.Size() });
}
void UploadCollision()
@ -236,42 +264,81 @@ public:
UploadRanges.Node.Push({ 0, (int)Collision->get_nodes().size() });
}
void ClearRanges(TArray<MeshBufferRange>& ranges)
int RemoveRange(TArray<MeshBufferRange>& ranges, int count)
{
ranges.Clear();
for (unsigned int i = ranges.Size(); i > 0; i--)
{
auto& item = ranges[i - 1];
if (item.End - item.Start >= count)
{
int pos = item.Start;
item.Start += count;
if (item.Start == item.End)
{
ranges.Delete(i - 1);
}
return pos;
}
}
I_FatalError("Could not find space in level mesh buffer");
}
void AddRange(TArray<MeshBufferRange>& ranges, MeshBufferRange range)
{
if (range.Size <= 0)
// Empty range?
if (range.Start == range.End)
return;
auto right = std::lower_bound(ranges.begin(), ranges.end(), range);
// First element?
if (ranges.Size() == 0)
{
ranges.push_back(range);
return;
}
// Find start position in ranges
auto right = std::lower_bound(ranges.begin(), ranges.end(), range, [](const auto& a, const auto& b) { return a.Start < b.Start; });
bool leftExists = right != ranges.begin();
bool rightExists = right != ranges.end();
auto left = right;
if (leftExists)
--left;
if (leftExists && rightExists && left->Offset + left->Size == range.Offset && right->Offset == range.Offset + range.Size) // ####[--]####
{
left->Size += range.Size + right->Size;
ranges.Delete(right - ranges.begin());
}
else if (leftExists && left->Offset + left->Size == range.Offset) // ####[--] ####
{
left->Size += range.Size;
}
else if (rightExists && right->Offset == range.Offset + range.Size) // #### [--]####
{
right->Offset -= range.Size;
}
else // ##### [--] ####
// Is this a gap between two ranges?
if ((!leftExists || left->End < range.Start) && (!rightExists || right->Start > range.End))
{
ranges.Insert(right - ranges.begin(), range);
return;
}
// Are we extending the left or the right range?
if (leftExists && range.Start <= left->End)
{
left->End = std::max(left->End, range.End);
right = left;
}
else // if (rightExists && right->Start <= range.End)
{
right->Start = range.Start;
right->End = std::max(right->End, range.End);
left = right;
}
// Merge overlaps to the right
while (true)
{
++right;
if (right == ranges.end() || right->Start > range.End)
break;
left->End = std::max(right->End, range.End);
}
// Remove ranges now covered by the extended range
//ranges.erase(++left, right);
++left;
auto leftPos = left - ranges.begin();
auto rightPos = right - ranges.begin();
ranges.Delete(leftPos, rightPos - leftPos);
}
};