Limit heap growth when uploading with SetSubData.

Removes unbounded heap growth by capping the heap
size to 4MB and falling back to direct allocation for larger
requests.

BUG=dawn:239

Change-Id: I9ae660809e3c0c539fbcfbee4afcf6fb1f466355
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12720
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
This commit is contained in:
Bryan Bernhart 2019-10-29 22:49:43 +00:00 committed by Commit Bot service account
parent 6eb7d0ec3e
commit 3debb26a9e
3 changed files with 20 additions and 19 deletions

View File

@ -18,9 +18,9 @@
namespace dawn_native {
DynamicUploader::DynamicUploader(DeviceBase* device, uint64_t size) : mDevice(device) {
mRingBuffers.emplace_back(
std::unique_ptr<RingBuffer>(new RingBuffer{nullptr, RingBufferAllocator(size)}));
DynamicUploader::DynamicUploader(DeviceBase* device) : mDevice(device) {
mRingBuffers.emplace_back(std::unique_ptr<RingBuffer>(
new RingBuffer{nullptr, RingBufferAllocator(kRingBufferSize)}));
}
void DynamicUploader::ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer) {
@ -29,6 +29,19 @@ namespace dawn_native {
}
ResultOrError<UploadHandle> DynamicUploader::Allocate(uint64_t allocationSize, Serial serial) {
// Disable further sub-allocation should the request be too large.
if (allocationSize > kRingBufferSize) {
std::unique_ptr<StagingBufferBase> stagingBuffer;
DAWN_TRY_ASSIGN(stagingBuffer, mDevice->CreateStagingBuffer(allocationSize));
UploadHandle uploadHandle;
uploadHandle.mappedBuffer = static_cast<uint8_t*>(stagingBuffer->GetMappedPointer());
uploadHandle.stagingBuffer = stagingBuffer.get();
ReleaseStagingBuffer(std::move(stagingBuffer));
return uploadHandle;
}
// 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();
@ -49,18 +62,11 @@ namespace dawn_native {
startOffset = targetRingBuffer->mAllocator.Allocate(allocationSize, serial);
}
// Upon failure, append a newly created (and much larger) ring buffer to fulfill the
// Upon failure, append a newly created ring buffer to fulfill the
// request.
if (startOffset == RingBufferAllocator::kInvalidOffset) {
// Compute the new max size (in powers of two to preserve alignment).
size_t newMaxSize = targetRingBuffer->mAllocator.GetSize() * 2;
while (newMaxSize < allocationSize) {
newMaxSize *= 2;
}
// TODO(bryan.bernhart@intel.com): Fall-back to no sub-allocations should this fail.
mRingBuffers.emplace_back(std::unique_ptr<RingBuffer>(
new RingBuffer{nullptr, RingBufferAllocator(newMaxSize)}));
new RingBuffer{nullptr, RingBufferAllocator(kRingBufferSize)}));
targetRingBuffer = mRingBuffers.back().get();
startOffset = targetRingBuffer->mAllocator.Allocate(allocationSize, serial);

View File

@ -31,7 +31,7 @@ namespace dawn_native {
class DynamicUploader {
public:
DynamicUploader(DeviceBase* device, uint64_t size = kBaseUploadBufferSize);
DynamicUploader(DeviceBase* device);
~DynamicUploader() = default;
// We add functions to Release StagingBuffers to the DynamicUploader as there's
@ -44,8 +44,7 @@ namespace dawn_native {
void Deallocate(Serial lastCompletedSerial);
private:
// TODO(bryan.bernhart@intel.com): Figure out this value.
static constexpr uint64_t kBaseUploadBufferSize = 64000;
static constexpr uint64_t kRingBufferSize = 4 * 1024 * 1024;
struct RingBuffer {
std::unique_ptr<StagingBufferBase> mStagingBuffer;

View File

@ -142,10 +142,6 @@ void BufferUploadPerf::Step() {
}
TEST_P(BufferUploadPerf, Run) {
// TODO(crbug.com/dawn/239): Investigate why large buffer uploads via SetSubData fail.
DAWN_SKIP_TEST_IF(GetParam().uploadMethod == UploadMethod::SetSubData &&
GetParam().uploadSize == UploadSize::BufferSize_16MB);
RunTest();
}