Skip to content

Commit

Permalink
#5584: Implement CompactWindingVertexBuffer::removeWindings to provid…
Browse files Browse the repository at this point in the history
…e a removal algorithm that touches every moved element only once.
  • Loading branch information
codereader committed Nov 20, 2021
1 parent 32ade0e commit 734310b
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 1 deletion.
51 changes: 51 additions & 0 deletions libs/render/CompactWindingVertexBuffer.h
Expand Up @@ -167,6 +167,57 @@ class CompactWindingVertexBuffer
// So just cut off one winding from the end of the index array
_indices.resize(_indices.size() - getNumIndicesPerWinding());
}

// Removes multiple slots from this buffer in one sweep. The given array of slots must be sorted in ascending order
void removeWindings(const std::vector<Slot>& slotsToRemove)
{
if (slotsToRemove.empty()) return;

auto highestPossibleSlotNumber = static_cast<Slot>(_vertices.size() / _size);

auto s = slotsToRemove.begin();
auto gapStart = *s; // points at the first position that can be overwritten

while (s != slotsToRemove.end())
{
auto slotToRemove = *s;

if (slotToRemove >= highestPossibleSlotNumber) throw std::logic_error("Slot index out of bounds");

// Move forward until we hit the next unremoved slot
auto firstSlotToKeep = slotToRemove + 1;
++s;

while (s != slotsToRemove.end() && *s == firstSlotToKeep)
{
++firstSlotToKeep;
++s;
}

auto lastSlotToKeep = s == slotsToRemove.end() ? highestPossibleSlotNumber - 1 : *s - 1;
auto numSlotsToMove = lastSlotToKeep + 1 - firstSlotToKeep;

if (numSlotsToMove == 0) break;

// We move all vertices to the gap
auto target = _vertices.begin() + (gapStart * _size);

auto sourceStart = _vertices.begin() + (firstSlotToKeep * _size);
auto sourceEnd = sourceStart + (numSlotsToMove * _size);

std::move(sourceStart, sourceEnd, target);

gapStart += numSlotsToMove;
}

// Cut off the now unused range at the end
_vertices.resize(_vertices.size() - slotsToRemove.size() * _size);

// Since all the windings have the same structure, the index array will always look the same
// after shifting the index values of the remaining windings.
// So just cut off one winding from the end of the index array
_indices.resize(_indices.size() - slotsToRemove.size() * getNumIndicesPerWinding());
}
};

}
6 changes: 5 additions & 1 deletion libs/render/WindingRenderer.h
Expand Up @@ -256,6 +256,10 @@ class WindingRenderer :

std::sort(bucket.pendingDeletions.begin(), bucket.pendingDeletions.end());

#if 1
// Remove the winding from the bucket
bucket.buffer.removeWindings(bucket.pendingDeletions);
#else
for (auto s = bucket.pendingDeletions.rbegin(); s != bucket.pendingDeletions.rend(); ++s)
{
auto slotNumber = *s;
Expand All @@ -273,7 +277,7 @@ class WindingRenderer :
}
}
}

#endif
bucket.pendingDeletions.clear();
}

Expand Down
55 changes: 55 additions & 0 deletions test/WindingRendering.cpp
Expand Up @@ -226,6 +226,61 @@ TEST(CompactWindingVertexBuffer, ReplaceWinding)
}
}

TEST(CompactWindingVertexBuffer, RemoveMultipleWindings)
{
for (auto size = SmallestWindingSize; size < LargestWindingSize; ++size)
{
// We will work with a buffer containing N windings,
// the test will remove 1 to N windings from every possible position (2^N - 1)
constexpr auto NumWindings = 9;

for (int combination = 1; combination <= (1 << NumWindings) - 1; ++combination)
{
VertexBuffer buffer(size);

// Add the desired number of windings to the buffer
for (auto n = 1; n <= NumWindings; ++n)
{
buffer.pushWinding(createWinding(n, size));
}

std::vector<VertexBuffer::Slot> slotsToRemove;

// Translate the bit combination to a set of slots to remove
for (int pos = 0; pos < NumWindings; ++pos)
{
if (combination & (1 << pos))
{
slotsToRemove.push_back(pos);
}
}

buffer.removeWindings(slotsToRemove);

// The buffer should be smaller now
auto remainingWindings = NumWindings - slotsToRemove.size();

EXPECT_EQ(buffer.getVertices().size(), remainingWindings * buffer.getWindingSize()) <<
"Winding vertex array has the wrong size after removal";
EXPECT_EQ(buffer.getIndices().size(), remainingWindings * buffer.getNumIndicesPerWinding()) <<
"Winding index array has the wrong size after removal";

// Check the winding data, the ones we removed should be missing now
for (int i = 0, slot = 0; i < NumWindings; ++i)
{
if (std::find(slotsToRemove.begin(), slotsToRemove.end(), i) != slotsToRemove.end())
{
continue; // this id got removed, skip it
}

checkWindingIndices(buffer, slot);
checkWindingDataInSlot(buffer, slot, i + 1);
++slot;
}
}
}
}

TEST(CompactWindingVertexBuffer, TriangleIndexerSize3) // Winding size == 3
{
render::CompactWindingVertexBuffer<ArbitraryMeshVertex, render::WindingIndexer_Triangles> buffer(3);
Expand Down

0 comments on commit 734310b

Please sign in to comment.