Hash :
aea88562
Author :
Date :
2023-05-19T16:52:43
Reland "Metal: Optimized BufferSubData per device"
This reverts commit ee64836f702332adaca58d9f452063a04b2da955 ,
relanding the patch stack described there.
Between patchsets 1 and 5:
- The shadow buffer allocation has been replaced with a multimap of
precisely-sized buffers, rather than rounding up buffer sizes.
- Garbage collection of shadow buffers is triggered in three situations:
- A certain number of context switches have occurred; this number
was hand-tuned to avoid GC every frame.
- A certain number of command buffer submissions has occurred; this
number was hand-tuned to GC no more often than every few seconds
on representative workloads.
- The total size of the allocated shadow buffers is more than 1 MB,
and either more than twice the size at the last garbage
collection, or 64 MB more than at the last garbage collection. In
this case, aggressive GC is performed in order to reclaim shadow
buffers more quickly.
Performance before and after these changes appears identical on
microbenchmarks. On one Figma test case, comparing GPU memory
allocated inside the BufferManager, peak consumption is decreased by
over 75%, and steady-state consumption decreases by over 88%.
Patchset 6 adds a needed workaround for a bug in the
AMDMTLBronzeDriver affecting uploads of client-side data, and
therefore some dEQP tests. It also streamlines the aggressive GC.
Bug: angleproject:7544
Change-Id: I81b061f0b33c27fa403527fa12d626f4e9c88ebe
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4497413
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// mtl_buffer_pool.h:
// Defines class interface for BufferPool, managing a pool of mtl::Buffer
//
#ifndef LIBANGLE_RENDERER_METAL_MTL_BUFFER_POOL_H_
#define LIBANGLE_RENDERER_METAL_MTL_BUFFER_POOL_H_
#include "libANGLE/renderer/metal/mtl_resources.h"
#include <deque>
namespace rx
{
class ContextMtl;
namespace mtl
{
// A buffer pool is conceptually an infinitely long buffer. Each time you write to the buffer,
// you will always write to a previously unused portion. After a series of writes, you must flush
// the buffer data to the device. Buffer lifetime currently assumes that each new allocation will
// last as long or longer than each prior allocation.
//
// Buffer pool is used to implement a variety of data streaming operations in Metal, such
// as for immediate vertex array and element array data, and other dynamic data.
//
// Internally buffer pool keeps a collection of mtl::Buffer. When we write past the end of a
// currently active mtl::Buffer we keep it until it is no longer in use. We then mark it available
// for future allocations in a free list.
class BufferPool
{
public:
BufferPool();
// - alwaysAllocNewBuffer=true will always allocate new buffer or reuse free buffer on
// allocate(), regardless of whether current buffer still has unused portion or not.
BufferPool(bool alwaysAllocNewBuffer);
~BufferPool();
// Init is called after the buffer creation so that the alignment can be specified later.
void initialize(Context *context, size_t initialSize, size_t alignment, size_t maxBuffers);
// Calling this without initialize() will have same effect as calling initialize().
// If called after initialize(), the old pending buffers will be flushed and might be re-used if
// their size are big enough for the requested initialSize parameter.
angle::Result reset(ContextMtl *contextMtl,
size_t initialSize,
size_t alignment,
size_t maxBuffers);
// This call will allocate a new region at the end of the buffer. It internally may trigger
// a new buffer to be created (which is returned in the optional parameter
// `newBufferAllocatedOut`). The new region will be in the returned buffer at given offset. If
// a memory pointer is given, the buffer will be automatically map()ed.
angle::Result allocate(ContextMtl *contextMtl,
size_t sizeInBytes,
uint8_t **ptrOut = nullptr,
BufferRef *bufferOut = nullptr,
size_t *offsetOut = nullptr,
bool *newBufferAllocatedOut = nullptr);
// After a sequence of CPU writes, call commit to ensure the data is visible to the GPU.
// Note: the data will only be made visible to the GPU if the buffer's storage mode is not
// shared AND a non-null pointer was passed to allocate(). Otherwise, this call only advances
// the flush pointer.
angle::Result commit(ContextMtl *contextMtl, bool flushEntireBuffer = false);
// This releases all the buffers that have been allocated since this was last called.
void releaseInFlightBuffers(ContextMtl *contextMtl);
// This frees resources immediately.
void destroy(ContextMtl *contextMtl);
const BufferRef &getCurrentBuffer() { return mBuffer; }
size_t getAlignment() { return mAlignment; }
void updateAlignment(Context *context, size_t alignment);
size_t getMaxBuffers() const { return mMaxBuffers; }
// Set whether allocate() will always allocate new buffer or attempting to append to previous
// buffer or not. Default is false.
void setAlwaysAllocateNewBuffer(bool e) { mAlwaysAllocateNewBuffer = e; }
private:
MTLStorageMode storageMode(ContextMtl *contextMtl) const;
void reset();
angle::Result allocateNewBuffer(ContextMtl *contextMtl);
void destroyBufferList(ContextMtl *contextMtl, std::deque<BufferRef> *buffers);
angle::Result finalizePendingBuffer(ContextMtl *contextMtl);
size_t mInitialSize;
BufferRef mBuffer;
uint32_t mNextAllocationOffset;
uint32_t mLastFlushOffset;
size_t mSize;
size_t mAlignment;
std::deque<BufferRef> mInFlightBuffers;
std::deque<BufferRef> mBufferFreeList;
size_t mBuffersAllocated;
size_t mMaxBuffers;
bool mAlwaysAllocateNewBuffer;
};
} // namespace mtl
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_MTL_BUFFER_POOL_H_ */