зеркало из https://github.com/mozilla/gecko-dev.git
Cache MLGBuffers for systems that don't support constant buffer offsetting. (bug 1379714 part 2, r=bas)
This commit is contained in:
Родитель
4877a071cd
Коммит
6e75188a07
|
@ -5,14 +5,29 @@
|
|||
|
||||
#include "BufferCache.h"
|
||||
#include "MLGDevice.h"
|
||||
#include "ShaderDefinitionsMLGPU.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
using namespace mlg;
|
||||
|
||||
BufferCache::BufferCache(MLGDevice* aDevice)
|
||||
: mDevice(aDevice)
|
||||
: mDevice(aDevice),
|
||||
mFirstSizeClass(CeilingLog2(kConstantBufferElementSize)),
|
||||
mFrameNumber(0),
|
||||
mNextSizeClassToShrink(0)
|
||||
{
|
||||
// Create a cache of buffers for each size class, where each size class is a
|
||||
// power of 2 between the minimum and maximum size of a constant buffer.
|
||||
size_t maxBindSize = mDevice->GetMaxConstantBufferBindSize();
|
||||
MOZ_ASSERT(IsPowerOfTwo(maxBindSize));
|
||||
|
||||
size_t lastSizeClass = CeilingLog2(maxBindSize);
|
||||
MOZ_ASSERT(lastSizeClass >= mFirstSizeClass);
|
||||
|
||||
mCaches.resize(lastSizeClass - mFirstSizeClass + 1);
|
||||
}
|
||||
|
||||
BufferCache::~BufferCache()
|
||||
|
@ -22,12 +37,64 @@ BufferCache::~BufferCache()
|
|||
RefPtr<MLGBuffer>
|
||||
BufferCache::GetOrCreateBuffer(size_t aBytes)
|
||||
{
|
||||
return mDevice->CreateBuffer(MLGBufferType::Constant, aBytes, MLGUsage::Dynamic, nullptr);
|
||||
size_t sizeClass = CeilingLog2(aBytes);
|
||||
size_t sizeClassIndex = sizeClass - mFirstSizeClass;
|
||||
if (sizeClassIndex >= mCaches.size()) {
|
||||
return mDevice->CreateBuffer(MLGBufferType::Constant, aBytes, MLGUsage::Dynamic, nullptr);
|
||||
}
|
||||
|
||||
CachePool& pool = mCaches[sizeClassIndex];
|
||||
|
||||
// See if we've cached a buffer that wasn't used in the past 2 frames. A buffer
|
||||
// used this frame could have already been mapped and written to, and a buffer
|
||||
// used the previous frame might still be in-use by the GPU. While the latter
|
||||
// case is okay, it causes aliasing in the driver. Since content is double
|
||||
// buffered we do not let the compositor get more than 1 frames ahead, and a
|
||||
// count of 2 frames should ensure the buffer is unused.
|
||||
if (!pool.empty() && mFrameNumber >= pool.front().mLastUsedFrame + 2) {
|
||||
RefPtr<MLGBuffer> buffer = pool.front().mBuffer;
|
||||
pool.pop_front();
|
||||
pool.push_back(CacheEntry(mFrameNumber, buffer));
|
||||
MOZ_RELEASE_ASSERT(buffer->GetSize() >= aBytes);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Allocate a new buffer and cache it.
|
||||
size_t bytes = (size_t(1) << sizeClass);
|
||||
MOZ_ASSERT(bytes >= aBytes);
|
||||
|
||||
RefPtr<MLGBuffer> buffer =
|
||||
mDevice->CreateBuffer(MLGBufferType::Constant, bytes, MLGUsage::Dynamic, nullptr);
|
||||
if (!buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pool.push_back(CacheEntry(mFrameNumber, buffer));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void
|
||||
BufferCache::EndFrame()
|
||||
{
|
||||
// Consider a buffer dead after ~5 seconds assuming 60 fps.
|
||||
static size_t kMaxUnusedFrameCount = 60 * 5;
|
||||
|
||||
// At the end of each frame we pick one size class and see if it has any
|
||||
// buffers that haven't been used for many frames. If so we clear them.
|
||||
// The next frame we'll search the next size class. (This is just to spread
|
||||
// work over more than one frame.)
|
||||
CachePool& pool = mCaches[mNextSizeClassToShrink];
|
||||
while (!pool.empty()) {
|
||||
// Since the deque is sorted oldest-to-newest, front-to-back, we can stop
|
||||
// searching as soon as a buffer is active.
|
||||
if (mFrameNumber - pool.front().mLastUsedFrame < kMaxUnusedFrameCount) {
|
||||
break;
|
||||
}
|
||||
pool.pop_front();
|
||||
}
|
||||
mNextSizeClassToShrink = (mNextSizeClassToShrink + 1) % mCaches.size();
|
||||
|
||||
mFrameNumber++;
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
#define mozilla_gfx_layers_mlgpu_BufferCache_h
|
||||
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
@ -16,7 +16,7 @@ namespace layers {
|
|||
class MLGBuffer;
|
||||
class MLGDevice;
|
||||
|
||||
// Cache buffer pools based on how long ago they were last used.
|
||||
// Cache MLGBuffers based on how long ago they were last used.
|
||||
class BufferCache
|
||||
{
|
||||
public:
|
||||
|
@ -27,12 +27,58 @@ public:
|
|||
// if none can be re-used.
|
||||
RefPtr<MLGBuffer> GetOrCreateBuffer(size_t aBytes);
|
||||
|
||||
// Rotate buffers after a frame has been completed.
|
||||
// Age out old buffers after a frame has been completed.
|
||||
void EndFrame();
|
||||
|
||||
private:
|
||||
// Not RefPtr since this would create a cycle.
|
||||
MLGDevice* mDevice;
|
||||
|
||||
// The first size class is Log2(N), where 16 is the minimum size of a
|
||||
// constant buffer (currently 16 bytes).
|
||||
size_t mFirstSizeClass;
|
||||
|
||||
// Each size class is a power of 2. Each pool of buffers is represented as a
|
||||
// deque, with the least-recently-used (i.e., oldest) buffers at the front,
|
||||
// and most-recently-used (i.e., newest) buffers at the back. To re-use a
|
||||
// buffer it is popped off the front and re-added to the back.
|
||||
//
|
||||
// This is not always efficient use of storage: if a single frame allocates
|
||||
// 300 buffers of the same size, we may keep recycling through all those
|
||||
// buffers for a long time, as long as at least one gets used per frame.
|
||||
// But since buffers use tiny amounts of memory, and they are only mapped
|
||||
// while drawing, it shouldn't be a big deal.
|
||||
struct CacheEntry {
|
||||
CacheEntry() : mLastUsedFrame(0)
|
||||
{}
|
||||
CacheEntry(const CacheEntry& aEntry)
|
||||
: mLastUsedFrame(aEntry.mLastUsedFrame),
|
||||
mBuffer(aEntry.mBuffer)
|
||||
{}
|
||||
CacheEntry(CacheEntry&& aEntry)
|
||||
: mLastUsedFrame(aEntry.mLastUsedFrame),
|
||||
mBuffer(Move(aEntry.mBuffer))
|
||||
{}
|
||||
CacheEntry(size_t aLastUsedFrame, MLGBuffer* aBuffer)
|
||||
: mLastUsedFrame(aLastUsedFrame),
|
||||
mBuffer(aBuffer)
|
||||
{}
|
||||
|
||||
uint64_t mLastUsedFrame;
|
||||
RefPtr<MLGBuffer> mBuffer;
|
||||
};
|
||||
typedef std::deque<CacheEntry> CachePool;
|
||||
|
||||
// We track how many frames have occurred to determine the age of cache entries.
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// To avoid doing too much work in one frame, we only shrink one size class
|
||||
// per frame.
|
||||
uint64_t mNextSizeClassToShrink;
|
||||
|
||||
// There is one pool of buffers for each power of 2 allocation size. The
|
||||
// maximum buffer size is at most 64KB on Direct3D 11.
|
||||
std::vector<CachePool> mCaches;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
|
|
@ -409,6 +409,8 @@ public:
|
|||
// Return the maximum number of elements that can be bound to a constant
|
||||
// buffer. This is different than the maximum size of a buffer (there is
|
||||
// no such limit on Direct3D 11.1).
|
||||
//
|
||||
// The return value must be a power of two.
|
||||
size_t GetMaxConstantBufferBindSize() const {
|
||||
return mMaxConstantBufferBindSize;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче