Skip to content

Commit

Permalink
#5912: Extend IGeometryStore interface by setSubData() method that is…
Browse files Browse the repository at this point in the history
… updating only a part of the allocated memory.
  • Loading branch information
codereader committed Mar 4, 2022
1 parent 20a0444 commit d0dc716
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
8 changes: 8 additions & 0 deletions include/igeometrystore.h
Expand Up @@ -45,6 +45,14 @@ class IGeometryStore
virtual void updateData(Slot slot, const std::vector<ArbitraryMeshVertex>& vertices,
const std::vector<unsigned int>& indices) = 0;

/**
* Load a chunk of vertex and index data into the specified range, starting
* from vertexOffset/indexOffset respectively. The affected range must not be out of bounds
* of the allocated slot.
*/
virtual void updateSubData(Slot slot, std::size_t vertexOffset, const std::vector<ArbitraryMeshVertex>& vertices,
std::size_t indexOffset, const std::vector<unsigned int>& indices) = 0;

virtual void deallocateSlot(Slot slot) = 0;

// The render parameters suitable for rendering surfaces using gl(Multi)DrawElements
Expand Down
14 changes: 14 additions & 0 deletions libs/render/ContinuousBuffer.h
Expand Up @@ -144,6 +144,20 @@ class ContinuousBuffer
slot.Used = numElements;
}

void setSubData(Handle handle, std::size_t elementOffset, const std::vector<ElementType>& elements)
{
auto& slot = _slots[handle];

auto numElements = elements.size();
if (elementOffset + numElements > slot.Size)
{
throw std::logic_error("Cannot store more data than allocated in GeometryStore::Buffer::setSubData");
}

std::copy(elements.begin(), elements.end(), _buffer.begin() + slot.Offset + elementOffset);
slot.Used = std::max(slot.Used, elementOffset + numElements);
}

void deallocate(Handle handle)
{
auto& releasedSlot = _slots[handle];
Expand Down
16 changes: 16 additions & 0 deletions radiantcore/rendersystem/backend/GeometryStore.h
Expand Up @@ -125,6 +125,22 @@ class GeometryStore :
});
}

void updateSubData(Slot slot, std::size_t vertexOffset, const std::vector<ArbitraryMeshVertex>& vertices,
std::size_t indexOffset, const std::vector<unsigned int>& indices) override
{
assert(!vertices.empty());
assert(!indices.empty());

auto& current = getCurrentBuffer();

current.vertices.setSubData(GetVertexSlot(slot), vertexOffset, vertices);
current.indices.setSubData(GetIndexSlot(slot), indexOffset, indices);

_transactionLog.emplace_back(detail::BufferTransaction{
slot, detail::BufferTransaction::Type::Update
});
}

void deallocateSlot(Slot slot) override
{
auto& current = getCurrentBuffer();
Expand Down
76 changes: 76 additions & 0 deletions test/ContinuousBuffer.cpp
Expand Up @@ -132,6 +132,82 @@ TEST(ContinuousBufferTest, ReplaceDataOverflow)
EXPECT_THROW(buffer.setData(handle, eight), std::logic_error);
}

TEST(ContinuousBufferTest, ReplaceSubData)
{
auto eight = std::vector<int>({ 0,1,2,3,4,5,6,7 });
auto five = std::vector<int>({ 8,9,10,11,12 });

render::ContinuousBuffer<int> buffer(24);

auto handle = buffer.allocate(eight.size());
buffer.setData(handle, eight);

EXPECT_TRUE(checkData(buffer, handle, eight));

EXPECT_EQ(buffer.getSize(handle), eight.size());
EXPECT_EQ(buffer.getNumUsedElements(handle), eight.size());

// Update the data portion at various offsets
buffer.setSubData(handle, 0, five);
EXPECT_TRUE(checkData(buffer, handle, { 8,9,10,11,12, 5,6,7 }));
EXPECT_EQ(buffer.getNumUsedElements(handle), 8);

buffer.setSubData(handle, 1, five);
EXPECT_TRUE(checkData(buffer, handle, { 8, 8,9,10,11,12, 6,7 }));
EXPECT_EQ(buffer.getNumUsedElements(handle), 8);

buffer.setSubData(handle, 2, five);
EXPECT_TRUE(checkData(buffer, handle, { 8,8, 8,9,10,11,12, 7 }));
EXPECT_EQ(buffer.getNumUsedElements(handle), 8);

buffer.setSubData(handle, 3, five);
EXPECT_TRUE(checkData(buffer, handle, { 8,8,8, 8,9,10,11,12 }));
EXPECT_EQ(buffer.getNumUsedElements(handle), 8);
}

// In a chunk of memory that is only partially used, calling setSubData
// can increase that amount of used elements
TEST(ContinuousBufferTest, ReplaceSubDataIncreasesUsedSize)
{
auto eight = std::vector<int>({ 0,1,2,3,4,5,6,7 });
auto five = std::vector<int>({ 8,9,10,11,12 });

render::ContinuousBuffer<int> buffer(24);

// Allocate 6 more elements than needed
auto allocationSize = eight.size() + 6;
auto handle = buffer.allocate(allocationSize);
buffer.setData(handle, eight);

EXPECT_TRUE(checkData(buffer, handle, eight));

EXPECT_EQ(buffer.getSize(handle), allocationSize);
EXPECT_EQ(buffer.getNumUsedElements(handle), eight.size());

// Update the data portion at an offset that increases the fill rate
buffer.setSubData(handle, 4, five);
EXPECT_TRUE(checkData(buffer, handle, { 0,1,2,3,8,9,10,11,12 }));
EXPECT_EQ(buffer.getSize(handle), allocationSize);
EXPECT_EQ(buffer.getNumUsedElements(handle), 9) << "Should have increased use count to 9";
}

// Tests that subdata is still respecting the allocation bounds
TEST(ContinuousBufferTest, ReplaceSubDataOverflow)
{
auto eight = std::vector<int>({ 0,1,2,3,4,5,6,7 });
auto five = std::vector<int>({ 8,9,10,11,12 });

render::ContinuousBuffer<int> buffer(24);

auto handle = buffer.allocate(eight.size());
buffer.setData(handle, eight);

EXPECT_TRUE(checkData(buffer, handle, eight));

// Update the data portion at an invalid offset, exceeding the allocated slot size
EXPECT_THROW(buffer.setSubData(handle, 4, five), std::logic_error);
}

TEST(ContinuousBufferTest, BufferGrowth)
{
auto sixteen = std::vector<int>({ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 });
Expand Down

0 comments on commit d0dc716

Please sign in to comment.