Support flattening multiple CommandAllocators
This introduces CommandAllocator::AcquireCommandBlocks, which accepts a
vector of CommandAllocators and flattens them into a single iterable
sequence of commands. To support this, CommandAllocator is made movable.
Bug: dawn:809
Change-Id: I3984c243e4bd74568eccba1a8a58ec26324c8ffa
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/63822
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/CommandAllocator.cpp b/src/dawn_native/CommandAllocator.cpp
index 5ae5cfc..9516b59 100644
--- a/src/dawn_native/CommandAllocator.cpp
+++ b/src/dawn_native/CommandAllocator.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
#include <climits>
#include <cstdlib>
+#include <utility>
namespace dawn_native {
@@ -43,22 +44,32 @@
CommandIterator& CommandIterator::operator=(CommandIterator&& other) {
ASSERT(IsEmpty());
- mBlocks = std::move(other.mBlocks);
- other.Reset();
+ if (!other.IsEmpty()) {
+ mBlocks = std::move(other.mBlocks);
+ other.Reset();
+ }
Reset();
return *this;
}
- CommandIterator::CommandIterator(CommandAllocator&& allocator)
+ CommandIterator::CommandIterator(CommandAllocator allocator)
: mBlocks(allocator.AcquireBlocks()) {
Reset();
}
- CommandIterator& CommandIterator::operator=(CommandAllocator&& allocator) {
+ void CommandIterator::AcquireCommandBlocks(std::vector<CommandAllocator> allocators) {
ASSERT(IsEmpty());
- mBlocks = allocator.AcquireBlocks();
+ mBlocks.clear();
+ for (CommandAllocator& allocator : allocators) {
+ CommandBlocks blocks = allocator.AcquireBlocks();
+ if (!blocks.empty()) {
+ mBlocks.reserve(mBlocks.size() + blocks.size());
+ for (BlockDef& block : blocks) {
+ mBlocks.push_back(std::move(block));
+ }
+ }
+ }
Reset();
- return *this;
}
bool CommandIterator::NextCommandIdInNewBlock(uint32_t* commandId) {
@@ -92,7 +103,7 @@
return;
}
- for (auto& block : mBlocks) {
+ for (BlockDef& block : mBlocks) {
free(block.block);
}
mBlocks.clear();
@@ -114,13 +125,49 @@
// - Better block allocation, maybe have Dawn API to say command buffer is going to have size
// close to another
- CommandAllocator::CommandAllocator()
- : mCurrentPtr(reinterpret_cast<uint8_t*>(&mDummyEnum[0])),
- mEndPtr(reinterpret_cast<uint8_t*>(&mDummyEnum[1])) {
+ CommandAllocator::CommandAllocator() {
+ ResetPointers();
}
CommandAllocator::~CommandAllocator() {
- ASSERT(mBlocks.empty());
+ Reset();
+ }
+
+ CommandAllocator::CommandAllocator(CommandAllocator&& other)
+ : mBlocks(std::move(other.mBlocks)), mLastAllocationSize(other.mLastAllocationSize) {
+ other.mBlocks.clear();
+ if (!other.IsEmpty()) {
+ mCurrentPtr = other.mCurrentPtr;
+ mEndPtr = other.mEndPtr;
+ } else {
+ ResetPointers();
+ }
+ other.Reset();
+ }
+
+ CommandAllocator& CommandAllocator::operator=(CommandAllocator&& other) {
+ Reset();
+ if (!other.IsEmpty()) {
+ std::swap(mBlocks, other.mBlocks);
+ mLastAllocationSize = other.mLastAllocationSize;
+ mCurrentPtr = other.mCurrentPtr;
+ mEndPtr = other.mEndPtr;
+ }
+ other.Reset();
+ return *this;
+ }
+
+ void CommandAllocator::Reset() {
+ for (BlockDef& block : mBlocks) {
+ free(block.block);
+ }
+ mBlocks.clear();
+ mLastAllocationSize = kDefaultBaseAllocationSize;
+ ResetPointers();
+ }
+
+ bool CommandAllocator::IsEmpty() const {
+ return mCurrentPtr == reinterpret_cast<const uint8_t*>(&mDummyEnum[0]);
}
CommandBlocks&& CommandAllocator::AcquireBlocks() {
@@ -173,4 +220,9 @@
return true;
}
+ void CommandAllocator::ResetPointers() {
+ mCurrentPtr = reinterpret_cast<uint8_t*>(&mDummyEnum[0]);
+ mEndPtr = reinterpret_cast<uint8_t*>(&mDummyEnum[1]);
+ }
+
} // namespace dawn_native
diff --git a/src/dawn_native/CommandAllocator.h b/src/dawn_native/CommandAllocator.h
index 2713545..7a706aa 100644
--- a/src/dawn_native/CommandAllocator.h
+++ b/src/dawn_native/CommandAllocator.h
@@ -75,8 +75,10 @@
CommandIterator(CommandIterator&& other);
CommandIterator& operator=(CommandIterator&& other);
- CommandIterator(CommandAllocator&& allocator);
- CommandIterator& operator=(CommandAllocator&& allocator);
+ // Shorthand constructor for acquiring CommandBlocks from a single CommandAllocator.
+ explicit CommandIterator(CommandAllocator allocator);
+
+ void AcquireCommandBlocks(std::vector<CommandAllocator> allocators);
template <typename E>
bool NextCommandId(E* commandId) {
@@ -149,6 +151,15 @@
CommandAllocator();
~CommandAllocator();
+ // NOTE: A moved-from CommandAllocator is reset to its initial empty state.
+ CommandAllocator(CommandAllocator&&);
+ CommandAllocator& operator=(CommandAllocator&&);
+
+ // Frees all blocks held by the allocator and restores it to its initial empty state.
+ void Reset();
+
+ bool IsEmpty() const;
+
template <typename T, typename E>
T* Allocate(E commandId) {
static_assert(sizeof(E) == sizeof(uint32_t), "");
@@ -186,6 +197,9 @@
static constexpr size_t kWorstCaseAdditionalSize =
sizeof(uint32_t) + kMaxSupportedAlignment + alignof(uint32_t) + sizeof(uint32_t);
+ // The default value of mLastAllocationSize.
+ static constexpr size_t kDefaultBaseAllocationSize = 2048;
+
friend CommandIterator;
CommandBlocks&& AcquireBlocks();
@@ -237,19 +251,21 @@
bool GetNewBlock(size_t minimumSize);
+ void ResetPointers();
+
CommandBlocks mBlocks;
- size_t mLastAllocationSize = 2048;
+ size_t mLastAllocationSize = kDefaultBaseAllocationSize;
+
+ // Data used for the block range at initialization so that the first call to Allocate sees
+ // there is not enough space and calls GetNewBlock. This avoids having to special case the
+ // initialization in Allocate.
+ uint32_t mDummyEnum[1] = {0};
// Pointers to the current range of allocation in the block. Guaranteed to allow for at
// least one uint32_t if not nullptr, so that the special kEndOfBlock command id can always
// be written. Nullptr iff the blocks were moved out.
uint8_t* mCurrentPtr = nullptr;
uint8_t* mEndPtr = nullptr;
-
- // Data used for the block range at initialization so that the first call to Allocate sees
- // there is not enough space and calls GetNewBlock. This avoids having to special case the
- // initialization in Allocate.
- uint32_t mDummyEnum[1] = {0};
};
} // namespace dawn_native
diff --git a/src/dawn_native/EncodingContext.cpp b/src/dawn_native/EncodingContext.cpp
index fe347db..9e8812d 100644
--- a/src/dawn_native/EncodingContext.cpp
+++ b/src/dawn_native/EncodingContext.cpp
@@ -48,7 +48,7 @@
void EncodingContext::MoveToIterator() {
if (!mWasMovedToIterator) {
- mIterator = std::move(mAllocator);
+ mIterator = CommandIterator(std::move(mAllocator));
mWasMovedToIterator = true;
}
}
diff --git a/src/tests/unittests/CommandAllocatorTests.cpp b/src/tests/unittests/CommandAllocatorTests.cpp
index b01c3a5..5d1ca9d 100644
--- a/src/tests/unittests/CommandAllocatorTests.cpp
+++ b/src/tests/unittests/CommandAllocatorTests.cpp
@@ -428,7 +428,7 @@
iterator.MakeEmptyAsDataWasDestroyed();
}
-// Test that the allcator correctly defaults initalizes data for AllocateData
+// Test that the allocator correctly default-initalizes data for AllocateData
TEST(CommandAllocator, AllocateDataDefaultInitializes) {
CommandAllocator allocator;
@@ -447,3 +447,57 @@
CommandIterator iterator(std::move(allocator));
iterator.MakeEmptyAsDataWasDestroyed();
}
+
+// Tests flattening of multiple CommandAllocators into a single CommandIterator using
+// AcquireCommandBlocks.
+TEST(CommandAllocator, AcquireCommandBlocks) {
+ constexpr size_t kNumAllocators = 2;
+ constexpr size_t kNumCommandsPerAllocator = 2;
+ const uint64_t pipelines[kNumAllocators][kNumCommandsPerAllocator] = {
+ {0xDEADBEEFBEEFDEAD, 0xC0FFEEF00DC0FFEE},
+ {0x1337C0DE1337C0DE, 0xCAFEFACEFACECAFE},
+ };
+ const uint32_t attachmentPoints[kNumAllocators][kNumCommandsPerAllocator] = {{1, 2}, {3, 4}};
+ const uint32_t firsts[kNumAllocators][kNumCommandsPerAllocator] = {{42, 43}, {5, 6}};
+ const uint32_t counts[kNumAllocators][kNumCommandsPerAllocator] = {{16, 32}, {4, 8}};
+
+ std::vector<CommandAllocator> allocators(kNumAllocators);
+ for (size_t j = 0; j < kNumAllocators; ++j) {
+ CommandAllocator& allocator = allocators[j];
+ for (size_t i = 0; i < kNumCommandsPerAllocator; ++i) {
+ CommandPipeline* pipeline = allocator.Allocate<CommandPipeline>(CommandType::Pipeline);
+ pipeline->pipeline = pipelines[j][i];
+ pipeline->attachmentPoint = attachmentPoints[j][i];
+
+ CommandDraw* draw = allocator.Allocate<CommandDraw>(CommandType::Draw);
+ draw->first = firsts[j][i];
+ draw->count = counts[j][i];
+ }
+ }
+
+ CommandIterator iterator;
+ iterator.AcquireCommandBlocks(std::move(allocators));
+ for (size_t j = 0; j < kNumAllocators; ++j) {
+ for (size_t i = 0; i < kNumCommandsPerAllocator; ++i) {
+ CommandType type;
+ bool hasNext = iterator.NextCommandId(&type);
+ ASSERT_TRUE(hasNext);
+ ASSERT_EQ(type, CommandType::Pipeline);
+
+ CommandPipeline* pipeline = iterator.NextCommand<CommandPipeline>();
+ ASSERT_EQ(pipeline->pipeline, pipelines[j][i]);
+ ASSERT_EQ(pipeline->attachmentPoint, attachmentPoints[j][i]);
+
+ hasNext = iterator.NextCommandId(&type);
+ ASSERT_TRUE(hasNext);
+ ASSERT_EQ(type, CommandType::Draw);
+
+ CommandDraw* draw = iterator.NextCommand<CommandDraw>();
+ ASSERT_EQ(draw->first, firsts[j][i]);
+ ASSERT_EQ(draw->count, counts[j][i]);
+ }
+ }
+ CommandType type;
+ ASSERT_FALSE(iterator.NextCommandId(&type));
+ iterator.MakeEmptyAsDataWasDestroyed();
+}