BLI: allow extending a Vector with another Vector with move-semantics

This enables moving elements form one vector to another.

Usually this is being doing by extending a vector with the content
from the secondary vector and then clearing the secondary vector.
However sometimes this being performed to transfer ownership of managed elements,
if elements are copied from the secondary vector, but not cleared, this could
lead to 2 vectors to share ownership of objects.

Pull Request: https://projects.blender.org/blender/blender/pulls/131560
This commit is contained in:
Guillermo Venegas
2024-12-11 17:43:44 +01:00
committed by Jacques Lucke
parent 661facbde0
commit 0576f22a67
3 changed files with 93 additions and 15 deletions

View File

@@ -566,6 +566,22 @@ class Vector {
this->extend_unchecked(start, amount);
}
/**
* Moves the elements of another vector to the end of this vector.
*
* This may result in reallocation to fit other vector elements, other vector will keep it
* buffer allocation, but it will become empty.
* This can be used in vectors that manages resources, allowing acquiring resources from another
* vector, preventing shared ownership of managed resources.
*/
template<int64_t OtherInlineBufferCapacity>
void extend(Vector<T, OtherInlineBufferCapacity, Allocator> &&other)
{
BLI_assert(this != &other);
this->extend(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()));
other.clear();
}
/**
* Adds all elements from the array that are not already in the vector. This is an expensive
* operation when the vector is large, but can be very cheap when it is known that the vector is
@@ -651,7 +667,12 @@ class Vector {
}
try {
std::uninitialized_copy_n(first, insert_amount, begin_ + insert_index);
if constexpr (std::is_rvalue_reference_v<decltype(*first)>) {
std::uninitialized_move_n(first, insert_amount, begin_ + insert_index);
}
else {
std::uninitialized_copy_n(first, insert_amount, begin_ + insert_index);
}
}
catch (...) {
/* Destruct all values that have been moved. */

View File

@@ -468,6 +468,69 @@ TEST(vector, ExtendArray)
EXPECT_EQ(a[1], 4);
}
TEST(vector, ExtendMoveFromSmallVector)
{
Vector<Vector<uint64_t, 0>> a = {
{1, 2, 3, 4, 5},
{6, 7, 8},
};
Vector<Vector<uint64_t, 0>> b = {
{9, 10, 11, 12},
{13, 14, 15, 16},
};
const Vector<Vector<uint64_t, 0>> c = {
{1, 2, 3, 4, 5},
{6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16},
};
a.extend(std::move(b));
EXPECT_EQ(a, c);
EXPECT_TRUE(b.is_empty());
}
TEST(vector, ExtendMoveFromUniquePtrVector)
{
Vector<int *> ptr_vec;
Vector<std::unique_ptr<int>> a;
a.append(std::make_unique<int>(0));
a.append(std::make_unique<int>(1));
a.append(std::make_unique<int>(2));
for (std::unique_ptr<int> &i : a) {
ptr_vec.append(i.get());
}
Vector<std::unique_ptr<int>> b;
b.append(std::make_unique<int>(7));
b.append(std::make_unique<int>(8));
b.append(std::make_unique<int>(9));
b.append(std::make_unique<int>(20));
for (std::unique_ptr<int> &i : b) {
ptr_vec.append(i.get());
}
ASSERT_EQ(a.size(), 3);
ASSERT_EQ(b.size(), 4);
a.extend(std::move(b));
std::array<int, 7> values = {0, 1, 2, 7, 8, 9, 20};
ASSERT_EQ(size_t(a.size()), values.size());
ASSERT_TRUE(b.is_empty());
ASSERT_EQ(a.size(), ptr_vec.size());
for (int64_t i = 0; i < a.size(); i++) {
ASSERT_EQ(*a[i], values[size_t(i)]);
ASSERT_EQ(a[i].get(), ptr_vec[i]);
}
}
TEST(vector, Last)
{
Vector<int> a{3, 5, 7};

View File

@@ -37,20 +37,14 @@ void VKDiscardPool::move_data(VKDiscardPool &src_pool)
{
std::scoped_lock mutex(mutex_);
std::scoped_lock mutex_src(src_pool.mutex_);
buffers_.extend(src_pool.buffers_);
image_views_.extend(src_pool.image_views_);
images_.extend(src_pool.images_);
shader_modules_.extend(src_pool.shader_modules_);
pipeline_layouts_.extend(src_pool.pipeline_layouts_);
framebuffers_.extend(src_pool.framebuffers_);
render_passes_.extend(src_pool.render_passes_);
src_pool.buffers_.clear();
src_pool.image_views_.clear();
src_pool.images_.clear();
src_pool.shader_modules_.clear();
src_pool.pipeline_layouts_.clear();
src_pool.framebuffers_.clear();
src_pool.render_passes_.clear();
buffers_.extend(std::move(src_pool.buffers_));
image_views_.extend(std::move(src_pool.image_views_));
images_.extend(std::move(src_pool.images_));
shader_modules_.extend(std::move(src_pool.shader_modules_));
pipeline_layouts_.extend(std::move(src_pool.pipeline_layouts_));
framebuffers_.extend(std::move(src_pool.framebuffers_));
render_passes_.extend(std::move(src_pool.render_passes_));
for (const Map<VkCommandPool, Vector<VkCommandBuffer>>::Item &item :
src_pool.command_buffers_.items())
{