Replace size_t with uint64_t in ringbuffer.
Adds overflow check in RingBufferAllocator + unit-test. Also, update clients to use uint64_t to avoid casts or narrowing. BUG=dawn:233 Change-Id: I652e3142407006d082491add600371f95d44741a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12380 Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
This commit is contained in:
parent
4794168ef8
commit
f603903da7
|
@ -18,7 +18,7 @@
|
|||
|
||||
namespace dawn_native {
|
||||
|
||||
DynamicUploader::DynamicUploader(DeviceBase* device, size_t size) : mDevice(device) {
|
||||
DynamicUploader::DynamicUploader(DeviceBase* device, uint64_t size) : mDevice(device) {
|
||||
mRingBuffers.emplace_back(
|
||||
std::unique_ptr<RingBuffer>(new RingBuffer{nullptr, RingBufferAllocator(size)}));
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace dawn_native {
|
|||
mDevice->GetPendingCommandSerial());
|
||||
}
|
||||
|
||||
ResultOrError<UploadHandle> DynamicUploader::Allocate(size_t allocationSize, Serial serial) {
|
||||
ResultOrError<UploadHandle> DynamicUploader::Allocate(uint64_t allocationSize, Serial serial) {
|
||||
// Note: Validation ensures size is already aligned.
|
||||
// First-fit: find next smallest buffer large enough to satisfy the allocation request.
|
||||
RingBuffer* targetRingBuffer = mRingBuffers.back().get();
|
||||
|
@ -36,7 +36,7 @@ namespace dawn_native {
|
|||
const RingBufferAllocator& ringBufferAllocator = ringBuffer->mAllocator;
|
||||
// Prevent overflow.
|
||||
ASSERT(ringBufferAllocator.GetSize() >= ringBufferAllocator.GetUsedSize());
|
||||
const size_t remainingSize =
|
||||
const uint64_t remainingSize =
|
||||
ringBufferAllocator.GetSize() - ringBufferAllocator.GetUsedSize();
|
||||
if (allocationSize <= remainingSize) {
|
||||
targetRingBuffer = ringBuffer.get();
|
||||
|
@ -44,7 +44,7 @@ namespace dawn_native {
|
|||
}
|
||||
}
|
||||
|
||||
size_t startOffset = RingBufferAllocator::kInvalidOffset;
|
||||
uint64_t startOffset = RingBufferAllocator::kInvalidOffset;
|
||||
if (targetRingBuffer != nullptr) {
|
||||
startOffset = targetRingBuffer->mAllocator.Allocate(allocationSize, serial);
|
||||
}
|
||||
|
|
|
@ -25,13 +25,13 @@ namespace dawn_native {
|
|||
|
||||
struct UploadHandle {
|
||||
uint8_t* mappedBuffer = nullptr;
|
||||
size_t startOffset = 0;
|
||||
uint64_t startOffset = 0;
|
||||
StagingBufferBase* stagingBuffer = nullptr;
|
||||
};
|
||||
|
||||
class DynamicUploader {
|
||||
public:
|
||||
DynamicUploader(DeviceBase* device, size_t size = kBaseUploadBufferSize);
|
||||
DynamicUploader(DeviceBase* device, uint64_t size = kBaseUploadBufferSize);
|
||||
~DynamicUploader() = default;
|
||||
|
||||
// We add functions to Release StagingBuffers to the DynamicUploader as there's
|
||||
|
@ -40,12 +40,12 @@ namespace dawn_native {
|
|||
// implemented.
|
||||
void ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer);
|
||||
|
||||
ResultOrError<UploadHandle> Allocate(size_t allocationSize, Serial serial);
|
||||
ResultOrError<UploadHandle> Allocate(uint64_t allocationSize, Serial serial);
|
||||
void Deallocate(Serial lastCompletedSerial);
|
||||
|
||||
private:
|
||||
// TODO(bryan.bernhart@intel.com): Figure out this value.
|
||||
static constexpr size_t kBaseUploadBufferSize = 64000;
|
||||
static constexpr uint64_t kBaseUploadBufferSize = 64000;
|
||||
|
||||
struct RingBuffer {
|
||||
std::unique_ptr<StagingBufferBase> mStagingBuffer;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
// TODO(bryan.bernhart@intel.com): Follow-up with ringbuffer optimization.
|
||||
namespace dawn_native {
|
||||
|
||||
RingBufferAllocator::RingBufferAllocator(size_t maxSize) : mMaxBlockSize(maxSize) {
|
||||
RingBufferAllocator::RingBufferAllocator(uint64_t maxSize) : mMaxBlockSize(maxSize) {
|
||||
}
|
||||
|
||||
void RingBufferAllocator::Deallocate(Serial lastCompletedSerial) {
|
||||
|
@ -43,11 +43,11 @@ namespace dawn_native {
|
|||
mInflightRequests.ClearUpTo(lastCompletedSerial);
|
||||
}
|
||||
|
||||
size_t RingBufferAllocator::GetSize() const {
|
||||
uint64_t RingBufferAllocator::GetSize() const {
|
||||
return mMaxBlockSize;
|
||||
}
|
||||
|
||||
size_t RingBufferAllocator::GetUsedSize() const {
|
||||
uint64_t RingBufferAllocator::GetUsedSize() const {
|
||||
return mUsedSize;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ namespace dawn_native {
|
|||
// queue, which identifies an existing (or new) frames-worth of resources. Internally, the
|
||||
// ring-buffer maintains offsets of 3 "memory" states: Free, Reclaimed, and Used. This is done
|
||||
// in FIFO order as older frames would free resources before newer ones.
|
||||
size_t RingBufferAllocator::Allocate(size_t allocationSize, Serial serial) {
|
||||
uint64_t RingBufferAllocator::Allocate(uint64_t allocationSize, Serial serial) {
|
||||
// Check if the buffer is full by comparing the used size.
|
||||
// If the buffer is not split where waste occurs (e.g. cannot fit new sub-alloc in front), a
|
||||
// subsequent sub-alloc could fail where the used size was previously adjusted to include
|
||||
|
@ -71,7 +71,13 @@ namespace dawn_native {
|
|||
return kInvalidOffset;
|
||||
}
|
||||
|
||||
size_t startOffset = kInvalidOffset;
|
||||
// Ensure adding allocationSize does not overflow.
|
||||
const uint64_t remainingSize = (mMaxBlockSize - mUsedSize);
|
||||
if (allocationSize > remainingSize) {
|
||||
return kInvalidOffset;
|
||||
}
|
||||
|
||||
uint64_t startOffset = kInvalidOffset;
|
||||
|
||||
// Check if the buffer is NOT split (i.e sub-alloc on ends)
|
||||
if (mUsedStartOffset <= mUsedEndOffset) {
|
||||
|
@ -86,7 +92,7 @@ namespace dawn_native {
|
|||
} else if (allocationSize <= mUsedStartOffset) { // Try to sub-alloc at front.
|
||||
// Count the space at the end so that a subsequent
|
||||
// sub-alloc cannot not succeed when the buffer is full.
|
||||
const size_t requestSize = (mMaxBlockSize - mUsedEndOffset) + allocationSize;
|
||||
const uint64_t requestSize = (mMaxBlockSize - mUsedEndOffset) + allocationSize;
|
||||
|
||||
startOffset = 0;
|
||||
mUsedEndOffset = allocationSize;
|
||||
|
|
|
@ -26,32 +26,32 @@ namespace dawn_native {
|
|||
class RingBufferAllocator {
|
||||
public:
|
||||
RingBufferAllocator() = default;
|
||||
RingBufferAllocator(size_t maxSize);
|
||||
RingBufferAllocator(uint64_t maxSize);
|
||||
~RingBufferAllocator() = default;
|
||||
|
||||
size_t Allocate(size_t allocationSize, Serial serial);
|
||||
uint64_t Allocate(uint64_t allocationSize, Serial serial);
|
||||
void Deallocate(Serial lastCompletedSerial);
|
||||
|
||||
size_t GetSize() const;
|
||||
uint64_t GetSize() const;
|
||||
bool Empty() const;
|
||||
size_t GetUsedSize() const;
|
||||
uint64_t GetUsedSize() const;
|
||||
|
||||
static constexpr size_t kInvalidOffset = std::numeric_limits<size_t>::max();
|
||||
static constexpr uint64_t kInvalidOffset = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
private:
|
||||
struct Request {
|
||||
size_t endOffset;
|
||||
size_t size;
|
||||
uint64_t endOffset;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
SerialQueue<Request> mInflightRequests; // Queue of the recorded sub-alloc requests (e.g.
|
||||
// frame of resources).
|
||||
|
||||
size_t mUsedEndOffset = 0; // Tail of used sub-alloc requests (in bytes).
|
||||
size_t mUsedStartOffset = 0; // Head of used sub-alloc requests (in bytes).
|
||||
size_t mMaxBlockSize = 0; // Max size of the ring buffer (in bytes).
|
||||
size_t mUsedSize = 0; // Size of the sub-alloc requests (in bytes) of the ring buffer.
|
||||
size_t mCurrentRequestSize =
|
||||
uint64_t mUsedEndOffset = 0; // Tail of used sub-alloc requests (in bytes).
|
||||
uint64_t mUsedStartOffset = 0; // Head of used sub-alloc requests (in bytes).
|
||||
uint64_t mMaxBlockSize = 0; // Max size of the ring buffer (in bytes).
|
||||
uint64_t mUsedSize = 0; // Size of the sub-alloc requests (in bytes) of the ring buffer.
|
||||
uint64_t mCurrentRequestSize =
|
||||
0; // Size of the sub-alloc requests (in bytes) of the current serial.
|
||||
};
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
DescriptorHeapHandle::DescriptorHeapHandle(ComPtr<ID3D12DescriptorHeap> descriptorHeap,
|
||||
uint32_t sizeIncrement,
|
||||
uint32_t offset)
|
||||
uint64_t offset)
|
||||
: mDescriptorHeap(descriptorHeap), mSizeIncrement(sizeIncrement), mOffset(offset) {
|
||||
}
|
||||
|
||||
|
@ -68,12 +68,11 @@ namespace dawn_native { namespace d3d12 {
|
|||
DescriptorHeapInfo* heapInfo,
|
||||
D3D12_DESCRIPTOR_HEAP_FLAGS flags) {
|
||||
const Serial pendingSerial = mDevice->GetPendingCommandSerial();
|
||||
size_t startOffset = (heapInfo->heap == nullptr)
|
||||
? RingBufferAllocator::kInvalidOffset
|
||||
: heapInfo->allocator.Allocate(count, pendingSerial);
|
||||
uint64_t startOffset = (heapInfo->heap == nullptr)
|
||||
? RingBufferAllocator::kInvalidOffset
|
||||
: heapInfo->allocator.Allocate(count, pendingSerial);
|
||||
if (startOffset != RingBufferAllocator::kInvalidOffset) {
|
||||
return DescriptorHeapHandle{heapInfo->heap, mSizeIncrements[type],
|
||||
static_cast<uint32_t>(startOffset)};
|
||||
return DescriptorHeapHandle{heapInfo->heap, mSizeIncrements[type], startOffset};
|
||||
}
|
||||
|
||||
// If the pool has no more space, replace the pool with a new one of the specified size
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
DescriptorHeapHandle();
|
||||
DescriptorHeapHandle(ComPtr<ID3D12DescriptorHeap> descriptorHeap,
|
||||
uint32_t sizeIncrement,
|
||||
uint32_t offset);
|
||||
uint64_t offset);
|
||||
|
||||
ID3D12DescriptorHeap* Get() const;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE GetCPUHandle(uint32_t index) const;
|
||||
|
@ -42,7 +42,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
private:
|
||||
ComPtr<ID3D12DescriptorHeap> mDescriptorHeap;
|
||||
uint32_t mSizeIncrement;
|
||||
uint32_t mOffset;
|
||||
uint64_t mOffset;
|
||||
};
|
||||
|
||||
class DescriptorHeapAllocator {
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
|
||||
using namespace dawn_native;
|
||||
|
||||
constexpr size_t RingBufferAllocator::kInvalidOffset;
|
||||
constexpr uint64_t RingBufferAllocator::kInvalidOffset;
|
||||
|
||||
// Number of basic tests for Ringbuffer
|
||||
TEST(RingBufferAllocatorTests, BasicTest) {
|
||||
constexpr size_t sizeInBytes = 64000;
|
||||
constexpr uint64_t sizeInBytes = 64000;
|
||||
RingBufferAllocator allocator(sizeInBytes);
|
||||
|
||||
// Ensure no requests exist on empty buffer.
|
||||
|
@ -43,8 +43,8 @@ TEST(RingBufferAllocatorTests, BasicTest) {
|
|||
|
||||
// Tests that several ringbuffer allocations do not fail.
|
||||
TEST(RingBufferAllocatorTests, RingBufferManyAlloc) {
|
||||
constexpr size_t maxNumOfFrames = 64000;
|
||||
constexpr size_t frameSizeInBytes = 4;
|
||||
constexpr uint64_t maxNumOfFrames = 64000;
|
||||
constexpr uint64_t frameSizeInBytes = 4;
|
||||
|
||||
RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes);
|
||||
|
||||
|
@ -57,8 +57,8 @@ TEST(RingBufferAllocatorTests, RingBufferManyAlloc) {
|
|||
|
||||
// Tests ringbuffer sub-allocations of the same serial are correctly tracked.
|
||||
TEST(RingBufferAllocatorTests, AllocInSameFrame) {
|
||||
constexpr size_t maxNumOfFrames = 3;
|
||||
constexpr size_t frameSizeInBytes = 4;
|
||||
constexpr uint64_t maxNumOfFrames = 3;
|
||||
constexpr uint64_t frameSizeInBytes = 4;
|
||||
|
||||
RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes);
|
||||
|
||||
|
@ -87,8 +87,8 @@ TEST(RingBufferAllocatorTests, AllocInSameFrame) {
|
|||
|
||||
// Tests ringbuffer sub-allocation at various offsets.
|
||||
TEST(RingBufferAllocatorTests, RingBufferSubAlloc) {
|
||||
constexpr size_t maxNumOfFrames = 10;
|
||||
constexpr size_t frameSizeInBytes = 4;
|
||||
constexpr uint64_t maxNumOfFrames = 10;
|
||||
constexpr uint64_t frameSizeInBytes = 4;
|
||||
|
||||
RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes);
|
||||
|
||||
|
@ -164,3 +164,14 @@ TEST(RingBufferAllocatorTests, RingBufferSubAlloc) {
|
|||
|
||||
EXPECT_TRUE(allocator.Empty());
|
||||
}
|
||||
|
||||
// Checks if ringbuffer sub-allocation does not overflow.
|
||||
TEST(RingBufferAllocatorTests, RingBufferOverflow) {
|
||||
Serial serial = 1;
|
||||
|
||||
RingBufferAllocator allocator(std::numeric_limits<uint64_t>::max());
|
||||
|
||||
ASSERT_EQ(allocator.Allocate(1, serial), 0u);
|
||||
ASSERT_EQ(allocator.Allocate(std::numeric_limits<uint64_t>::max(), serial + 1),
|
||||
RingBufferAllocator::kInvalidOffset);
|
||||
}
|
Loading…
Reference in New Issue