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