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 "gfxPlatform.h"
7 : #include "ImageContainer.h"
8 : #include "mozilla/layers/BufferTexture.h"
9 : #include "mozilla/layers/ISurfaceAllocator.h"
10 : #include "mozilla/layers/TextureForwarder.h"
11 : #include "TextureClientRecycleAllocator.h"
12 :
13 : namespace mozilla {
14 : namespace layers {
15 :
16 : // Used to keep TextureClient's reference count stable as not to disrupt recycling.
17 : class TextureClientHolder
18 : {
19 0 : ~TextureClientHolder() {}
20 : public:
21 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientHolder)
22 :
23 0 : explicit TextureClientHolder(TextureClient* aClient)
24 0 : : mTextureClient(aClient)
25 0 : , mWillRecycle(true)
26 0 : {}
27 :
28 0 : TextureClient* GetTextureClient()
29 : {
30 0 : return mTextureClient;
31 : }
32 :
33 0 : bool WillRecycle()
34 : {
35 0 : return mWillRecycle;
36 : }
37 :
38 0 : void ClearWillRecycle()
39 : {
40 0 : mWillRecycle = false;
41 0 : }
42 :
43 0 : void ClearTextureClient() { mTextureClient = nullptr; }
44 : protected:
45 : RefPtr<TextureClient> mTextureClient;
46 : bool mWillRecycle;
47 : };
48 :
49 : class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper
50 : {
51 : public:
52 0 : DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator,
53 : gfx::SurfaceFormat aFormat,
54 : gfx::IntSize aSize,
55 : BackendSelector aSelector,
56 : TextureFlags aTextureFlags,
57 : TextureAllocationFlags aAllocationFlags)
58 0 : : ITextureClientAllocationHelper(aFormat,
59 : aSize,
60 : aSelector,
61 : aTextureFlags,
62 : aAllocationFlags)
63 0 : , mAllocator(aAllocator)
64 0 : {}
65 :
66 0 : bool IsCompatible(TextureClient* aTextureClient) override
67 : {
68 0 : if (aTextureClient->GetFormat() != mFormat ||
69 0 : aTextureClient->GetSize() != mSize) {
70 0 : return false;
71 : }
72 0 : return true;
73 : }
74 :
75 0 : already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override
76 : {
77 0 : return mAllocator->Allocate(mFormat,
78 : mSize,
79 0 : mSelector,
80 0 : mTextureFlags,
81 0 : mAllocationFlags);
82 : }
83 :
84 : protected:
85 : TextureClientRecycleAllocator* mAllocator;
86 : };
87 :
88 0 : YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
89 0 : TextureFlags aTextureFlags)
90 : : ITextureClientAllocationHelper(gfx::SurfaceFormat::YUV,
91 : aData.mYSize,
92 : BackendSelector::Content,
93 : aTextureFlags,
94 : ALLOC_DEFAULT)
95 0 : , mData(aData)
96 : {
97 0 : }
98 :
99 : bool
100 0 : YCbCrTextureClientAllocationHelper::IsCompatible(TextureClient* aTextureClient)
101 : {
102 0 : MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
103 :
104 0 : BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
105 0 : if (!bufferData ||
106 0 : aTextureClient->GetSize() != mData.mYSize ||
107 0 : bufferData->GetCbCrSize().isNothing() ||
108 0 : bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
109 0 : bufferData->GetYUVColorSpace().isNothing() ||
110 0 : bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
111 0 : bufferData->GetStereoMode().isNothing() ||
112 0 : bufferData->GetStereoMode().ref() != mData.mStereoMode) {
113 0 : return false;
114 : }
115 0 : return true;
116 : }
117 :
118 : already_AddRefed<TextureClient>
119 0 : YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
120 : {
121 : return TextureClient::CreateForYCbCr(aAllocator,
122 0 : mData.mYSize, mData.mCbCrSize,
123 0 : mData.mStereoMode,
124 0 : mData.mYUVColorSpace,
125 0 : mTextureFlags);
126 : }
127 :
128 0 : TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
129 : : mSurfaceAllocator(aAllocator)
130 : , mMaxPooledSize(kMaxPooledSized)
131 : , mLock("TextureClientRecycleAllocatorImp.mLock")
132 0 : , mIsDestroyed(false)
133 : {
134 0 : }
135 :
136 0 : TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
137 : {
138 0 : MutexAutoLock lock(mLock);
139 0 : while (!mPooledClients.empty()) {
140 0 : mPooledClients.pop();
141 : }
142 0 : MOZ_ASSERT(mInUseClients.empty());
143 0 : }
144 :
145 : void
146 0 : TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax)
147 : {
148 0 : mMaxPooledSize = aMax;
149 0 : }
150 :
151 : already_AddRefed<TextureClient>
152 0 : TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat,
153 : gfx::IntSize aSize,
154 : BackendSelector aSelector,
155 : TextureFlags aTextureFlags,
156 : TextureAllocationFlags aAllocFlags)
157 : {
158 0 : MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
159 : DefaultTextureClientAllocationHelper helper(this,
160 : aFormat,
161 : aSize,
162 : aSelector,
163 : aTextureFlags,
164 0 : aAllocFlags);
165 0 : return CreateOrRecycle(helper);
166 : }
167 :
168 : already_AddRefed<TextureClient>
169 0 : TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
170 : {
171 0 : MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
172 :
173 0 : RefPtr<TextureClientHolder> textureHolder;
174 :
175 : {
176 0 : MutexAutoLock lock(mLock);
177 0 : if (mIsDestroyed) {
178 0 : return nullptr;
179 : }
180 0 : if (!mPooledClients.empty()) {
181 0 : textureHolder = mPooledClients.top();
182 0 : mPooledClients.pop();
183 : // If the texture's allocator is not open or a pooled TextureClient is
184 : // not compatible, release it.
185 0 : if (!textureHolder->GetTextureClient()->GetAllocator()->IPCOpen() ||
186 0 : !aHelper.IsCompatible(textureHolder->GetTextureClient())) {
187 : // Release TextureClient.
188 0 : RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
189 0 : textureHolder->ClearTextureClient();
190 0 : textureHolder = nullptr;
191 0 : mSurfaceAllocator->GetTextureForwarder()->GetMessageLoop()->PostTask(task.forget());
192 : } else {
193 0 : textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags);
194 : }
195 : }
196 : }
197 :
198 0 : if (!textureHolder) {
199 : // Allocate new TextureClient
200 0 : RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
201 0 : if (!texture) {
202 0 : return nullptr;
203 : }
204 0 : textureHolder = new TextureClientHolder(texture);
205 : }
206 :
207 : {
208 0 : MutexAutoLock lock(mLock);
209 0 : MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
210 : // Register TextureClient
211 0 : mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
212 : }
213 0 : RefPtr<TextureClient> client(textureHolder->GetTextureClient());
214 :
215 : // Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its
216 : // ref count drops to 1.
217 0 : client->SetRecycleAllocator(this);
218 0 : return client.forget();
219 : }
220 :
221 : already_AddRefed<TextureClient>
222 0 : TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
223 : gfx::IntSize aSize,
224 : BackendSelector aSelector,
225 : TextureFlags aTextureFlags,
226 : TextureAllocationFlags aAllocFlags)
227 : {
228 : return TextureClient::CreateForDrawing(mSurfaceAllocator, aFormat, aSize,
229 0 : aSelector, aTextureFlags, aAllocFlags);
230 : }
231 :
232 : void
233 0 : TextureClientRecycleAllocator::ShrinkToMinimumSize()
234 : {
235 0 : MutexAutoLock lock(mLock);
236 0 : while (!mPooledClients.empty()) {
237 0 : mPooledClients.pop();
238 : }
239 : // We can not clear using TextureClients safely.
240 : // Just clear WillRecycle here.
241 0 : std::map<TextureClient*, RefPtr<TextureClientHolder> >::iterator it;
242 0 : for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) {
243 0 : RefPtr<TextureClientHolder> holder = it->second;
244 0 : holder->ClearWillRecycle();
245 : }
246 0 : }
247 :
248 : void
249 0 : TextureClientRecycleAllocator::Destroy()
250 : {
251 0 : MutexAutoLock lock(mLock);
252 0 : while (!mPooledClients.empty()) {
253 0 : mPooledClients.pop();
254 : }
255 0 : mIsDestroyed = true;
256 0 : }
257 :
258 : void
259 0 : TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient)
260 : {
261 : // Clearing the recycle allocator drops a reference, so make sure we stay alive
262 : // for the duration of this function.
263 0 : RefPtr<TextureClientRecycleAllocator> kungFuDeathGrip(this);
264 0 : aClient->SetRecycleAllocator(nullptr);
265 :
266 0 : RefPtr<TextureClientHolder> textureHolder;
267 : {
268 0 : MutexAutoLock lock(mLock);
269 0 : if (mInUseClients.find(aClient) != mInUseClients.end()) {
270 0 : textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock.
271 0 : if (textureHolder->WillRecycle() &&
272 0 : !mIsDestroyed && mPooledClients.size() < mMaxPooledSize) {
273 0 : mPooledClients.push(textureHolder);
274 : }
275 0 : mInUseClients.erase(aClient);
276 : }
277 : }
278 0 : }
279 :
280 : } // namespace layers
281 : } // namespace mozilla
|