Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/DecodedSurfaceProvider.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; 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 "DecodedSurfaceProvider.h"
7
8
#include "gfxPrefs.h"
9
#include "nsProxyRelease.h"
10
11
#include "Decoder.h"
12
13
using namespace mozilla::gfx;
14
15
namespace mozilla {
16
namespace image {
17
18
DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage,
19
                                               const SurfaceKey& aSurfaceKey,
20
                                               NotNull<Decoder*> aDecoder)
21
  : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
22
                     AvailabilityState::StartAsPlaceholder())
23
  , mImage(aImage.get())
24
  , mMutex("mozilla::image::DecodedSurfaceProvider")
25
  , mDecoder(aDecoder.get())
26
0
{
27
0
  MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
28
0
             "Use MetadataDecodingTask for metadata decodes");
29
0
  MOZ_ASSERT(mDecoder->IsFirstFrameDecode(),
30
0
             "Use AnimationSurfaceProvider for animation decodes");
31
0
}
32
33
DecodedSurfaceProvider::~DecodedSurfaceProvider()
34
0
{
35
0
  DropImageReference();
36
0
}
37
38
void
39
DecodedSurfaceProvider::DropImageReference()
40
0
{
41
0
  if (!mImage) {
42
0
    return;  // Nothing to do.
43
0
  }
44
0
45
0
  // RasterImage objects need to be destroyed on the main thread. We also need
46
0
  // to destroy them asynchronously, because if our surface cache entry is
47
0
  // destroyed and we were the only thing keeping |mImage| alive, RasterImage's
48
0
  // destructor may call into the surface cache while whatever code caused us to
49
0
  // get evicted is holding the surface cache lock, causing deadlock.
50
0
  RefPtr<RasterImage> image = mImage;
51
0
  mImage = nullptr;
52
0
  NS_ReleaseOnMainThreadSystemGroup(image.forget(), /* aAlwaysProxy = */ true);
53
0
}
54
55
DrawableFrameRef
56
DecodedSurfaceProvider::DrawableRef(size_t aFrame)
57
0
{
58
0
  MOZ_ASSERT(aFrame == 0,
59
0
             "Requesting an animation frame from a DecodedSurfaceProvider?");
60
0
61
0
  // We depend on SurfaceCache::SurfaceAvailable() to provide synchronization
62
0
  // for methods that touch |mSurface|; after SurfaceAvailable() is called,
63
0
  // |mSurface| should be non-null and shouldn't be mutated further until we get
64
0
  // destroyed. That means that the assertions below are very important; we'll
65
0
  // end up with data races if these assumptions are violated.
66
0
  if (Availability().IsPlaceholder()) {
67
0
    MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
68
0
    return DrawableFrameRef();
69
0
  }
70
0
71
0
  if (!mSurface) {
72
0
    MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no surface");
73
0
    return DrawableFrameRef();
74
0
  }
75
0
76
0
  return mSurface->DrawableRef();
77
0
}
78
79
bool
80
DecodedSurfaceProvider::IsFinished() const
81
0
{
82
0
  // See DrawableRef() for commentary on these assertions.
83
0
  if (Availability().IsPlaceholder()) {
84
0
    MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
85
0
    return false;
86
0
  }
87
0
88
0
  if (!mSurface) {
89
0
    MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no surface");
90
0
    return false;
91
0
  }
92
0
93
0
  return mSurface->IsFinished();
94
0
}
95
96
void
97
DecodedSurfaceProvider::SetLocked(bool aLocked)
98
0
{
99
0
  // See DrawableRef() for commentary on these assertions.
100
0
  if (Availability().IsPlaceholder()) {
101
0
    MOZ_ASSERT_UNREACHABLE("Calling SetLocked() on a placeholder");
102
0
    return;
103
0
  }
104
0
105
0
  if (!mSurface) {
106
0
    MOZ_ASSERT_UNREACHABLE("Calling SetLocked() when we have no surface");
107
0
    return;
108
0
  }
109
0
110
0
  if (aLocked == IsLocked()) {
111
0
    return;  // Nothing to do.
112
0
  }
113
0
114
0
  // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep any
115
0
  // volatile buffer it owns in memory.
116
0
  mLockRef = aLocked ? mSurface->DrawableRef()
117
0
                     : DrawableFrameRef();
118
0
}
119
120
size_t
121
DecodedSurfaceProvider::LogicalSizeInBytes() const
122
0
{
123
0
  // Single frame images are always 32bpp.
124
0
  IntSize size = GetSurfaceKey().Size();
125
0
  return size_t(size.width) * size_t(size.height) * sizeof(uint32_t);
126
0
}
127
128
void
129
DecodedSurfaceProvider::Run()
130
0
{
131
0
  MutexAutoLock lock(mMutex);
132
0
133
0
  if (!mDecoder || !mImage) {
134
0
    MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
135
0
    return;
136
0
  }
137
0
138
0
  // Run the decoder.
139
0
  LexerResult result = mDecoder->Decode(WrapNotNull(this));
140
0
141
0
  // If there's a new surface available, announce it to the surface cache.
142
0
  CheckForNewSurface();
143
0
144
0
  if (result.is<TerminalState>()) {
145
0
    FinishDecoding();
146
0
    return;  // We're done.
147
0
  }
148
0
149
0
  // Notify for the progress we've made so far.
150
0
  if (mDecoder->HasProgress()) {
151
0
    NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
152
0
  }
153
0
154
0
  MOZ_ASSERT(result.is<Yield>());
155
0
156
0
  if (result == LexerResult(Yield::NEED_MORE_DATA)) {
157
0
    // We can't make any more progress right now. The decoder itself will ensure
158
0
    // that we get reenqueued when more data is available; just return for now.
159
0
    return;
160
0
  }
161
0
162
0
  // Single-frame images shouldn't yield for any reason except NEED_MORE_DATA.
163
0
  MOZ_ASSERT_UNREACHABLE("Unexpected yield for single-frame image");
164
0
  mDecoder->TerminateFailure();
165
0
  FinishDecoding();
166
0
}
167
168
void
169
DecodedSurfaceProvider::CheckForNewSurface()
170
0
{
171
0
  mMutex.AssertCurrentThreadOwns();
172
0
  MOZ_ASSERT(mDecoder);
173
0
174
0
  if (mSurface) {
175
0
    // Single-frame images should produce no more than one surface, so if we
176
0
    // have one, it should be the same one the decoder is working on.
177
0
    MOZ_ASSERT(mSurface.get() == mDecoder->GetCurrentFrameRef().get(),
178
0
               "DecodedSurfaceProvider and Decoder have different surfaces?");
179
0
    return;
180
0
  }
181
0
182
0
  // We don't have a surface yet; try to get one from the decoder.
183
0
  mSurface = mDecoder->GetCurrentFrameRef().get();
184
0
  if (!mSurface) {
185
0
    return;  // No surface yet.
186
0
  }
187
0
188
0
  // We just got a surface for the first time; let the surface cache know.
189
0
  MOZ_ASSERT(mImage);
190
0
  SurfaceCache::SurfaceAvailable(WrapNotNull(this));
191
0
}
192
193
void
194
DecodedSurfaceProvider::FinishDecoding()
195
0
{
196
0
  mMutex.AssertCurrentThreadOwns();
197
0
  MOZ_ASSERT(mImage);
198
0
  MOZ_ASSERT(mDecoder);
199
0
200
0
  // Send notifications.
201
0
  NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
202
0
203
0
  // If we have a new and complete surface, we can try to prune similarly sized
204
0
  // surfaces if the cache supports it.
205
0
  if (mSurface && mSurface->IsFinished()) {
206
0
    SurfaceCache::PruneImage(ImageKey(mImage));
207
0
  }
208
0
209
0
  // Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
210
0
  // our surface can never be optimized, because the decoder has a
211
0
  // RawAccessFrameRef to it.)
212
0
  mDecoder = nullptr;
213
0
214
0
  // We don't need a reference to our image anymore, either, and we don't want
215
0
  // one. We may be stored in the surface cache for a long time after decoding
216
0
  // finishes. If we don't drop our reference to the image, we'll end up
217
0
  // keeping it alive as long as we remain in the surface cache, which could
218
0
  // greatly extend the image's lifetime - in fact, if the image isn't
219
0
  // discardable, it'd result in a leak!
220
0
  DropImageReference();
221
0
}
222
223
bool
224
DecodedSurfaceProvider::ShouldPreferSyncRun() const
225
0
{
226
0
  return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
227
0
}
228
229
} // namespace image
230
} // namespace mozilla