Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "BufferCache.h"
7 : #include "MLGDevice.h"
8 : #include "ShaderDefinitionsMLGPU.h"
9 : #include "mozilla/MathAlgorithms.h"
10 :
11 : namespace mozilla {
12 : namespace layers {
13 :
14 : using namespace mlg;
15 :
16 0 : BufferCache::BufferCache(MLGDevice* aDevice)
17 : : mDevice(aDevice),
18 0 : mFirstSizeClass(CeilingLog2(kConstantBufferElementSize)),
19 : mFrameNumber(0),
20 0 : mNextSizeClassToShrink(0)
21 : {
22 : // Create a cache of buffers for each size class, where each size class is a
23 : // power of 2 between the minimum and maximum size of a constant buffer.
24 0 : size_t maxBindSize = mDevice->GetMaxConstantBufferBindSize();
25 0 : MOZ_ASSERT(IsPowerOfTwo(maxBindSize));
26 :
27 0 : size_t lastSizeClass = CeilingLog2(maxBindSize);
28 0 : MOZ_ASSERT(lastSizeClass >= mFirstSizeClass);
29 :
30 0 : mCaches.resize(lastSizeClass - mFirstSizeClass + 1);
31 0 : }
32 :
33 0 : BufferCache::~BufferCache()
34 : {
35 0 : }
36 :
37 : RefPtr<MLGBuffer>
38 0 : BufferCache::GetOrCreateBuffer(size_t aBytes)
39 : {
40 0 : size_t sizeClass = CeilingLog2(aBytes);
41 0 : size_t sizeClassIndex = sizeClass - mFirstSizeClass;
42 0 : if (sizeClassIndex >= mCaches.size()) {
43 0 : return mDevice->CreateBuffer(MLGBufferType::Constant, aBytes, MLGUsage::Dynamic, nullptr);
44 : }
45 :
46 0 : CachePool& pool = mCaches[sizeClassIndex];
47 :
48 : // See if we've cached a buffer that wasn't used in the past 2 frames. A buffer
49 : // used this frame could have already been mapped and written to, and a buffer
50 : // used the previous frame might still be in-use by the GPU. While the latter
51 : // case is okay, it causes aliasing in the driver. Since content is double
52 : // buffered we do not let the compositor get more than 1 frames ahead, and a
53 : // count of 2 frames should ensure the buffer is unused.
54 0 : if (!pool.empty() && mFrameNumber >= pool.front().mLastUsedFrame + 2) {
55 0 : RefPtr<MLGBuffer> buffer = pool.front().mBuffer;
56 0 : pool.pop_front();
57 0 : pool.push_back(CacheEntry(mFrameNumber, buffer));
58 0 : MOZ_RELEASE_ASSERT(buffer->GetSize() >= aBytes);
59 0 : return buffer;
60 : }
61 :
62 : // Allocate a new buffer and cache it.
63 0 : size_t bytes = (size_t(1) << sizeClass);
64 0 : MOZ_ASSERT(bytes >= aBytes);
65 :
66 : RefPtr<MLGBuffer> buffer =
67 0 : mDevice->CreateBuffer(MLGBufferType::Constant, bytes, MLGUsage::Dynamic, nullptr);
68 0 : if (!buffer) {
69 0 : return nullptr;
70 : }
71 :
72 0 : pool.push_back(CacheEntry(mFrameNumber, buffer));
73 0 : return buffer;
74 : }
75 :
76 : void
77 0 : BufferCache::EndFrame()
78 : {
79 : // Consider a buffer dead after ~5 seconds assuming 60 fps.
80 : static size_t kMaxUnusedFrameCount = 60 * 5;
81 :
82 : // At the end of each frame we pick one size class and see if it has any
83 : // buffers that haven't been used for many frames. If so we clear them.
84 : // The next frame we'll search the next size class. (This is just to spread
85 : // work over more than one frame.)
86 0 : CachePool& pool = mCaches[mNextSizeClassToShrink];
87 0 : while (!pool.empty()) {
88 : // Since the deque is sorted oldest-to-newest, front-to-back, we can stop
89 : // searching as soon as a buffer is active.
90 0 : if (mFrameNumber - pool.front().mLastUsedFrame < kMaxUnusedFrameCount) {
91 0 : break;
92 : }
93 0 : pool.pop_front();
94 : }
95 0 : mNextSizeClassToShrink = (mNextSizeClassToShrink + 1) % mCaches.size();
96 :
97 0 : mFrameNumber++;
98 0 : }
99 :
100 : } // namespace layers
101 : } // namespace mozilla
|